

/* -- 0-console.js -- */

// Avoid `console` errors in browsers that lack a console.
;(function() {
    var method;
    var noop = function noop() {};
    var methods = [
        'assert', 'clear', 'count', 'debug', 'dir', 'dirxml', 'error',
        'exception', 'group', 'groupCollapsed', 'groupEnd', 'info', 'log',
        'markTimeline', 'profile', 'profileEnd', 'table', 'time', 'timeEnd',
        'timeStamp', 'trace', 'warn'
    ];
    var length = methods.length;
    var console = (window.console = window.console || {});

    while (length--) {
        method = methods[length];

        // Only stub undefined methods.
        if (!console[method]) {
            console[method] = noop;
        }
    }
}());


/* -- Martini-QueryString.js -- */

/**
 * Examples:
 *   qs - provides two functions
 *      qa.get('action') will return any variable attached to ?action=foo in the querystring.
 *      qa.objToQueryString - will return a query string from a key:value object
 *
 */

var qs = {
    parsed : false,
    queryString : {},
    get:function(key) {
        if (!this.parsed) {
            this.init();
        }
        return (typeof this.queryString[key] == 'undefined') ? null : this.queryString[key];
    },
    init:function () {
        this.queryString = this.decode(location.search.replace(/\?/,''));
        this.parsed = true;
    },
    decode: function(data) {
        var pairs = data.split('&');
        var l = pairs.length;

        var parsedData = {};

        for ( var i = 0; i < l; i++) {
            var val = pairs[i].toString();
            if (pairs[i].indexOf("=") == -1) {
                continue;
            }

            var keyval = pairs[i].split( "=" );

            if (parsedData.hasOwnProperty(keyval[0]))  {
                if (parsedData[keyval[0]] instanceof Array) {
                    parsedData[keyval[0]].push(keyval[1]);
                } else {
                    var existingData = parsedData[keyval[0]];
                    parsedData[keyval[0]] = [];
                    parsedData[keyval[0]].push(existingData);
                    parsedData[keyval[0]].push(keyval[1]);
                }
            } else {
                parsedData[ keyval[0] ] = keyval[1];
            }
        }

        return parsedData;
    },
    objToQueryString : function(qs) {
        var pairs = [];
        $.each(qs, function (key, value) {
            // we don't set for invalid values
            if (value == null || value.length < 1) {
                return;
            }
            pairs.push(key+'='+encodeURIComponent(value.toLowerCase().replace('"','')));
        });

        return pairs.join('&');
    }
};


/* -- Martini-Skin.js -- */

/* eslint-disable */
/*
 *  Martinit Skin Handler
 *
 * This script is initiated by DFP skin ads and forces skin ads to be positioned below the primary navigation.
 */
(function() {
    var MartiniSkin = {
        ad_container: null,
        top_section_height: null,
        init: function(ad_container, top_section_height){

            MartiniSkin.ad_container = ad_container; // '#creative_disp'
            MartiniSkin.top_section_height = top_section_height + "px"; // '250'

            // Remove background colour and width (if set) - For some reason setting these using .css() wont work.
            $('#takeover').attr("style", "background: none !important; width: auto !important; margin-top: 0 !important");

            // Make advert go behind body content
            $(MartiniSkin.ad_container+", "+MartiniSkin.ad_container+" > div").css("z-index", "0");

            // Make all containers on page have a higher z-index
            $('#takeover .container').css("z-index", "1");

            // Make first container have a passing-top when skin is enabled.
            $('#redesign-content > .container:first-child').css("padding-top", "25px");

            // Hide top leaderboard ad.
            $('#DFP_top_leaderboard').hide();

            MartiniSkin.positionSkin();

            // Apply top margin to main content for height of top skin section
            $('#redesign-content').css("margin-top", MartiniSkin.top_section_height);

            //Re-position skin on scroll
            $(window).scroll(function() {
                MartiniSkin.positionSkin();
            });
        },
        positionSkin: function(){
            // Get height of visible header
            var header_visible = MartiniSkin.getVisible('#redesign-header');

            if(header_visible > 38){
                $(MartiniSkin.ad_container+", "+MartiniSkin.ad_container+" > div").css("top", header_visible + "px");
            }else{
                $(MartiniSkin.ad_container+", "+MartiniSkin.ad_container+" > div").css("top", "38px"); // Height of sticky nav
            }
        },
        getVisible: function(el) {
            $el = $(el);
            scrollTop = $('body').scrollTop(),
            scrollBot = scrollTop + $('body').height(),
            elTop = $el.offset().top,
            elBottom = elTop + $el.outerHeight(),
            visibleTop = elTop < scrollTop ? scrollTop : elTop,
            visibleBottom = elBottom > scrollBot ? scrollBot : elBottom;
            return visibleBottom - visibleTop;
        }
    };

    window.MartiniSkin = MartiniSkin;
})();


/* -- PageHeader.js -- */

/* eslint-disable */
; $(document).ready(function() {


var PageHeader = {

    scrollPosition: 0,
    timeout: null,
    triggerAdSticky: true,
    adHeightSet: false,

    init: function() {
        this.bind();
        this.initNav();
    },

    bind: function() {
        if ($( "#redesign-header").length === 0) {
            return;
        }


        var self = this;

        // Mega Menu Open
        $( "#redesign-header").on("click", ".trigger-mega-nav", function() {
            // Cache scroll position for when the user closes the mega menu
            self.scrollPosition = $(document).scrollTop();

            //Toggle
            $('#mega-menu, #takeover').toggle();
        });

        // Mega Menu Close
        $( "#mega-menu" ).on("click", "i.m-icon-cancel-1", function() {
            //Toggle
            $('#mega-menu, #takeover').toggle( 0, function(){
                // Apply cached scroll position
                $(document).scrollTop(self.scrollPosition);
            });
        });

        // Search Tooltip
        $("#redesign-header").on("click", ".search a", function(e) {
            e.preventDefault();

            $('.search-tooltip').slideToggle(300, function(){
                if($(this).is(':visible')){
                    $(this).parent().find('a i').attr("class", "m-icon-cancel-1");
                }else{
                    $(this).parent().find('a i').attr("class", "m-icon-search");
                }
                $('.search-tooltip input').get(0).focus();
            });
        });

        // Social Tooltips
        $( "#redesign-header" ).on( "click", ".social-icon", function(e) {
            var socialItem = $(this).data("social");

            // Ensure all other social tooltips are closed.
            $(".social-tooltip").each(function( index ) {
                var network = $(this).parent().find(".social-icon").data("social");
                if(network != socialItem){
                    $(".social-tooltip-"+network).hide();
                }
            });

            $(this).parent().find(".social-tooltip").toggle();
        });
    },
    initNav: function() {

        if ($('.brand').length === 0) {
            return;
        }

        // jQuery .load() errors on mobile so we use this alternative
        var image = new Image();
        image.onload = function () {
            // Initiate ad sticky
            PageHeader.initAdSticky();

            // Initiate sticky nav
            if (window.screen.width <= 768) {
                if($('#BlockArticleDisplay').length === 0){
                    PageHeader.initSticky();
                }

                // Handle window scroll
                $(window).scroll(function() {
                    if($('#BlockArticleDisplay').length === 0){
                        PageHeader.initSticky();
                    }
                    PageHeader.initAdSticky();
                });
            }else{
                // Handle window scroll
                $(window).scroll(function() {
                    PageHeader.initAdSticky();
                });
            }

            // Handle window resizes
            // Unfortunately, we have to do this
            $(window).resize(function() {
                // Re-calculate sticky trigger
                // We have to do this, because reducing the viewport width may result in a change to the header height (as logo gets smaller).
                PageHeader.showHideNavItems();
            });

            // Run show hide nav items after 5,10,15 seconds - by this point hopefully any skins would have loaded.
            setTimeout(PageHeader.showHideNavItems, 5000);
            setTimeout(PageHeader.showHideNavItems, 10000);
            setTimeout(PageHeader.showHideNavItems, 15000);
        }

        image.src = $('.brand').attr('src');
    },

    initAdSticky: function() {
        // if we've turned off the trigger, exit.
        if (!PageHeader.triggerAdSticky) {
            return;
        }

        var advert = $('#DFP_top_leaderboard');
        var advertHeight = advert.height() + 20;

        var takeOverPos = $('#takeover').position();
        var takeOverMarginTop = parseInt($('#takeover').css("margin-top"), 10); // Some skins add a margin-top.
        var adYTriggerPoint = $('#redesign-header').height() + parseInt($('#redesign-header').css("margin-bottom"), 10) + takeOverPos.top + takeOverMarginTop;

        // adjust for smoothness
        adYTriggerPoint -= 10;

        if ($(window).scrollTop() > adYTriggerPoint){
            if (advertHeight > 0 && !PageHeader.adHeightSet) {
                advert.parent().css('height', advertHeight+'px');
                PageHeader.adHeightSet = true;
            }

            if(!advert.hasClass("is-sticky")) {
                advert.addClass("is-sticky");
                window.setTimeout(PageHeader.hideAdSticky, 3000); // hide after 3 seconds.
            }
        } else {
            advert.removeClass("is-sticky");
        }
    },
    hideAdSticky: function() {
        PageHeader.triggerAdSticky = false;
        $('#DFP_top_leaderboard').removeClass("is-sticky");
        $('#DFP_top_leaderboard').parent().css('height', 'auto');

        // This code is used to notify the "NextUpDesktop" component that the sticky ad has closed and therefore can be displayed.
        if (typeof nextUpDesktop != 'undefined') {
            nextUpDesktop.stickyAdClosedTrigger();
        }
        if (typeof articleStickyNavDesktop != 'undefined') {
            articleStickyNavDesktop.stickyAdClosedTrigger();
        }
    },

    initSticky: function(){
        var header = $('header');

        var mobileSticky = $('.primary-nav.primary-nav-mobile-sticky');
        var desktopSticky = $('.primary-nav.primary-nav-mobile-sticky');

        // NOTE: Here we will need to work out based on viewport width if desktop of mobile sticky is required. For now, always show mobile.
        var sticky = mobileSticky;

        // Accommodate skins.
        var takeOverPos = $('#takeover').position();
        var takeOverMarginTop = parseInt($('#takeover').css("margin-top"), 10); // Some skins add a margin-top.
        var navYTriggerPoint = $('#redesign-header').height() - 37 + takeOverPos.top + takeOverMarginTop;

        if ($(window).scrollTop() > navYTriggerPoint){
            // does the element already have scroll class?
            if(!sticky.hasClass("is-sticky")){
                // Make overflow hidden for duration of transition
                //$('.primary-nav-menu').animate({ marginLeft: '230px'}, 200)

                // Make sticky
                sticky.addClass("is-sticky");

                // Wait for transition to be complete before rechecking nav items.
                // header.one('webkitTransitionEnd otransitionend oTransitionEnd msTransitionEnd transitionend', function(e) {
                //     // $('.primary-nav-menu').css("overflow", "visible");
                //     // PageHeader.showHideNavItems();
                // });
            }
        }else{
            sticky.removeClass("is-sticky");
            //$('.primary-nav-menu').css("margin-left", "0");
            // PageHeader.showHideNavItems();
        }
    },

    showHideNavItems: function() {
        // This function is used to hide nav links that fall below the navigation
        var $primary_nav_menu_items = $('nav.primary-nav-non-sticky .primary-nav-menu > li');

        // Loop over items
        $primary_nav_menu_items.each(function(index) {
            if(index > 0){
                $(this).show();

                var item_pos = $(this).position();

                if(item_pos.top > 0){
                    $(this).hide();
                }
            }
        });
    // window.clearTimeout(this.timeout);
        // this.timeout = window.setTimeout(PageHeader.showHideNavItems, 1000);
    },
    // checkTouchUser: function() {
    //     window.addEventListener('touchstart', function onFirstTouch() {
    //
    //         // or set your app's state however you normally would
    //         $(window).trigger('USER:TOUCHING');
    //
    //         // we've had the event, we don't care again
    //         window.removeEventListener('touchstart', onFirstTouch, false);
    //     }, false);
    // }
};
    PageHeader.init();
    window.PageHeader = PageHeader;
});


/* -- jquery.cookie.js -- */

if (typeof window.$ !== 'undefined')
{

/*!
 * jQuery Cookie Plugin v1.4.1
 * https://github.com/carhartl/jquery-cookie
 *
 * Copyright 2006, 2014 Klaus Hartl
 * Released under the MIT license
 */
(function (factory) {
	factory(jQuery);
	
}(function ($) {

	var pluses = /\+/g;

	function encode(s) {
		return config.raw ? s : encodeURIComponent(s);
	}

	function decode(s) {
		return config.raw ? s : decodeURIComponent(s);
	}

	function stringifyCookieValue(value) {
		return encode(config.json ? JSON.stringify(value) : String(value));
	}

	function parseCookieValue(s) {
		if (s.indexOf('"') === 0) {
			// This is a quoted cookie as according to RFC2068, unescape...
			s = s.slice(1, -1).replace(/\\"/g, '"').replace(/\\\\/g, '\\');
		}

		try {
			// Replace server-side written pluses with spaces.
			// If we can't decode the cookie, ignore it, it's unusable.
			// If we can't parse the cookie, ignore it, it's unusable.
			s = decodeURIComponent(s.replace(pluses, ' '));
			return config.json ? JSON.parse(s) : s;
		} catch(e) {}
	}

	function read(s, converter) {
		var value = config.raw ? s : parseCookieValue(s);
		return $.isFunction(converter) ? converter(value) : value;
	}

	var config = $.cookie = function (key, value, options) {

		// Write

		if (arguments.length > 1 && !$.isFunction(value)) {
			options = $.extend({}, config.defaults, options);

			if (typeof options.expires === 'number') {
				var days = options.expires, t = options.expires = new Date();
				t.setMilliseconds(t.getMilliseconds() + days * 864e+5);
			}

			return (document.cookie = [
				encode(key), '=', stringifyCookieValue(value),
				options.expires ? '; expires=' + options.expires.toUTCString() : '', // use expires attribute, max-age is not supported by IE
				options.path    ? '; path=' + options.path : '',
				options.domain  ? '; domain=' + options.domain : '',
				options.secure  ? '; secure' : ''
			].join(''));
		}

		// Read

		var result = key ? undefined : {},
			// To prevent the for loop in the first place assign an empty array
			// in case there are no cookies at all. Also prevents odd result when
			// calling $.cookie().
			cookies = document.cookie ? document.cookie.split('; ') : [],
			i = 0,
			l = cookies.length;

		for (; i < l; i++) {
			var parts = cookies[i].split('='),
				name = decode(parts.shift()),
				cookie = parts.join('=');

			if (key === name) {
				// If second argument (value) is a function it's a converter...
				result = read(cookie, value);
				break;
			}

			// Prevent storing a cookie that we couldn't decode.
			if (!key && (cookie = read(cookie)) !== undefined) {
				result[name] = cookie;
			}
		}

		return result;
	};

	config.defaults = {};

	$.removeCookie = function (key, options) {
		// Must not alter options, thus extending a fresh object...
		$.cookie(key, '', $.extend({}, options, { expires: -1 }));
		return !$.cookie(key);
	};

}));

}


/* -- mobile-detect.js -- */

// THIS FILE IS GENERATED - DO NOT EDIT!
/*!mobile-detect v1.3.5 2016-11-14*/
/*global module:false, define:false*/
/*jshint latedef:false*/
/*!@license Copyright 2013, Heinrich Goebl, License: MIT, see https://github.com/hgoebl/mobile-detect.js*/
(function () {
    'use strict';

    var impl = {};

    impl.mobileDetectRules = {
    "phones": {
        "iPhone": "\\biPhone\\b|\\biPod\\b",
        "BlackBerry": "BlackBerry|\\bBB10\\b|rim[0-9]+",
        "HTC": "HTC|HTC.*(Sensation|Evo|Vision|Explorer|6800|8100|8900|A7272|S510e|C110e|Legend|Desire|T8282)|APX515CKT|Qtek9090|APA9292KT|HD_mini|Sensation.*Z710e|PG86100|Z715e|Desire.*(A8181|HD)|ADR6200|ADR6400L|ADR6425|001HT|Inspire 4G|Android.*\\bEVO\\b|T-Mobile G1|Z520m",
        "Nexus": "Nexus One|Nexus S|Galaxy.*Nexus|Android.*Nexus.*Mobile|Nexus 4|Nexus 5|Nexus 6",
        "Dell": "Dell.*Streak|Dell.*Aero|Dell.*Venue|DELL.*Venue Pro|Dell Flash|Dell Smoke|Dell Mini 3iX|XCD28|XCD35|\\b001DL\\b|\\b101DL\\b|\\bGS01\\b",
        "Motorola": "Motorola|DROIDX|DROID BIONIC|\\bDroid\\b.*Build|Android.*Xoom|HRI39|MOT-|A1260|A1680|A555|A853|A855|A953|A955|A956|Motorola.*ELECTRIFY|Motorola.*i1|i867|i940|MB200|MB300|MB501|MB502|MB508|MB511|MB520|MB525|MB526|MB611|MB612|MB632|MB810|MB855|MB860|MB861|MB865|MB870|ME501|ME502|ME511|ME525|ME600|ME632|ME722|ME811|ME860|ME863|ME865|MT620|MT710|MT716|MT720|MT810|MT870|MT917|Motorola.*TITANIUM|WX435|WX445|XT300|XT301|XT311|XT316|XT317|XT319|XT320|XT390|XT502|XT530|XT531|XT532|XT535|XT603|XT610|XT611|XT615|XT681|XT701|XT702|XT711|XT720|XT800|XT806|XT860|XT862|XT875|XT882|XT883|XT894|XT901|XT907|XT909|XT910|XT912|XT928|XT926|XT915|XT919|XT925|XT1021|\\bMoto E\\b",
        "Samsung": "\\bSamsung\\b|SM-G9250|GT-19300|SGH-I337|BGT-S5230|GT-B2100|GT-B2700|GT-B2710|GT-B3210|GT-B3310|GT-B3410|GT-B3730|GT-B3740|GT-B5510|GT-B5512|GT-B5722|GT-B6520|GT-B7300|GT-B7320|GT-B7330|GT-B7350|GT-B7510|GT-B7722|GT-B7800|GT-C3010|GT-C3011|GT-C3060|GT-C3200|GT-C3212|GT-C3212I|GT-C3262|GT-C3222|GT-C3300|GT-C3300K|GT-C3303|GT-C3303K|GT-C3310|GT-C3322|GT-C3330|GT-C3350|GT-C3500|GT-C3510|GT-C3530|GT-C3630|GT-C3780|GT-C5010|GT-C5212|GT-C6620|GT-C6625|GT-C6712|GT-E1050|GT-E1070|GT-E1075|GT-E1080|GT-E1081|GT-E1085|GT-E1087|GT-E1100|GT-E1107|GT-E1110|GT-E1120|GT-E1125|GT-E1130|GT-E1160|GT-E1170|GT-E1175|GT-E1180|GT-E1182|GT-E1200|GT-E1210|GT-E1225|GT-E1230|GT-E1390|GT-E2100|GT-E2120|GT-E2121|GT-E2152|GT-E2220|GT-E2222|GT-E2230|GT-E2232|GT-E2250|GT-E2370|GT-E2550|GT-E2652|GT-E3210|GT-E3213|GT-I5500|GT-I5503|GT-I5700|GT-I5800|GT-I5801|GT-I6410|GT-I6420|GT-I7110|GT-I7410|GT-I7500|GT-I8000|GT-I8150|GT-I8160|GT-I8190|GT-I8320|GT-I8330|GT-I8350|GT-I8530|GT-I8700|GT-I8703|GT-I8910|GT-I9000|GT-I9001|GT-I9003|GT-I9010|GT-I9020|GT-I9023|GT-I9070|GT-I9082|GT-I9100|GT-I9103|GT-I9220|GT-I9250|GT-I9300|GT-I9305|GT-I9500|GT-I9505|GT-M3510|GT-M5650|GT-M7500|GT-M7600|GT-M7603|GT-M8800|GT-M8910|GT-N7000|GT-S3110|GT-S3310|GT-S3350|GT-S3353|GT-S3370|GT-S3650|GT-S3653|GT-S3770|GT-S3850|GT-S5210|GT-S5220|GT-S5229|GT-S5230|GT-S5233|GT-S5250|GT-S5253|GT-S5260|GT-S5263|GT-S5270|GT-S5300|GT-S5330|GT-S5350|GT-S5360|GT-S5363|GT-S5369|GT-S5380|GT-S5380D|GT-S5560|GT-S5570|GT-S5600|GT-S5603|GT-S5610|GT-S5620|GT-S5660|GT-S5670|GT-S5690|GT-S5750|GT-S5780|GT-S5830|GT-S5839|GT-S6102|GT-S6500|GT-S7070|GT-S7200|GT-S7220|GT-S7230|GT-S7233|GT-S7250|GT-S7500|GT-S7530|GT-S7550|GT-S7562|GT-S7710|GT-S8000|GT-S8003|GT-S8500|GT-S8530|GT-S8600|SCH-A310|SCH-A530|SCH-A570|SCH-A610|SCH-A630|SCH-A650|SCH-A790|SCH-A795|SCH-A850|SCH-A870|SCH-A890|SCH-A930|SCH-A950|SCH-A970|SCH-A990|SCH-I100|SCH-I110|SCH-I400|SCH-I405|SCH-I500|SCH-I510|SCH-I515|SCH-I600|SCH-I730|SCH-I760|SCH-I770|SCH-I830|SCH-I910|SCH-I920|SCH-I959|SCH-LC11|SCH-N150|SCH-N300|SCH-R100|SCH-R300|SCH-R351|SCH-R400|SCH-R410|SCH-T300|SCH-U310|SCH-U320|SCH-U350|SCH-U360|SCH-U365|SCH-U370|SCH-U380|SCH-U410|SCH-U430|SCH-U450|SCH-U460|SCH-U470|SCH-U490|SCH-U540|SCH-U550|SCH-U620|SCH-U640|SCH-U650|SCH-U660|SCH-U700|SCH-U740|SCH-U750|SCH-U810|SCH-U820|SCH-U900|SCH-U940|SCH-U960|SCS-26UC|SGH-A107|SGH-A117|SGH-A127|SGH-A137|SGH-A157|SGH-A167|SGH-A177|SGH-A187|SGH-A197|SGH-A227|SGH-A237|SGH-A257|SGH-A437|SGH-A517|SGH-A597|SGH-A637|SGH-A657|SGH-A667|SGH-A687|SGH-A697|SGH-A707|SGH-A717|SGH-A727|SGH-A737|SGH-A747|SGH-A767|SGH-A777|SGH-A797|SGH-A817|SGH-A827|SGH-A837|SGH-A847|SGH-A867|SGH-A877|SGH-A887|SGH-A897|SGH-A927|SGH-B100|SGH-B130|SGH-B200|SGH-B220|SGH-C100|SGH-C110|SGH-C120|SGH-C130|SGH-C140|SGH-C160|SGH-C170|SGH-C180|SGH-C200|SGH-C207|SGH-C210|SGH-C225|SGH-C230|SGH-C417|SGH-C450|SGH-D307|SGH-D347|SGH-D357|SGH-D407|SGH-D415|SGH-D780|SGH-D807|SGH-D980|SGH-E105|SGH-E200|SGH-E315|SGH-E316|SGH-E317|SGH-E335|SGH-E590|SGH-E635|SGH-E715|SGH-E890|SGH-F300|SGH-F480|SGH-I200|SGH-I300|SGH-I320|SGH-I550|SGH-I577|SGH-I600|SGH-I607|SGH-I617|SGH-I627|SGH-I637|SGH-I677|SGH-I700|SGH-I717|SGH-I727|SGH-i747M|SGH-I777|SGH-I780|SGH-I827|SGH-I847|SGH-I857|SGH-I896|SGH-I897|SGH-I900|SGH-I907|SGH-I917|SGH-I927|SGH-I937|SGH-I997|SGH-J150|SGH-J200|SGH-L170|SGH-L700|SGH-M110|SGH-M150|SGH-M200|SGH-N105|SGH-N500|SGH-N600|SGH-N620|SGH-N625|SGH-N700|SGH-N710|SGH-P107|SGH-P207|SGH-P300|SGH-P310|SGH-P520|SGH-P735|SGH-P777|SGH-Q105|SGH-R210|SGH-R220|SGH-R225|SGH-S105|SGH-S307|SGH-T109|SGH-T119|SGH-T139|SGH-T209|SGH-T219|SGH-T229|SGH-T239|SGH-T249|SGH-T259|SGH-T309|SGH-T319|SGH-T329|SGH-T339|SGH-T349|SGH-T359|SGH-T369|SGH-T379|SGH-T409|SGH-T429|SGH-T439|SGH-T459|SGH-T469|SGH-T479|SGH-T499|SGH-T509|SGH-T519|SGH-T539|SGH-T559|SGH-T589|SGH-T609|SGH-T619|SGH-T629|SGH-T639|SGH-T659|SGH-T669|SGH-T679|SGH-T709|SGH-T719|SGH-T729|SGH-T739|SGH-T746|SGH-T749|SGH-T759|SGH-T769|SGH-T809|SGH-T819|SGH-T839|SGH-T919|SGH-T929|SGH-T939|SGH-T959|SGH-T989|SGH-U100|SGH-U200|SGH-U800|SGH-V205|SGH-V206|SGH-X100|SGH-X105|SGH-X120|SGH-X140|SGH-X426|SGH-X427|SGH-X475|SGH-X495|SGH-X497|SGH-X507|SGH-X600|SGH-X610|SGH-X620|SGH-X630|SGH-X700|SGH-X820|SGH-X890|SGH-Z130|SGH-Z150|SGH-Z170|SGH-ZX10|SGH-ZX20|SHW-M110|SPH-A120|SPH-A400|SPH-A420|SPH-A460|SPH-A500|SPH-A560|SPH-A600|SPH-A620|SPH-A660|SPH-A700|SPH-A740|SPH-A760|SPH-A790|SPH-A800|SPH-A820|SPH-A840|SPH-A880|SPH-A900|SPH-A940|SPH-A960|SPH-D600|SPH-D700|SPH-D710|SPH-D720|SPH-I300|SPH-I325|SPH-I330|SPH-I350|SPH-I500|SPH-I600|SPH-I700|SPH-L700|SPH-M100|SPH-M220|SPH-M240|SPH-M300|SPH-M305|SPH-M320|SPH-M330|SPH-M350|SPH-M360|SPH-M370|SPH-M380|SPH-M510|SPH-M540|SPH-M550|SPH-M560|SPH-M570|SPH-M580|SPH-M610|SPH-M620|SPH-M630|SPH-M800|SPH-M810|SPH-M850|SPH-M900|SPH-M910|SPH-M920|SPH-M930|SPH-N100|SPH-N200|SPH-N240|SPH-N300|SPH-N400|SPH-Z400|SWC-E100|SCH-i909|GT-N7100|GT-N7105|SCH-I535|SM-N900A|SGH-I317|SGH-T999L|GT-S5360B|GT-I8262|GT-S6802|GT-S6312|GT-S6310|GT-S5312|GT-S5310|GT-I9105|GT-I8510|GT-S6790N|SM-G7105|SM-N9005|GT-S5301|GT-I9295|GT-I9195|SM-C101|GT-S7392|GT-S7560|GT-B7610|GT-I5510|GT-S7582|GT-S7530E|GT-I8750|SM-G9006V|SM-G9008V|SM-G9009D|SM-G900A|SM-G900D|SM-G900F|SM-G900H|SM-G900I|SM-G900J|SM-G900K|SM-G900L|SM-G900M|SM-G900P|SM-G900R4|SM-G900S|SM-G900T|SM-G900V|SM-G900W8|SHV-E160K|SCH-P709|SCH-P729|SM-T2558|GT-I9205|SM-G9350|SM-J120F",
        "LG": "\\bLG\\b;|LG[- ]?(C800|C900|E400|E610|E900|E-900|F160|F180K|F180L|F180S|730|855|L160|LS740|LS840|LS970|LU6200|MS690|MS695|MS770|MS840|MS870|MS910|P500|P700|P705|VM696|AS680|AS695|AX840|C729|E970|GS505|272|C395|E739BK|E960|L55C|L75C|LS696|LS860|P769BK|P350|P500|P509|P870|UN272|US730|VS840|VS950|LN272|LN510|LS670|LS855|LW690|MN270|MN510|P509|P769|P930|UN200|UN270|UN510|UN610|US670|US740|US760|UX265|UX840|VN271|VN530|VS660|VS700|VS740|VS750|VS910|VS920|VS930|VX9200|VX11000|AX840A|LW770|P506|P925|P999|E612|D955|D802|MS323)",
        "Sony": "SonyST|SonyLT|SonyEricsson|SonyEricssonLT15iv|LT18i|E10i|LT28h|LT26w|SonyEricssonMT27i|C5303|C6902|C6903|C6906|C6943|D2533",
        "Asus": "Asus.*Galaxy|PadFone.*Mobile",
        "NokiaLumia": "Lumia [0-9]{3,4}",
        "Micromax": "Micromax.*\\b(A210|A92|A88|A72|A111|A110Q|A115|A116|A110|A90S|A26|A51|A35|A54|A25|A27|A89|A68|A65|A57|A90)\\b",
        "Palm": "PalmSource|Palm",
        "Vertu": "Vertu|Vertu.*Ltd|Vertu.*Ascent|Vertu.*Ayxta|Vertu.*Constellation(F|Quest)?|Vertu.*Monika|Vertu.*Signature",
        "Pantech": "PANTECH|IM-A850S|IM-A840S|IM-A830L|IM-A830K|IM-A830S|IM-A820L|IM-A810K|IM-A810S|IM-A800S|IM-T100K|IM-A725L|IM-A780L|IM-A775C|IM-A770K|IM-A760S|IM-A750K|IM-A740S|IM-A730S|IM-A720L|IM-A710K|IM-A690L|IM-A690S|IM-A650S|IM-A630K|IM-A600S|VEGA PTL21|PT003|P8010|ADR910L|P6030|P6020|P9070|P4100|P9060|P5000|CDM8992|TXT8045|ADR8995|IS11PT|P2030|P6010|P8000|PT002|IS06|CDM8999|P9050|PT001|TXT8040|P2020|P9020|P2000|P7040|P7000|C790",
        "Fly": "IQ230|IQ444|IQ450|IQ440|IQ442|IQ441|IQ245|IQ256|IQ236|IQ255|IQ235|IQ245|IQ275|IQ240|IQ285|IQ280|IQ270|IQ260|IQ250",
        "Wiko": "KITE 4G|HIGHWAY|GETAWAY|STAIRWAY|DARKSIDE|DARKFULL|DARKNIGHT|DARKMOON|SLIDE|WAX 4G|RAINBOW|BLOOM|SUNSET|GOA(?!nna)|LENNY|BARRY|IGGY|OZZY|CINK FIVE|CINK PEAX|CINK PEAX 2|CINK SLIM|CINK SLIM 2|CINK +|CINK KING|CINK PEAX|CINK SLIM|SUBLIM",
        "iMobile": "i-mobile (IQ|i-STYLE|idea|ZAA|Hitz)",
        "SimValley": "\\b(SP-80|XT-930|SX-340|XT-930|SX-310|SP-360|SP60|SPT-800|SP-120|SPT-800|SP-140|SPX-5|SPX-8|SP-100|SPX-8|SPX-12)\\b",
        "Wolfgang": "AT-B24D|AT-AS50HD|AT-AS40W|AT-AS55HD|AT-AS45q2|AT-B26D|AT-AS50Q",
        "Alcatel": "Alcatel",
        "Nintendo": "Nintendo 3DS",
        "Amoi": "Amoi",
        "INQ": "INQ",
        "GenericPhone": "Tapatalk|PDA;|SAGEM|\\bmmp\\b|pocket|\\bpsp\\b|symbian|Smartphone|smartfon|treo|up.browser|up.link|vodafone|\\bwap\\b|nokia|Series40|Series60|S60|SonyEricsson|N900|MAUI.*WAP.*Browser"
    },
    "tablets": {
        "iPad": "iPad|iPad.*Mobile",
        "NexusTablet": "Android.*Nexus[\\s]+(7|9|10)",
        "SamsungTablet": "SAMSUNG.*Tablet|Galaxy.*Tab|SC-01C|GT-P1000|GT-P1003|GT-P1010|GT-P3105|GT-P6210|GT-P6800|GT-P6810|GT-P7100|GT-P7300|GT-P7310|GT-P7500|GT-P7510|SCH-I800|SCH-I815|SCH-I905|SGH-I957|SGH-I987|SGH-T849|SGH-T859|SGH-T869|SPH-P100|GT-P3100|GT-P3108|GT-P3110|GT-P5100|GT-P5110|GT-P6200|GT-P7320|GT-P7511|GT-N8000|GT-P8510|SGH-I497|SPH-P500|SGH-T779|SCH-I705|SCH-I915|GT-N8013|GT-P3113|GT-P5113|GT-P8110|GT-N8010|GT-N8005|GT-N8020|GT-P1013|GT-P6201|GT-P7501|GT-N5100|GT-N5105|GT-N5110|SHV-E140K|SHV-E140L|SHV-E140S|SHV-E150S|SHV-E230K|SHV-E230L|SHV-E230S|SHW-M180K|SHW-M180L|SHW-M180S|SHW-M180W|SHW-M300W|SHW-M305W|SHW-M380K|SHW-M380S|SHW-M380W|SHW-M430W|SHW-M480K|SHW-M480S|SHW-M480W|SHW-M485W|SHW-M486W|SHW-M500W|GT-I9228|SCH-P739|SCH-I925|GT-I9200|GT-P5200|GT-P5210|GT-P5210X|SM-T311|SM-T310|SM-T310X|SM-T210|SM-T210R|SM-T211|SM-P600|SM-P601|SM-P605|SM-P900|SM-P901|SM-T217|SM-T217A|SM-T217S|SM-P6000|SM-T3100|SGH-I467|XE500|SM-T110|GT-P5220|GT-I9200X|GT-N5110X|GT-N5120|SM-P905|SM-T111|SM-T2105|SM-T315|SM-T320|SM-T320X|SM-T321|SM-T520|SM-T525|SM-T530NU|SM-T230NU|SM-T330NU|SM-T900|XE500T1C|SM-P605V|SM-P905V|SM-T337V|SM-T537V|SM-T707V|SM-T807V|SM-P600X|SM-P900X|SM-T210X|SM-T230|SM-T230X|SM-T325|GT-P7503|SM-T531|SM-T330|SM-T530|SM-T705|SM-T705C|SM-T535|SM-T331|SM-T800|SM-T700|SM-T537|SM-T807|SM-P907A|SM-T337A|SM-T537A|SM-T707A|SM-T807A|SM-T237|SM-T807P|SM-P607T|SM-T217T|SM-T337T|SM-T807T|SM-T116NQ|SM-P550|SM-T350|SM-T550|SM-T9000|SM-P9000|SM-T705Y|SM-T805|GT-P3113|SM-T710|SM-T810|SM-T815|SM-T360|SM-T533|SM-T113|SM-T335|SM-T715|SM-T560|SM-T670|SM-T677|SM-T377|SM-T567|SM-T357T|SM-T555|SM-T561|SM-T713|SM-T719|SM-T813|SM-T819|SM-T580|SM-T355Y|SM-T280",
        "Kindle": "Kindle|Silk.*Accelerated|Android.*\\b(KFOT|KFTT|KFJWI|KFJWA|KFOTE|KFSOWI|KFTHWI|KFTHWA|KFAPWI|KFAPWA|WFJWAE|KFSAWA|KFSAWI|KFASWI|KFARWI)\\b",
        "SurfaceTablet": "Windows NT [0-9.]+; ARM;.*(Tablet|ARMBJS)",
        "HPTablet": "HP Slate (7|8|10)|HP ElitePad 900|hp-tablet|EliteBook.*Touch|HP 8|Slate 21|HP SlateBook 10",
        "AsusTablet": "^.*PadFone((?!Mobile).)*$|Transformer|TF101|TF101G|TF300T|TF300TG|TF300TL|TF700T|TF700KL|TF701T|TF810C|ME171|ME301T|ME302C|ME371MG|ME370T|ME372MG|ME172V|ME173X|ME400C|Slider SL101|\\bK00F\\b|\\bK00C\\b|\\bK00E\\b|\\bK00L\\b|TX201LA|ME176C|ME102A|\\bM80TA\\b|ME372CL|ME560CG|ME372CG|ME302KL| K010 | K011 | K017 | K01E |ME572C|ME103K|ME170C|ME171C|\\bME70C\\b|ME581C|ME581CL|ME8510C|ME181C|P01Y|PO1MA|P01Z",
        "BlackBerryTablet": "PlayBook|RIM Tablet",
        "HTCtablet": "HTC_Flyer_P512|HTC Flyer|HTC Jetstream|HTC-P715a|HTC EVO View 4G|PG41200|PG09410",
        "MotorolaTablet": "xoom|sholest|MZ615|MZ605|MZ505|MZ601|MZ602|MZ603|MZ604|MZ606|MZ607|MZ608|MZ609|MZ615|MZ616|MZ617",
        "NookTablet": "Android.*Nook|NookColor|nook browser|BNRV200|BNRV200A|BNTV250|BNTV250A|BNTV400|BNTV600|LogicPD Zoom2",
        "AcerTablet": "Android.*; \\b(A100|A101|A110|A200|A210|A211|A500|A501|A510|A511|A700|A701|W500|W500P|W501|W501P|W510|W511|W700|G100|G100W|B1-A71|B1-710|B1-711|A1-810|A1-811|A1-830)\\b|W3-810|\\bA3-A10\\b|\\bA3-A11\\b|\\bA3-A20\\b|\\bA3-A30",
        "ToshibaTablet": "Android.*(AT100|AT105|AT200|AT205|AT270|AT275|AT300|AT305|AT1S5|AT500|AT570|AT700|AT830)|TOSHIBA.*FOLIO",
        "LGTablet": "\\bL-06C|LG-V909|LG-V900|LG-V700|LG-V510|LG-V500|LG-V410|LG-V400|LG-VK810\\b",
        "FujitsuTablet": "Android.*\\b(F-01D|F-02F|F-05E|F-10D|M532|Q572)\\b",
        "PrestigioTablet": "PMP3170B|PMP3270B|PMP3470B|PMP7170B|PMP3370B|PMP3570C|PMP5870C|PMP3670B|PMP5570C|PMP5770D|PMP3970B|PMP3870C|PMP5580C|PMP5880D|PMP5780D|PMP5588C|PMP7280C|PMP7280C3G|PMP7280|PMP7880D|PMP5597D|PMP5597|PMP7100D|PER3464|PER3274|PER3574|PER3884|PER5274|PER5474|PMP5097CPRO|PMP5097|PMP7380D|PMP5297C|PMP5297C_QUAD|PMP812E|PMP812E3G|PMP812F|PMP810E|PMP880TD|PMT3017|PMT3037|PMT3047|PMT3057|PMT7008|PMT5887|PMT5001|PMT5002",
        "LenovoTablet": "Lenovo TAB|Idea(Tab|Pad)( A1|A10| K1|)|ThinkPad([ ]+)?Tablet|YT3-X90L|YT3-X90F|YT3-X90X|Lenovo.*(S2109|S2110|S5000|S6000|K3011|A3000|A3500|A1000|A2107|A2109|A1107|A5500|A7600|B6000|B8000|B8080)(-|)(FL|F|HV|H|)",
        "DellTablet": "Venue 11|Venue 8|Venue 7|Dell Streak 10|Dell Streak 7",
        "YarvikTablet": "Android.*\\b(TAB210|TAB211|TAB224|TAB250|TAB260|TAB264|TAB310|TAB360|TAB364|TAB410|TAB411|TAB420|TAB424|TAB450|TAB460|TAB461|TAB464|TAB465|TAB467|TAB468|TAB07-100|TAB07-101|TAB07-150|TAB07-151|TAB07-152|TAB07-200|TAB07-201-3G|TAB07-210|TAB07-211|TAB07-212|TAB07-214|TAB07-220|TAB07-400|TAB07-485|TAB08-150|TAB08-200|TAB08-201-3G|TAB08-201-30|TAB09-100|TAB09-211|TAB09-410|TAB10-150|TAB10-201|TAB10-211|TAB10-400|TAB10-410|TAB13-201|TAB274EUK|TAB275EUK|TAB374EUK|TAB462EUK|TAB474EUK|TAB9-200)\\b",
        "MedionTablet": "Android.*\\bOYO\\b|LIFE.*(P9212|P9514|P9516|S9512)|LIFETAB",
        "ArnovaTablet": "97G4|AN10G2|AN7bG3|AN7fG3|AN8G3|AN8cG3|AN7G3|AN9G3|AN7dG3|AN7dG3ST|AN7dG3ChildPad|AN10bG3|AN10bG3DT|AN9G2",
        "IntensoTablet": "INM8002KP|INM1010FP|INM805ND|Intenso Tab|TAB1004",
        "IRUTablet": "M702pro",
        "MegafonTablet": "MegaFon V9|\\bZTE V9\\b|Android.*\\bMT7A\\b",
        "EbodaTablet": "E-Boda (Supreme|Impresspeed|Izzycomm|Essential)",
        "AllViewTablet": "Allview.*(Viva|Alldro|City|Speed|All TV|Frenzy|Quasar|Shine|TX1|AX1|AX2)",
        "ArchosTablet": "\\b(101G9|80G9|A101IT)\\b|Qilive 97R|Archos5|\\bARCHOS (70|79|80|90|97|101|FAMILYPAD|)(b|)(G10| Cobalt| TITANIUM(HD|)| Xenon| Neon|XSK| 2| XS 2| PLATINUM| CARBON|GAMEPAD)\\b",
        "AinolTablet": "NOVO7|NOVO8|NOVO10|Novo7Aurora|Novo7Basic|NOVO7PALADIN|novo9-Spark",
        "NokiaLumiaTablet": "Lumia 2520",
        "SonyTablet": "Sony.*Tablet|Xperia Tablet|Sony Tablet S|SO-03E|SGPT12|SGPT13|SGPT114|SGPT121|SGPT122|SGPT123|SGPT111|SGPT112|SGPT113|SGPT131|SGPT132|SGPT133|SGPT211|SGPT212|SGPT213|SGP311|SGP312|SGP321|EBRD1101|EBRD1102|EBRD1201|SGP351|SGP341|SGP511|SGP512|SGP521|SGP541|SGP551|SGP621|SGP612|SOT31",
        "PhilipsTablet": "\\b(PI2010|PI3000|PI3100|PI3105|PI3110|PI3205|PI3210|PI3900|PI4010|PI7000|PI7100)\\b",
        "CubeTablet": "Android.*(K8GT|U9GT|U10GT|U16GT|U17GT|U18GT|U19GT|U20GT|U23GT|U30GT)|CUBE U8GT",
        "CobyTablet": "MID1042|MID1045|MID1125|MID1126|MID7012|MID7014|MID7015|MID7034|MID7035|MID7036|MID7042|MID7048|MID7127|MID8042|MID8048|MID8127|MID9042|MID9740|MID9742|MID7022|MID7010",
        "MIDTablet": "M9701|M9000|M9100|M806|M1052|M806|T703|MID701|MID713|MID710|MID727|MID760|MID830|MID728|MID933|MID125|MID810|MID732|MID120|MID930|MID800|MID731|MID900|MID100|MID820|MID735|MID980|MID130|MID833|MID737|MID960|MID135|MID860|MID736|MID140|MID930|MID835|MID733|MID4X10",
        "MSITablet": "MSI \\b(Primo 73K|Primo 73L|Primo 81L|Primo 77|Primo 93|Primo 75|Primo 76|Primo 73|Primo 81|Primo 91|Primo 90|Enjoy 71|Enjoy 7|Enjoy 10)\\b",
        "SMiTTablet": "Android.*(\\bMID\\b|MID-560|MTV-T1200|MTV-PND531|MTV-P1101|MTV-PND530)",
        "RockChipTablet": "Android.*(RK2818|RK2808A|RK2918|RK3066)|RK2738|RK2808A",
        "FlyTablet": "IQ310|Fly Vision",
        "bqTablet": "Android.*(bq)?.*(Elcano|Curie|Edison|Maxwell|Kepler|Pascal|Tesla|Hypatia|Platon|Newton|Livingstone|Cervantes|Avant|Aquaris [E|M]10)|Maxwell.*Lite|Maxwell.*Plus",
        "HuaweiTablet": "MediaPad|MediaPad 7 Youth|IDEOS S7|S7-201c|S7-202u|S7-101|S7-103|S7-104|S7-105|S7-106|S7-201|S7-Slim",
        "NecTablet": "\\bN-06D|\\bN-08D",
        "PantechTablet": "Pantech.*P4100",
        "BronchoTablet": "Broncho.*(N701|N708|N802|a710)",
        "VersusTablet": "TOUCHPAD.*[78910]|\\bTOUCHTAB\\b",
        "ZyncTablet": "z1000|Z99 2G|z99|z930|z999|z990|z909|Z919|z900",
        "PositivoTablet": "TB07STA|TB10STA|TB07FTA|TB10FTA",
        "NabiTablet": "Android.*\\bNabi",
        "KoboTablet": "Kobo Touch|\\bK080\\b|\\bVox\\b Build|\\bArc\\b Build",
        "DanewTablet": "DSlide.*\\b(700|701R|702|703R|704|802|970|971|972|973|974|1010|1012)\\b",
        "TexetTablet": "NaviPad|TB-772A|TM-7045|TM-7055|TM-9750|TM-7016|TM-7024|TM-7026|TM-7041|TM-7043|TM-7047|TM-8041|TM-9741|TM-9747|TM-9748|TM-9751|TM-7022|TM-7021|TM-7020|TM-7011|TM-7010|TM-7023|TM-7025|TM-7037W|TM-7038W|TM-7027W|TM-9720|TM-9725|TM-9737W|TM-1020|TM-9738W|TM-9740|TM-9743W|TB-807A|TB-771A|TB-727A|TB-725A|TB-719A|TB-823A|TB-805A|TB-723A|TB-715A|TB-707A|TB-705A|TB-709A|TB-711A|TB-890HD|TB-880HD|TB-790HD|TB-780HD|TB-770HD|TB-721HD|TB-710HD|TB-434HD|TB-860HD|TB-840HD|TB-760HD|TB-750HD|TB-740HD|TB-730HD|TB-722HD|TB-720HD|TB-700HD|TB-500HD|TB-470HD|TB-431HD|TB-430HD|TB-506|TB-504|TB-446|TB-436|TB-416|TB-146SE|TB-126SE",
        "PlaystationTablet": "Playstation.*(Portable|Vita)",
        "TrekstorTablet": "ST10416-1|VT10416-1|ST70408-1|ST702xx-1|ST702xx-2|ST80208|ST97216|ST70104-2|VT10416-2|ST10216-2A|SurfTab",
        "PyleAudioTablet": "\\b(PTBL10CEU|PTBL10C|PTBL72BC|PTBL72BCEU|PTBL7CEU|PTBL7C|PTBL92BC|PTBL92BCEU|PTBL9CEU|PTBL9CUK|PTBL9C)\\b",
        "AdvanTablet": "Android.* \\b(E3A|T3X|T5C|T5B|T3E|T3C|T3B|T1J|T1F|T2A|T1H|T1i|E1C|T1-E|T5-A|T4|E1-B|T2Ci|T1-B|T1-D|O1-A|E1-A|T1-A|T3A|T4i)\\b ",
        "DanyTechTablet": "Genius Tab G3|Genius Tab S2|Genius Tab Q3|Genius Tab G4|Genius Tab Q4|Genius Tab G-II|Genius TAB GII|Genius TAB GIII|Genius Tab S1",
        "GalapadTablet": "Android.*\\bG1\\b",
        "MicromaxTablet": "Funbook|Micromax.*\\b(P250|P560|P360|P362|P600|P300|P350|P500|P275)\\b",
        "KarbonnTablet": "Android.*\\b(A39|A37|A34|ST8|ST10|ST7|Smart Tab3|Smart Tab2)\\b",
        "AllFineTablet": "Fine7 Genius|Fine7 Shine|Fine7 Air|Fine8 Style|Fine9 More|Fine10 Joy|Fine11 Wide",
        "PROSCANTablet": "\\b(PEM63|PLT1023G|PLT1041|PLT1044|PLT1044G|PLT1091|PLT4311|PLT4311PL|PLT4315|PLT7030|PLT7033|PLT7033D|PLT7035|PLT7035D|PLT7044K|PLT7045K|PLT7045KB|PLT7071KG|PLT7072|PLT7223G|PLT7225G|PLT7777G|PLT7810K|PLT7849G|PLT7851G|PLT7852G|PLT8015|PLT8031|PLT8034|PLT8036|PLT8080K|PLT8082|PLT8088|PLT8223G|PLT8234G|PLT8235G|PLT8816K|PLT9011|PLT9045K|PLT9233G|PLT9735|PLT9760G|PLT9770G)\\b",
        "YONESTablet": "BQ1078|BC1003|BC1077|RK9702|BC9730|BC9001|IT9001|BC7008|BC7010|BC708|BC728|BC7012|BC7030|BC7027|BC7026",
        "ChangJiaTablet": "TPC7102|TPC7103|TPC7105|TPC7106|TPC7107|TPC7201|TPC7203|TPC7205|TPC7210|TPC7708|TPC7709|TPC7712|TPC7110|TPC8101|TPC8103|TPC8105|TPC8106|TPC8203|TPC8205|TPC8503|TPC9106|TPC9701|TPC97101|TPC97103|TPC97105|TPC97106|TPC97111|TPC97113|TPC97203|TPC97603|TPC97809|TPC97205|TPC10101|TPC10103|TPC10106|TPC10111|TPC10203|TPC10205|TPC10503",
        "GUTablet": "TX-A1301|TX-M9002|Q702|kf026",
        "PointOfViewTablet": "TAB-P506|TAB-navi-7-3G-M|TAB-P517|TAB-P-527|TAB-P701|TAB-P703|TAB-P721|TAB-P731N|TAB-P741|TAB-P825|TAB-P905|TAB-P925|TAB-PR945|TAB-PL1015|TAB-P1025|TAB-PI1045|TAB-P1325|TAB-PROTAB[0-9]+|TAB-PROTAB25|TAB-PROTAB26|TAB-PROTAB27|TAB-PROTAB26XL|TAB-PROTAB2-IPS9|TAB-PROTAB30-IPS9|TAB-PROTAB25XXL|TAB-PROTAB26-IPS10|TAB-PROTAB30-IPS10",
        "OvermaxTablet": "OV-(SteelCore|NewBase|Basecore|Baseone|Exellen|Quattor|EduTab|Solution|ACTION|BasicTab|TeddyTab|MagicTab|Stream|TB-08|TB-09)",
        "HCLTablet": "HCL.*Tablet|Connect-3G-2.0|Connect-2G-2.0|ME Tablet U1|ME Tablet U2|ME Tablet G1|ME Tablet X1|ME Tablet Y2|ME Tablet Sync",
        "DPSTablet": "DPS Dream 9|DPS Dual 7",
        "VistureTablet": "V97 HD|i75 3G|Visture V4( HD)?|Visture V5( HD)?|Visture V10",
        "CrestaTablet": "CTP(-)?810|CTP(-)?818|CTP(-)?828|CTP(-)?838|CTP(-)?888|CTP(-)?978|CTP(-)?980|CTP(-)?987|CTP(-)?988|CTP(-)?989",
        "MediatekTablet": "\\bMT8125|MT8389|MT8135|MT8377\\b",
        "ConcordeTablet": "Concorde([ ]+)?Tab|ConCorde ReadMan",
        "GoCleverTablet": "GOCLEVER TAB|A7GOCLEVER|M1042|M7841|M742|R1042BK|R1041|TAB A975|TAB A7842|TAB A741|TAB A741L|TAB M723G|TAB M721|TAB A1021|TAB I921|TAB R721|TAB I720|TAB T76|TAB R70|TAB R76.2|TAB R106|TAB R83.2|TAB M813G|TAB I721|GCTA722|TAB I70|TAB I71|TAB S73|TAB R73|TAB R74|TAB R93|TAB R75|TAB R76.1|TAB A73|TAB A93|TAB A93.2|TAB T72|TAB R83|TAB R974|TAB R973|TAB A101|TAB A103|TAB A104|TAB A104.2|R105BK|M713G|A972BK|TAB A971|TAB R974.2|TAB R104|TAB R83.3|TAB A1042",
        "ModecomTablet": "FreeTAB 9000|FreeTAB 7.4|FreeTAB 7004|FreeTAB 7800|FreeTAB 2096|FreeTAB 7.5|FreeTAB 1014|FreeTAB 1001 |FreeTAB 8001|FreeTAB 9706|FreeTAB 9702|FreeTAB 7003|FreeTAB 7002|FreeTAB 1002|FreeTAB 7801|FreeTAB 1331|FreeTAB 1004|FreeTAB 8002|FreeTAB 8014|FreeTAB 9704|FreeTAB 1003",
        "VoninoTablet": "\\b(Argus[ _]?S|Diamond[ _]?79HD|Emerald[ _]?78E|Luna[ _]?70C|Onyx[ _]?S|Onyx[ _]?Z|Orin[ _]?HD|Orin[ _]?S|Otis[ _]?S|SpeedStar[ _]?S|Magnet[ _]?M9|Primus[ _]?94[ _]?3G|Primus[ _]?94HD|Primus[ _]?QS|Android.*\\bQ8\\b|Sirius[ _]?EVO[ _]?QS|Sirius[ _]?QS|Spirit[ _]?S)\\b",
        "ECSTablet": "V07OT2|TM105A|S10OT1|TR10CS1",
        "StorexTablet": "eZee[_']?(Tab|Go)[0-9]+|TabLC7|Looney Tunes Tab",
        "VodafoneTablet": "SmartTab([ ]+)?[0-9]+|SmartTabII10|SmartTabII7|VF-1497",
        "EssentielBTablet": "Smart[ ']?TAB[ ]+?[0-9]+|Family[ ']?TAB2",
        "RossMoorTablet": "RM-790|RM-997|RMD-878G|RMD-974R|RMT-705A|RMT-701|RME-601|RMT-501|RMT-711",
        "iMobileTablet": "i-mobile i-note",
        "TolinoTablet": "tolino tab [0-9.]+|tolino shine",
        "AudioSonicTablet": "\\bC-22Q|T7-QC|T-17B|T-17P\\b",
        "AMPETablet": "Android.* A78 ",
        "SkkTablet": "Android.* (SKYPAD|PHOENIX|CYCLOPS)",
        "TecnoTablet": "TECNO P9",
        "JXDTablet": "Android.* \\b(F3000|A3300|JXD5000|JXD3000|JXD2000|JXD300B|JXD300|S5800|S7800|S602b|S5110b|S7300|S5300|S602|S603|S5100|S5110|S601|S7100a|P3000F|P3000s|P101|P200s|P1000m|P200m|P9100|P1000s|S6600b|S908|P1000|P300|S18|S6600|S9100)\\b",
        "iJoyTablet": "Tablet (Spirit 7|Essentia|Galatea|Fusion|Onix 7|Landa|Titan|Scooby|Deox|Stella|Themis|Argon|Unique 7|Sygnus|Hexen|Finity 7|Cream|Cream X2|Jade|Neon 7|Neron 7|Kandy|Scape|Saphyr 7|Rebel|Biox|Rebel|Rebel 8GB|Myst|Draco 7|Myst|Tab7-004|Myst|Tadeo Jones|Tablet Boing|Arrow|Draco Dual Cam|Aurix|Mint|Amity|Revolution|Finity 9|Neon 9|T9w|Amity 4GB Dual Cam|Stone 4GB|Stone 8GB|Andromeda|Silken|X2|Andromeda II|Halley|Flame|Saphyr 9,7|Touch 8|Planet|Triton|Unique 10|Hexen 10|Memphis 4GB|Memphis 8GB|Onix 10)",
        "FX2Tablet": "FX2 PAD7|FX2 PAD10",
        "XoroTablet": "KidsPAD 701|PAD[ ]?712|PAD[ ]?714|PAD[ ]?716|PAD[ ]?717|PAD[ ]?718|PAD[ ]?720|PAD[ ]?721|PAD[ ]?722|PAD[ ]?790|PAD[ ]?792|PAD[ ]?900|PAD[ ]?9715D|PAD[ ]?9716DR|PAD[ ]?9718DR|PAD[ ]?9719QR|PAD[ ]?9720QR|TelePAD1030|Telepad1032|TelePAD730|TelePAD731|TelePAD732|TelePAD735Q|TelePAD830|TelePAD9730|TelePAD795|MegaPAD 1331|MegaPAD 1851|MegaPAD 2151",
        "ViewsonicTablet": "ViewPad 10pi|ViewPad 10e|ViewPad 10s|ViewPad E72|ViewPad7|ViewPad E100|ViewPad 7e|ViewSonic VB733|VB100a",
        "OdysTablet": "LOOX|XENO10|ODYS[ -](Space|EVO|Xpress|NOON)|\\bXELIO\\b|Xelio10Pro|XELIO7PHONETAB|XELIO10EXTREME|XELIOPT2|NEO_QUAD10",
        "CaptivaTablet": "CAPTIVA PAD",
        "IconbitTablet": "NetTAB|NT-3702|NT-3702S|NT-3702S|NT-3603P|NT-3603P|NT-0704S|NT-0704S|NT-3805C|NT-3805C|NT-0806C|NT-0806C|NT-0909T|NT-0909T|NT-0907S|NT-0907S|NT-0902S|NT-0902S",
        "TeclastTablet": "T98 4G|\\bP80\\b|\\bX90HD\\b|X98 Air|X98 Air 3G|\\bX89\\b|P80 3G|\\bX80h\\b|P98 Air|\\bX89HD\\b|P98 3G|\\bP90HD\\b|P89 3G|X98 3G|\\bP70h\\b|P79HD 3G|G18d 3G|\\bP79HD\\b|\\bP89s\\b|\\bA88\\b|\\bP10HD\\b|\\bP19HD\\b|G18 3G|\\bP78HD\\b|\\bA78\\b|\\bP75\\b|G17s 3G|G17h 3G|\\bP85t\\b|\\bP90\\b|\\bP11\\b|\\bP98t\\b|\\bP98HD\\b|\\bG18d\\b|\\bP85s\\b|\\bP11HD\\b|\\bP88s\\b|\\bA80HD\\b|\\bA80se\\b|\\bA10h\\b|\\bP89\\b|\\bP78s\\b|\\bG18\\b|\\bP85\\b|\\bA70h\\b|\\bA70\\b|\\bG17\\b|\\bP18\\b|\\bA80s\\b|\\bA11s\\b|\\bP88HD\\b|\\bA80h\\b|\\bP76s\\b|\\bP76h\\b|\\bP98\\b|\\bA10HD\\b|\\bP78\\b|\\bP88\\b|\\bA11\\b|\\bA10t\\b|\\bP76a\\b|\\bP76t\\b|\\bP76e\\b|\\bP85HD\\b|\\bP85a\\b|\\bP86\\b|\\bP75HD\\b|\\bP76v\\b|\\bA12\\b|\\bP75a\\b|\\bA15\\b|\\bP76Ti\\b|\\bP81HD\\b|\\bA10\\b|\\bT760VE\\b|\\bT720HD\\b|\\bP76\\b|\\bP73\\b|\\bP71\\b|\\bP72\\b|\\bT720SE\\b|\\bC520Ti\\b|\\bT760\\b|\\bT720VE\\b|T720-3GE|T720-WiFi",
        "OndaTablet": "\\b(V975i|Vi30|VX530|V701|Vi60|V701s|Vi50|V801s|V719|Vx610w|VX610W|V819i|Vi10|VX580W|Vi10|V711s|V813|V811|V820w|V820|Vi20|V711|VI30W|V712|V891w|V972|V819w|V820w|Vi60|V820w|V711|V813s|V801|V819|V975s|V801|V819|V819|V818|V811|V712|V975m|V101w|V961w|V812|V818|V971|V971s|V919|V989|V116w|V102w|V973|Vi40)\\b[\\s]+",
        "JaytechTablet": "TPC-PA762",
        "BlaupunktTablet": "Endeavour 800NG|Endeavour 1010",
        "DigmaTablet": "\\b(iDx10|iDx9|iDx8|iDx7|iDxD7|iDxD8|iDsQ8|iDsQ7|iDsQ8|iDsD10|iDnD7|3TS804H|iDsQ11|iDj7|iDs10)\\b",
        "EvolioTablet": "ARIA_Mini_wifi|Aria[ _]Mini|Evolio X10|Evolio X7|Evolio X8|\\bEvotab\\b|\\bNeura\\b",
        "LavaTablet": "QPAD E704|\\bIvoryS\\b|E-TAB IVORY|\\bE-TAB\\b",
        "AocTablet": "MW0811|MW0812|MW0922|MTK8382|MW1031|MW0831|MW0821|MW0931|MW0712",
        "MpmanTablet": "MP11 OCTA|MP10 OCTA|MPQC1114|MPQC1004|MPQC994|MPQC974|MPQC973|MPQC804|MPQC784|MPQC780|\\bMPG7\\b|MPDCG75|MPDCG71|MPDC1006|MP101DC|MPDC9000|MPDC905|MPDC706HD|MPDC706|MPDC705|MPDC110|MPDC100|MPDC99|MPDC97|MPDC88|MPDC8|MPDC77|MP709|MID701|MID711|MID170|MPDC703|MPQC1010",
        "CelkonTablet": "CT695|CT888|CT[\\s]?910|CT7 Tab|CT9 Tab|CT3 Tab|CT2 Tab|CT1 Tab|C820|C720|\\bCT-1\\b",
        "WolderTablet": "miTab \\b(DIAMOND|SPACE|BROOKLYN|NEO|FLY|MANHATTAN|FUNK|EVOLUTION|SKY|GOCAR|IRON|GENIUS|POP|MINT|EPSILON|BROADWAY|JUMP|HOP|LEGEND|NEW AGE|LINE|ADVANCE|FEEL|FOLLOW|LIKE|LINK|LIVE|THINK|FREEDOM|CHICAGO|CLEVELAND|BALTIMORE-GH|IOWA|BOSTON|SEATTLE|PHOENIX|DALLAS|IN 101|MasterChef)\\b",
        "MiTablet": "\\bMI PAD\\b|\\bHM NOTE 1W\\b",
        "NibiruTablet": "Nibiru M1|Nibiru Jupiter One",
        "NexoTablet": "NEXO NOVA|NEXO 10|NEXO AVIO|NEXO FREE|NEXO GO|NEXO EVO|NEXO 3G|NEXO SMART|NEXO KIDDO|NEXO MOBI",
        "LeaderTablet": "TBLT10Q|TBLT10I|TBL-10WDKB|TBL-10WDKBO2013|TBL-W230V2|TBL-W450|TBL-W500|SV572|TBLT7I|TBA-AC7-8G|TBLT79|TBL-8W16|TBL-10W32|TBL-10WKB|TBL-W100",
        "UbislateTablet": "UbiSlate[\\s]?7C",
        "PocketBookTablet": "Pocketbook",
        "KocasoTablet": "\\b(TB-1207)\\b",
        "HisenseTablet": "\\b(F5281|E2371)\\b",
        "Hudl": "Hudl HT7S3|Hudl 2",
        "TelstraTablet": "T-Hub2",
        "GenericTablet": "Android.*\\b97D\\b|Tablet(?!.*PC)|BNTV250A|MID-WCDMA|LogicPD Zoom2|\\bA7EB\\b|CatNova8|A1_07|CT704|CT1002|\\bM721\\b|rk30sdk|\\bEVOTAB\\b|M758A|ET904|ALUMIUM10|Smartfren Tab|Endeavour 1010|Tablet-PC-4|Tagi Tab|\\bM6pro\\b|CT1020W|arc 10HD|\\bTP750\\b"
    },
    "oss": {
        "AndroidOS": "Android",
        "BlackBerryOS": "blackberry|\\bBB10\\b|rim tablet os",
        "PalmOS": "PalmOS|avantgo|blazer|elaine|hiptop|palm|plucker|xiino",
        "SymbianOS": "Symbian|SymbOS|Series60|Series40|SYB-[0-9]+|\\bS60\\b",
        "WindowsMobileOS": "Windows CE.*(PPC|Smartphone|Mobile|[0-9]{3}x[0-9]{3})|Window Mobile|Windows Phone [0-9.]+|WCE;",
        "WindowsPhoneOS": "Windows Phone 10.0|Windows Phone 8.1|Windows Phone 8.0|Windows Phone OS|XBLWP7|ZuneWP7|Windows NT 6.[23]; ARM;",
        "iOS": "\\biPhone.*Mobile|\\biPod|\\biPad",
        "MeeGoOS": "MeeGo",
        "MaemoOS": "Maemo",
        "JavaOS": "J2ME\/|\\bMIDP\\b|\\bCLDC\\b",
        "webOS": "webOS|hpwOS",
        "badaOS": "\\bBada\\b",
        "BREWOS": "BREW"
    },
    "uas": {
        "Chrome": "\\bCrMo\\b|CriOS|Android.*Chrome\/[.0-9]* (Mobile)?",
        "Dolfin": "\\bDolfin\\b",
        "Opera": "Opera.*Mini|Opera.*Mobi|Android.*Opera|Mobile.*OPR\/[0-9.]+|Coast\/[0-9.]+",
        "Skyfire": "Skyfire",
        "Edge": "Mobile Safari\/[.0-9]* Edge",
        "IE": "IEMobile|MSIEMobile",
        "Firefox": "fennec|firefox.*maemo|(Mobile|Tablet).*Firefox|Firefox.*Mobile|FxiOS",
        "Bolt": "bolt",
        "TeaShark": "teashark",
        "Blazer": "Blazer",
        "Safari": "Version.*Mobile.*Safari|Safari.*Mobile|MobileSafari",
        "UCBrowser": "UC.*Browser|UCWEB",
        "baiduboxapp": "baiduboxapp",
        "baidubrowser": "baidubrowser",
        "DiigoBrowser": "DiigoBrowser",
        "Puffin": "Puffin",
        "Mercury": "\\bMercury\\b",
        "ObigoBrowser": "Obigo",
        "NetFront": "NF-Browser",
        "GenericBrowser": "NokiaBrowser|OviBrowser|OneBrowser|TwonkyBeamBrowser|SEMC.*Browser|FlyFlow|Minimo|NetFront|Novarra-Vision|MQQBrowser|MicroMessenger",
        "PaleMoon": "Android.*PaleMoon|Mobile.*PaleMoon"
    },
    "props": {
        "Mobile": "Mobile\/[VER]",
        "Build": "Build\/[VER]",
        "Version": "Version\/[VER]",
        "VendorID": "VendorID\/[VER]",
        "iPad": "iPad.*CPU[a-z ]+[VER]",
        "iPhone": "iPhone.*CPU[a-z ]+[VER]",
        "iPod": "iPod.*CPU[a-z ]+[VER]",
        "Kindle": "Kindle\/[VER]",
        "Chrome": [
            "Chrome\/[VER]",
            "CriOS\/[VER]",
            "CrMo\/[VER]"
        ],
        "Coast": [
            "Coast\/[VER]"
        ],
        "Dolfin": "Dolfin\/[VER]",
        "Firefox": [
            "Firefox\/[VER]",
            "FxiOS\/[VER]"
        ],
        "Fennec": "Fennec\/[VER]",
        "Edge": "Edge\/[VER]",
        "IE": [
            "IEMobile\/[VER];",
            "IEMobile [VER]",
            "MSIE [VER];",
            "Trident\/[0-9.]+;.*rv:[VER]"
        ],
        "NetFront": "NetFront\/[VER]",
        "NokiaBrowser": "NokiaBrowser\/[VER]",
        "Opera": [
            " OPR\/[VER]",
            "Opera Mini\/[VER]",
            "Version\/[VER]"
        ],
        "Opera Mini": "Opera Mini\/[VER]",
        "Opera Mobi": "Version\/[VER]",
        "UC Browser": "UC Browser[VER]",
        "MQQBrowser": "MQQBrowser\/[VER]",
        "MicroMessenger": "MicroMessenger\/[VER]",
        "baiduboxapp": "baiduboxapp\/[VER]",
        "baidubrowser": "baidubrowser\/[VER]",
        "SamsungBrowser": "SamsungBrowser\/[VER]",
        "Iron": "Iron\/[VER]",
        "Safari": [
            "Version\/[VER]",
            "Safari\/[VER]"
        ],
        "Skyfire": "Skyfire\/[VER]",
        "Tizen": "Tizen\/[VER]",
        "Webkit": "webkit[ \/][VER]",
        "PaleMoon": "PaleMoon\/[VER]",
        "Gecko": "Gecko\/[VER]",
        "Trident": "Trident\/[VER]",
        "Presto": "Presto\/[VER]",
        "Goanna": "Goanna\/[VER]",
        "iOS": " \\bi?OS\\b [VER][ ;]{1}",
        "Android": "Android [VER]",
        "BlackBerry": [
            "BlackBerry[\\w]+\/[VER]",
            "BlackBerry.*Version\/[VER]",
            "Version\/[VER]"
        ],
        "BREW": "BREW [VER]",
        "Java": "Java\/[VER]",
        "Windows Phone OS": [
            "Windows Phone OS [VER]",
            "Windows Phone [VER]"
        ],
        "Windows Phone": "Windows Phone [VER]",
        "Windows CE": "Windows CE\/[VER]",
        "Windows NT": "Windows NT [VER]",
        "Symbian": [
            "SymbianOS\/[VER]",
            "Symbian\/[VER]"
        ],
        "webOS": [
            "webOS\/[VER]",
            "hpwOS\/[VER];"
        ]
    },
    "utils": {
        "Bot": "Googlebot|facebookexternalhit|AdsBot-Google|Google Keyword Suggestion|Facebot|YandexBot|YandexMobileBot|bingbot|ia_archiver|AhrefsBot|Ezooms|GSLFbot|WBSearchBot|Twitterbot|TweetmemeBot|Twikle|PaperLiBot|Wotbox|UnwindFetchor|Exabot|MJ12bot|YandexImages|TurnitinBot|Pingdom",
        "MobileBot": "Googlebot-Mobile|AdsBot-Google-Mobile|YahooSeeker\/M1A1-R2D2",
        "DesktopMode": "WPDesktop",
        "TV": "SonyDTV|HbbTV",
        "WebKit": "(webkit)[ \/]([\\w.]+)",
        "Console": "\\b(Nintendo|Nintendo WiiU|Nintendo 3DS|PLAYSTATION|Xbox)\\b",
        "Watch": "SM-V700"
    }
};

    // following patterns come from http://detectmobilebrowsers.com/
    impl.detectMobileBrowsers = {
        fullPattern: /(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i,
        shortPattern: /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i,
        tabletPattern: /android|ipad|playbook|silk/i
    };

    var hasOwnProp = Object.prototype.hasOwnProperty,
        isArray;

    impl.FALLBACK_PHONE = 'UnknownPhone';
    impl.FALLBACK_TABLET = 'UnknownTablet';
    impl.FALLBACK_MOBILE = 'UnknownMobile';

    isArray = ('isArray' in Array) ?
        Array.isArray : function (value) { return Object.prototype.toString.call(value) === '[object Array]'; };

    function equalIC(a, b) {
        return a != null && b != null && a.toLowerCase() === b.toLowerCase();
    }

    function containsIC(array, value) {
        var valueLC, i, len = array.length;
        if (!len || !value) {
            return false;
        }
        valueLC = value.toLowerCase();
        for (i = 0; i < len; ++i) {
            if (valueLC === array[i].toLowerCase()) {
                return true;
            }
        }
        return false;
    }

    function convertPropsToRegExp(object) {
        for (var key in object) {
            if (hasOwnProp.call(object, key)) {
                object[key] = new RegExp(object[key], 'i');
            }
        }
    }

    (function init() {
        var key, values, value, i, len, verPos, mobileDetectRules = impl.mobileDetectRules;
        for (key in mobileDetectRules.props) {
            if (hasOwnProp.call(mobileDetectRules.props, key)) {
                values = mobileDetectRules.props[key];
                if (!isArray(values)) {
                    values = [values];
                }
                len = values.length;
                for (i = 0; i < len; ++i) {
                    value = values[i];
                    verPos = value.indexOf('[VER]');
                    if (verPos >= 0) {
                        value = value.substring(0, verPos) + '([\\w._\\+]+)' + value.substring(verPos + 5);
                    }
                    values[i] = new RegExp(value, 'i');
                }
                mobileDetectRules.props[key] = values;
            }
        }
        convertPropsToRegExp(mobileDetectRules.oss);
        convertPropsToRegExp(mobileDetectRules.phones);
        convertPropsToRegExp(mobileDetectRules.tablets);
        convertPropsToRegExp(mobileDetectRules.uas);
        convertPropsToRegExp(mobileDetectRules.utils);

        // copy some patterns to oss0 which are tested first (see issue#15)
        mobileDetectRules.oss0 = {
            WindowsPhoneOS: mobileDetectRules.oss.WindowsPhoneOS,
            WindowsMobileOS: mobileDetectRules.oss.WindowsMobileOS
        };
    }());

    /**
     * Test userAgent string against a set of rules and find the first matched key.
     * @param {Object} rules (key is String, value is RegExp)
     * @param {String} userAgent the navigator.userAgent (or HTTP-Header 'User-Agent').
     * @returns {String|null} the matched key if found, otherwise <tt>null</tt>
     * @private
     */
    impl.findMatch = function(rules, userAgent) {
        for (var key in rules) {
            if (hasOwnProp.call(rules, key)) {
                if (rules[key].test(userAgent)) {
                    return key;
                }
            }
        }
        return null;
    };

    /**
     * Test userAgent string against a set of rules and return an array of matched keys.
     * @param {Object} rules (key is String, value is RegExp)
     * @param {String} userAgent the navigator.userAgent (or HTTP-Header 'User-Agent').
     * @returns {Array} an array of matched keys, may be empty when there is no match, but not <tt>null</tt>
     * @private
     */
    impl.findMatches = function(rules, userAgent) {
        var result = [];
        for (var key in rules) {
            if (hasOwnProp.call(rules, key)) {
                if (rules[key].test(userAgent)) {
                    result.push(key);
                }
            }
        }
        return result;
    };

    /**
     * Check the version of the given property in the User-Agent.
     *
     * @param {String} propertyName
     * @param {String} userAgent
     * @return {String} version or <tt>null</tt> if version not found
     * @private
     */
    impl.getVersionStr = function (propertyName, userAgent) {
        var props = impl.mobileDetectRules.props, patterns, i, len, match;
        if (hasOwnProp.call(props, propertyName)) {
            patterns = props[propertyName];
            len = patterns.length;
            for (i = 0; i < len; ++i) {
                match = patterns[i].exec(userAgent);
                if (match !== null) {
                    return match[1];
                }
            }
        }
        return null;
    };

    /**
     * Check the version of the given property in the User-Agent.
     * Will return a float number. (eg. 2_0 will return 2.0, 4.3.1 will return 4.31)
     *
     * @param {String} propertyName
     * @param {String} userAgent
     * @return {Number} version or <tt>NaN</tt> if version not found
     * @private
     */
    impl.getVersion = function (propertyName, userAgent) {
        var version = impl.getVersionStr(propertyName, userAgent);
        return version ? impl.prepareVersionNo(version) : NaN;
    };

    /**
     * Prepare the version number.
     *
     * @param {String} version
     * @return {Number} the version number as a floating number
     * @private
     */
    impl.prepareVersionNo = function (version) {
        var numbers;

        numbers = version.split(/[a-z._ \/\-]/i);
        if (numbers.length === 1) {
            version = numbers[0];
        }
        if (numbers.length > 1) {
            version = numbers[0] + '.';
            numbers.shift();
            version += numbers.join('');
        }
        return Number(version);
    };

    impl.isMobileFallback = function (userAgent) {
        return impl.detectMobileBrowsers.fullPattern.test(userAgent) ||
            impl.detectMobileBrowsers.shortPattern.test(userAgent.substr(0,4));
    };

    impl.isTabletFallback = function (userAgent) {
        return impl.detectMobileBrowsers.tabletPattern.test(userAgent);
    };

    impl.prepareDetectionCache = function (cache, userAgent, maxPhoneWidth) {
        if (cache.mobile !== undefined) {
            return;
        }
        var phone, tablet, phoneSized;

        // first check for stronger tablet rules, then phone (see issue#5)
        tablet = impl.findMatch(impl.mobileDetectRules.tablets, userAgent);
        if (tablet) {
            cache.mobile = cache.tablet = tablet;
            cache.phone = null;
            return; // unambiguously identified as tablet
        }

        phone = impl.findMatch(impl.mobileDetectRules.phones, userAgent);
        if (phone) {
            cache.mobile = cache.phone = phone;
            cache.tablet = null;
            return; // unambiguously identified as phone
        }

        // our rules haven't found a match -> try more general fallback rules
        if (impl.isMobileFallback(userAgent)) {
            phoneSized = MobileDetect.isPhoneSized(maxPhoneWidth);
            if (phoneSized === undefined) {
                cache.mobile = impl.FALLBACK_MOBILE;
                cache.tablet = cache.phone = null;
            } else if (phoneSized) {
                cache.mobile = cache.phone = impl.FALLBACK_PHONE;
                cache.tablet = null;
            } else {
                cache.mobile = cache.tablet = impl.FALLBACK_TABLET;
                cache.phone = null;
            }
        } else if (impl.isTabletFallback(userAgent)) {
            cache.mobile = cache.tablet = impl.FALLBACK_TABLET;
            cache.phone = null;
        } else {
            // not mobile at all!
            cache.mobile = cache.tablet = cache.phone = null;
        }
    };

    // t is a reference to a MobileDetect instance
    impl.mobileGrade = function (t) {
        // impl note:
        // To keep in sync w/ Mobile_Detect.php easily, the following code is tightly aligned to the PHP version.
        // When changes are made in Mobile_Detect.php, copy this method and replace:
        //     $this-> / t.
        //     self::MOBILE_GRADE_(.) / '$1'
        //     , self::VERSION_TYPE_FLOAT / (nothing)
        //     isIOS() / os('iOS')
        //     [reg] / (nothing)   <-- jsdelivr complaining about unescaped unicode character U+00AE
        var $isMobile = t.mobile() !== null;

        if (
            // Apple iOS 3.2-5.1 - Tested on the original iPad (4.3 / 5.0), iPad 2 (4.3), iPad 3 (5.1), original iPhone (3.1), iPhone 3 (3.2), 3GS (4.3), 4 (4.3 / 5.0), and 4S (5.1)
            t.os('iOS') && t.version('iPad')>=4.3 ||
            t.os('iOS') && t.version('iPhone')>=3.1 ||
            t.os('iOS') && t.version('iPod')>=3.1 ||

            // Android 2.1-2.3 - Tested on the HTC Incredible (2.2), original Droid (2.2), HTC Aria (2.1), Google Nexus S (2.3). Functional on 1.5 & 1.6 but performance may be sluggish, tested on Google G1 (1.5)
            // Android 3.1 (Honeycomb)  - Tested on the Samsung Galaxy Tab 10.1 and Motorola XOOM
            // Android 4.0 (ICS)  - Tested on a Galaxy Nexus. Note: transition performance can be poor on upgraded devices
            // Android 4.1 (Jelly Bean)  - Tested on a Galaxy Nexus and Galaxy 7
            ( t.version('Android')>2.1 && t.is('Webkit') ) ||

            // Windows Phone 7-7.5 - Tested on the HTC Surround (7.0) HTC Trophy (7.5), LG-E900 (7.5), Nokia Lumia 800
            t.version('Windows Phone OS')>=7.0 ||

            // Blackberry 7 - Tested on BlackBerry Torch 9810
            // Blackberry 6.0 - Tested on the Torch 9800 and Style 9670
            t.is('BlackBerry') && t.version('BlackBerry')>=6.0 ||
            // Blackberry Playbook (1.0-2.0) - Tested on PlayBook
            t.match('Playbook.*Tablet') ||

            // Palm WebOS (1.4-2.0) - Tested on the Palm Pixi (1.4), Pre (1.4), Pre 2 (2.0)
            ( t.version('webOS')>=1.4 && t.match('Palm|Pre|Pixi') ) ||
            // Palm WebOS 3.0  - Tested on HP TouchPad
            t.match('hp.*TouchPad') ||

            // Firefox Mobile (12 Beta) - Tested on Android 2.3 device
            ( t.is('Firefox') && t.version('Firefox')>=12 ) ||

            // Chrome for Android - Tested on Android 4.0, 4.1 device
            ( t.is('Chrome') && t.is('AndroidOS') && t.version('Android')>=4.0 ) ||

            // Skyfire 4.1 - Tested on Android 2.3 device
            ( t.is('Skyfire') && t.version('Skyfire')>=4.1 && t.is('AndroidOS') && t.version('Android')>=2.3 ) ||

            // Opera Mobile 11.5-12: Tested on Android 2.3
            ( t.is('Opera') && t.version('Opera Mobi')>11 && t.is('AndroidOS') ) ||

            // Meego 1.2 - Tested on Nokia 950 and N9
            t.is('MeeGoOS') ||

            // Tizen (pre-release) - Tested on early hardware
            t.is('Tizen') ||

            // Samsung Bada 2.0 - Tested on a Samsung Wave 3, Dolphin browser
            // @todo: more tests here!
            t.is('Dolfin') && t.version('Bada')>=2.0 ||

            // UC Browser - Tested on Android 2.3 device
            ( (t.is('UC Browser') || t.is('Dolfin')) && t.version('Android')>=2.3 ) ||

            // Kindle 3 and Fire  - Tested on the built-in WebKit browser for each
            ( t.match('Kindle Fire') ||
                t.is('Kindle') && t.version('Kindle')>=3.0 ) ||

            // Nook Color 1.4.1 - Tested on original Nook Color, not Nook Tablet
            t.is('AndroidOS') && t.is('NookTablet') ||

            // Chrome Desktop 11-21 - Tested on OS X 10.7 and Windows 7
            t.version('Chrome')>=11 && !$isMobile ||

            // Safari Desktop 4-5 - Tested on OS X 10.7 and Windows 7
            t.version('Safari')>=5.0 && !$isMobile ||

            // Firefox Desktop 4-13 - Tested on OS X 10.7 and Windows 7
            t.version('Firefox')>=4.0 && !$isMobile ||

            // Internet Explorer 7-9 - Tested on Windows XP, Vista and 7
            t.version('MSIE')>=7.0 && !$isMobile ||

            // Opera Desktop 10-12 - Tested on OS X 10.7 and Windows 7
            // @reference: http://my.opera.com/community/openweb/idopera/
            t.version('Opera')>=10 && !$isMobile

            ){
            return 'A';
        }

        if (
            t.os('iOS') && t.version('iPad')<4.3 ||
            t.os('iOS') && t.version('iPhone')<3.1 ||
            t.os('iOS') && t.version('iPod')<3.1 ||

            // Blackberry 5.0: Tested on the Storm 2 9550, Bold 9770
            t.is('Blackberry') && t.version('BlackBerry')>=5 && t.version('BlackBerry')<6 ||

            //Opera Mini (5.0-6.5) - Tested on iOS 3.2/4.3 and Android 2.3
            ( t.version('Opera Mini')>=5.0 && t.version('Opera Mini')<=6.5 &&
                (t.version('Android')>=2.3 || t.is('iOS')) ) ||

            // Nokia Symbian^3 - Tested on Nokia N8 (Symbian^3), C7 (Symbian^3), also works on N97 (Symbian^1)
            t.match('NokiaN8|NokiaC7|N97.*Series60|Symbian/3') ||

            // @todo: report this (tested on Nokia N71)
            t.version('Opera Mobi')>=11 && t.is('SymbianOS')
            ){
            return 'B';
        }

        if (
        // Blackberry 4.x - Tested on the Curve 8330
            t.version('BlackBerry')<5.0 ||
            // Windows Mobile - Tested on the HTC Leo (WinMo 5.2)
            t.match('MSIEMobile|Windows CE.*Mobile') || t.version('Windows Mobile')<=5.2

            ){
            return 'C';
        }

        //All older smartphone platforms and featurephones - Any device that doesn't support media queries
        //will receive the basic, C grade experience.
        return 'C';
    };

    impl.detectOS = function (ua) {
        return impl.findMatch(impl.mobileDetectRules.oss0, ua) ||
            impl.findMatch(impl.mobileDetectRules.oss, ua);
    };

    impl.getDeviceSmallerSide = function () {
        return window.screen.width < window.screen.height ?
            window.screen.width :
            window.screen.height;
    };

    /**
     * Constructor for MobileDetect object.
     * <br>
     * Such an object will keep a reference to the given user-agent string and cache most of the detect queries.<br>
     * <div style="background-color: #d9edf7; border: 1px solid #bce8f1; color: #3a87ad; padding: 14px; border-radius: 2px; margin-top: 20px">
     *     <strong>Find information how to download and install:</strong>
     *     <a href="https://github.com/hgoebl/mobile-detect.js/">github.com/hgoebl/mobile-detect.js/</a>
     * </div>
     *
     * @example <pre>
     *     var md = new MobileDetect(window.navigator.userAgent);
     *     if (md.mobile()) {
     *         location.href = (md.mobileGrade() === 'A') ? '/mobile/' : '/lynx/';
     *     }
     * </pre>
     *
     * @param {string} userAgent typically taken from window.navigator.userAgent or http_header['User-Agent']
     * @param {number} [maxPhoneWidth=600] <strong>only for browsers</strong> specify a value for the maximum
     *        width of smallest device side (in logical "CSS" pixels) until a device detected as mobile will be handled
     *        as phone.
     *        This is only used in cases where the device cannot be classified as phone or tablet.<br>
     *        See <a href="http://developer.android.com/guide/practices/screens_support.html">Declaring Tablet Layouts
     *        for Android</a>.<br>
     *        If you provide a value < 0, then this "fuzzy" check is disabled.
     * @constructor
     * @global
     */
    function MobileDetect(userAgent, maxPhoneWidth) {
        this.ua = userAgent || '';
        this._cache = {};
        //600dp is typical 7" tablet minimum width
        this.maxPhoneWidth = maxPhoneWidth || 600;
    }

    MobileDetect.prototype = {
        constructor: MobileDetect,

        /**
         * Returns the detected phone or tablet type or <tt>null</tt> if it is not a mobile device.
         * <br>
         * For a list of possible return values see {@link MobileDetect#phone} and {@link MobileDetect#tablet}.<br>
         * <br>
         * If the device is not detected by the regular expressions from Mobile-Detect, a test is made against
         * the patterns of <a href="http://detectmobilebrowsers.com/">detectmobilebrowsers.com</a>. If this test
         * is positive, a value of <code>UnknownPhone</code>, <code>UnknownTablet</code> or
         * <code>UnknownMobile</code> is returned.<br>
         * When used in browser, the decision whether phone or tablet is made based on <code>screen.width/height</code>.<br>
         * <br>
         * When used server-side (node.js), there is no way to tell the difference between <code>UnknownTablet</code>
         * and <code>UnknownMobile</code>, so you will get <code>UnknownMobile</code> here.<br>
         * Be aware that since v1.0.0 in this special case you will get <code>UnknownMobile</code> only for:
         * {@link MobileDetect#mobile}, not for {@link MobileDetect#phone} and {@link MobileDetect#tablet}.
         * In versions before v1.0.0 all 3 methods returned <code>UnknownMobile</code> which was tedious to use.
         * <br>
         * In most cases you will use the return value just as a boolean.
         *
         * @returns {String} the key for the phone family or tablet family, e.g. "Nexus".
         * @function MobileDetect#mobile
         */
        mobile: function () {
            impl.prepareDetectionCache(this._cache, this.ua, this.maxPhoneWidth);
            return this._cache.mobile;
        },

        /**
         * Returns the detected phone type/family string or <tt>null</tt>.
         * <br>
         * The returned tablet (family or producer) is one of following keys:<br>
         * <br><tt>iPhone, BlackBerry, HTC, Nexus, Dell, Motorola, Samsung, LG, Sony, Asus,
         * NokiaLumia, Micromax, Palm, Vertu, Pantech, Fly, Wiko, iMobile, SimValley,
         * Wolfgang, Alcatel, Nintendo, Amoi, INQ, GenericPhone</tt><br>
         * <br>
         * If the device is not detected by the regular expressions from Mobile-Detect, a test is made against
         * the patterns of <a href="http://detectmobilebrowsers.com/">detectmobilebrowsers.com</a>. If this test
         * is positive, a value of <code>UnknownPhone</code> or <code>UnknownMobile</code> is returned.<br>
         * When used in browser, the decision whether phone or tablet is made based on <code>screen.width/height</code>.<br>
         * <br>
         * When used server-side (node.js), there is no way to tell the difference between <code>UnknownTablet</code>
         * and <code>UnknownMobile</code>, so you will get <code>null</code> here, while {@link MobileDetect#mobile}
         * will return <code>UnknownMobile</code>.<br>
         * Be aware that since v1.0.0 in this special case you will get <code>UnknownMobile</code> only for:
         * {@link MobileDetect#mobile}, not for {@link MobileDetect#phone} and {@link MobileDetect#tablet}.
         * In versions before v1.0.0 all 3 methods returned <code>UnknownMobile</code> which was tedious to use.
         * <br>
         * In most cases you will use the return value just as a boolean.
         *
         * @returns {String} the key of the phone family or producer, e.g. "iPhone"
         * @function MobileDetect#phone
         */
        phone: function () {
            impl.prepareDetectionCache(this._cache, this.ua, this.maxPhoneWidth);
            return this._cache.phone;
        },

        /**
         * Returns the detected tablet type/family string or <tt>null</tt>.
         * <br>
         * The returned tablet (family or producer) is one of following keys:<br>
         * <br><tt>iPad, NexusTablet, SamsungTablet, Kindle, SurfaceTablet, HPTablet, AsusTablet,
         * BlackBerryTablet, HTCtablet, MotorolaTablet, NookTablet, AcerTablet,
         * ToshibaTablet, LGTablet, FujitsuTablet, PrestigioTablet, LenovoTablet,
         * DellTablet, YarvikTablet, MedionTablet, ArnovaTablet, IntensoTablet, IRUTablet,
         * MegafonTablet, EbodaTablet, AllViewTablet, ArchosTablet, AinolTablet,
         * NokiaLumiaTablet, SonyTablet, PhilipsTablet, CubeTablet, CobyTablet, MIDTablet,
         * MSITablet, SMiTTablet, RockChipTablet, FlyTablet, bqTablet, HuaweiTablet,
         * NecTablet, PantechTablet, BronchoTablet, VersusTablet, ZyncTablet,
         * PositivoTablet, NabiTablet, KoboTablet, DanewTablet, TexetTablet,
         * PlaystationTablet, TrekstorTablet, PyleAudioTablet, AdvanTablet,
         * DanyTechTablet, GalapadTablet, MicromaxTablet, KarbonnTablet, AllFineTablet,
         * PROSCANTablet, YONESTablet, ChangJiaTablet, GUTablet, PointOfViewTablet,
         * OvermaxTablet, HCLTablet, DPSTablet, VistureTablet, CrestaTablet,
         * MediatekTablet, ConcordeTablet, GoCleverTablet, ModecomTablet, VoninoTablet,
         * ECSTablet, StorexTablet, VodafoneTablet, EssentielBTablet, RossMoorTablet,
         * iMobileTablet, TolinoTablet, AudioSonicTablet, AMPETablet, SkkTablet,
         * TecnoTablet, JXDTablet, iJoyTablet, FX2Tablet, XoroTablet, ViewsonicTablet,
         * OdysTablet, CaptivaTablet, IconbitTablet, TeclastTablet, OndaTablet,
         * JaytechTablet, BlaupunktTablet, DigmaTablet, EvolioTablet, LavaTablet,
         * AocTablet, MpmanTablet, CelkonTablet, WolderTablet, MiTablet, NibiruTablet,
         * NexoTablet, LeaderTablet, UbislateTablet, PocketBookTablet, KocasoTablet,
         * HisenseTablet, Hudl, TelstraTablet, GenericTablet</tt><br>
         * <br>
         * If the device is not detected by the regular expressions from Mobile-Detect, a test is made against
         * the patterns of <a href="http://detectmobilebrowsers.com/">detectmobilebrowsers.com</a>. If this test
         * is positive, a value of <code>UnknownTablet</code> or <code>UnknownMobile</code> is returned.<br>
         * When used in browser, the decision whether phone or tablet is made based on <code>screen.width/height</code>.<br>
         * <br>
         * When used server-side (node.js), there is no way to tell the difference between <code>UnknownTablet</code>
         * and <code>UnknownMobile</code>, so you will get <code>null</code> here, while {@link MobileDetect#mobile}
         * will return <code>UnknownMobile</code>.<br>
         * Be aware that since v1.0.0 in this special case you will get <code>UnknownMobile</code> only for:
         * {@link MobileDetect#mobile}, not for {@link MobileDetect#phone} and {@link MobileDetect#tablet}.
         * In versions before v1.0.0 all 3 methods returned <code>UnknownMobile</code> which was tedious to use.
         * <br>
         * In most cases you will use the return value just as a boolean.
         *
         * @returns {String} the key of the tablet family or producer, e.g. "SamsungTablet"
         * @function MobileDetect#tablet
         */
        tablet: function () {
            impl.prepareDetectionCache(this._cache, this.ua, this.maxPhoneWidth);
            return this._cache.tablet;
        },

        /**
         * Returns the (first) detected user-agent string or <tt>null</tt>.
         * <br>
         * The returned user-agent is one of following keys:<br>
         * <br><tt>Chrome, Dolfin, Opera, Skyfire, Edge, IE, Firefox, Bolt, TeaShark, Blazer,
         * Safari, UCBrowser, baiduboxapp, baidubrowser, DiigoBrowser, Puffin, Mercury,
         * ObigoBrowser, NetFront, GenericBrowser, PaleMoon</tt><br>
         * <br>
         * In most cases calling {@link MobileDetect#userAgent} will be sufficient. But there are rare
         * cases where a mobile device pretends to be more than one particular browser. You can get the
         * list of all matches with {@link MobileDetect#userAgents} or check for a particular value by
         * providing one of the defined keys as first argument to {@link MobileDetect#is}.
         *
         * @returns {String} the key for the detected user-agent or <tt>null</tt>
         * @function MobileDetect#userAgent
         */
        userAgent: function () {
            if (this._cache.userAgent === undefined) {
                this._cache.userAgent = impl.findMatch(impl.mobileDetectRules.uas, this.ua);
            }
            return this._cache.userAgent;
        },

        /**
         * Returns all detected user-agent strings.
         * <br>
         * The array is empty or contains one or more of following keys:<br>
         * <br><tt>Chrome, Dolfin, Opera, Skyfire, Edge, IE, Firefox, Bolt, TeaShark, Blazer,
         * Safari, UCBrowser, baiduboxapp, baidubrowser, DiigoBrowser, Puffin, Mercury,
         * ObigoBrowser, NetFront, GenericBrowser, PaleMoon</tt><br>
         * <br>
         * In most cases calling {@link MobileDetect#userAgent} will be sufficient. But there are rare
         * cases where a mobile device pretends to be more than one particular browser. You can get the
         * list of all matches with {@link MobileDetect#userAgents} or check for a particular value by
         * providing one of the defined keys as first argument to {@link MobileDetect#is}.
         *
         * @returns {Array} the array of detected user-agent keys or <tt>[]</tt>
         * @function MobileDetect#userAgents
         */
        userAgents: function () {
            if (this._cache.userAgents === undefined) {
                this._cache.userAgents = impl.findMatches(impl.mobileDetectRules.uas, this.ua);
            }
            return this._cache.userAgents;
        },

        /**
         * Returns the detected operating system string or <tt>null</tt>.
         * <br>
         * The operating system is one of following keys:<br>
         * <br><tt>AndroidOS, BlackBerryOS, PalmOS, SymbianOS, WindowsMobileOS, WindowsPhoneOS,
         * iOS, MeeGoOS, MaemoOS, JavaOS, webOS, badaOS, BREWOS</tt><br>
         *
         * @returns {String} the key for the detected operating system.
         * @function MobileDetect#os
         */
        os: function () {
            if (this._cache.os === undefined) {
                this._cache.os = impl.detectOS(this.ua);
            }
            return this._cache.os;
        },

        /**
         * Get the version (as Number) of the given property in the User-Agent.
         * <br>
         * Will return a float number. (eg. 2_0 will return 2.0, 4.3.1 will return 4.31)
         *
         * @param {String} key a key defining a thing which has a version.<br>
         *        You can use one of following keys:<br>
         * <br><tt>Mobile, Build, Version, VendorID, iPad, iPhone, iPod, Kindle, Chrome, Coast,
         * Dolfin, Firefox, Fennec, Edge, IE, NetFront, NokiaBrowser, Opera, Opera Mini,
         * Opera Mobi, UC Browser, MQQBrowser, MicroMessenger, baiduboxapp, baidubrowser,
         * SamsungBrowser, Iron, Safari, Skyfire, Tizen, Webkit, PaleMoon, Gecko, Trident,
         * Presto, Goanna, iOS, Android, BlackBerry, BREW, Java, Windows Phone OS, Windows
         * Phone, Windows CE, Windows NT, Symbian, webOS</tt><br>
         *
         * @returns {Number} the version as float or <tt>NaN</tt> if User-Agent doesn't contain this version.
         *          Be careful when comparing this value with '==' operator!
         * @function MobileDetect#version
         */
        version: function (key) {
            return impl.getVersion(key, this.ua);
        },

        /**
         * Get the version (as String) of the given property in the User-Agent.
         * <br>
         *
         * @param {String} key a key defining a thing which has a version.<br>
         *        You can use one of following keys:<br>
         * <br><tt>Mobile, Build, Version, VendorID, iPad, iPhone, iPod, Kindle, Chrome, Coast,
         * Dolfin, Firefox, Fennec, Edge, IE, NetFront, NokiaBrowser, Opera, Opera Mini,
         * Opera Mobi, UC Browser, MQQBrowser, MicroMessenger, baiduboxapp, baidubrowser,
         * SamsungBrowser, Iron, Safari, Skyfire, Tizen, Webkit, PaleMoon, Gecko, Trident,
         * Presto, Goanna, iOS, Android, BlackBerry, BREW, Java, Windows Phone OS, Windows
         * Phone, Windows CE, Windows NT, Symbian, webOS</tt><br>
         *
         * @returns {String} the "raw" version as String or <tt>null</tt> if User-Agent doesn't contain this version.
         *
         * @function MobileDetect#versionStr
         */
        versionStr: function (key) {
            return impl.getVersionStr(key, this.ua);
        },

        /**
         * Global test key against userAgent, os, phone, tablet and some other properties of userAgent string.
         *
         * @param {String} key the key (case-insensitive) of a userAgent, an operating system, phone or
         *        tablet family.<br>
         *        For a complete list of possible values, see {@link MobileDetect#userAgent},
         *        {@link MobileDetect#os}, {@link MobileDetect#phone}, {@link MobileDetect#tablet}.<br>
         *        Additionally you have following keys:<br>
         * <br><tt>Bot, MobileBot, DesktopMode, TV, WebKit, Console, Watch</tt><br>
         *
         * @returns {boolean} <tt>true</tt> when the given key is one of the defined keys of userAgent, os, phone,
         *                    tablet or one of the listed additional keys, otherwise <tt>false</tt>
         * @function MobileDetect#is
         */
        is: function (key) {
            return containsIC(this.userAgents(), key) ||
                   equalIC(key, this.os()) ||
                   equalIC(key, this.phone()) ||
                   equalIC(key, this.tablet()) ||
                   containsIC(impl.findMatches(impl.mobileDetectRules.utils, this.ua), key);
        },

        /**
         * Do a quick test against navigator::userAgent.
         *
         * @param {String|RegExp} pattern the pattern, either as String or RegExp
         *                        (a string will be converted to a case-insensitive RegExp).
         * @returns {boolean} <tt>true</tt> when the pattern matches, otherwise <tt>false</tt>
         * @function MobileDetect#match
         */
        match: function (pattern) {
            if (!(pattern instanceof RegExp)) {
                pattern = new RegExp(pattern, 'i');
            }
            return pattern.test(this.ua);
        },

        /**
         * Checks whether the mobile device can be considered as phone regarding <code>screen.width</code>.
         * <br>
         * Obviously this method makes sense in browser environments only (not for Node.js)!
         * @param {number} [maxPhoneWidth] the maximum logical pixels (aka. CSS-pixels) to be considered as phone.<br>
         *        The argument is optional and if not present or falsy, the value of the constructor is taken.
         * @returns {boolean|undefined} <code>undefined</code> if screen size wasn't detectable, else <code>true</code>
         *          when screen.width is less or equal to maxPhoneWidth, otherwise <code>false</code>.<br>
         *          Will always return <code>undefined</code> server-side.
         */
        isPhoneSized: function (maxPhoneWidth) {
            return MobileDetect.isPhoneSized(maxPhoneWidth || this.maxPhoneWidth);
        },

        /**
         * Returns the mobile grade ('A', 'B', 'C').
         *
         * @returns {String} one of the mobile grades ('A', 'B', 'C').
         * @function MobileDetect#mobileGrade
         */
        mobileGrade: function () {
            if (this._cache.grade === undefined) {
                this._cache.grade = impl.mobileGrade(this);
            }
            return this._cache.grade;
        }
    };

    // environment-dependent
    if (typeof window !== 'undefined' && window.screen) {
        MobileDetect.isPhoneSized = function (maxPhoneWidth) {
            return maxPhoneWidth < 0 ? undefined : impl.getDeviceSmallerSide() <= maxPhoneWidth;
        };
    } else {
        MobileDetect.isPhoneSized = function () {};
    }

    // should not be replaced by a completely new object - just overwrite existing methods
    MobileDetect._impl = impl;

    MobileDetect.version = '1.3.5 2016-11-14';

    window.MobileDetect = MobileDetect;
})();


/* -- responsive-images.js -- */

$(document).ready(function () {
    // Show appropiately sized images depending on the type of browser visiting
    function getScreenDesc() {
        if (window.screen.width <= 480) {
            return 'martini-mobile-src';
        } else if (window.screen.width > 480 && window.screen.width < 992) {
            return 'martini-tablet-src';
        }
        return 'martini-desktop-src';
    }

    function getMinHeight(el) {
        var dAttr = '';
        if (window.screen.width <= 480) {
            dAttr = 'mobile_height';
        } else {
            dAttr = 'desktop_height';
        }

        var minHeight = $(el).data(dAttr);

        return minHeight;
    }

    // grab all the <img /> tags
    $('img').each(function (i, e) {
        var srcArg = getScreenDesc();
        var src = $(e).attr(srcArg);

        var minHeight = getMinHeight(e);
        if (minHeight > 0) {
            $(e).attr('style', 'min-height: ' + minHeight);
        }

        // some we don't do
        if (typeof src === 'undefined') {
            src = $(e).attr('martini-desktop-src');
            // some we don't do
            if (typeof src === 'undefined') {
                return;
            }
        }
        if ($(e).hasClass('lazy')) {
            // NB, bypass jquery doing it in memory rather than writing it to the dom properly
            e.setAttribute('data-src', src);
        } else {
            $(e).attr('src', src).show();
        }
    });


    // fotorama uses <a and not <img, so we add a class and set it up that way.
    $('a.responsive-image').each(function (i, e) {
        var srcArg = getScreenDesc();
        var src = $(e).attr(srcArg);

        // some we don't do
        if (typeof src === 'undefined') {
            return;
        }
        $(e).attr('href', src);
        $(e).attr('data-full', src);
    });
});




/* -- site-chrome.js -- */

// top navigation bar functions
$(document).ready(function () {

    $('.gdpr-option').click(function (e) {
        e.preventDefault();
        var i = $(this).data('id');
        var pmid = '5b1a668adcffaa35696f34d9';
        window._sp_.privacyManager.loadPrivacyManagerModal(i, pmid);
    });


    window.addEventListener('message', function (message) {

        var msgData = message.data;
        if (!msgData) {
            return;
        }

        switch(msgData.action) {
            case 'sp.complete':
            case 'sp.choiceComplete':
            case 'sp.cancel':
                $('#sp-modal').modal('hide');
        }
    });

    //Handle responsive social media dropdowns
    $('.social-dropdown').on('mouseenter', function(){
        var dd = $(this).find('.social-feed');
        dd.fadeIn();
    });

    $('.social-dropdown').on('mouseleave', function(){
        var dd = $(this).find('.social-feed');
        dd.delay(200).fadeOut();
    });
    function findNavTop() {
        current = $('#mainNav').data('top');
        navTop = $('#mainNav').position().top;
        if(current != navTop) {
            $('#mainNav').data('top', navTop);
            window.setTimeout(findNavTop, 1000);
        } else {
            $('#mainNav').data('pos', 1);
        }
    }
    if ($('#mainNav').length > 0) {
        findNavTop();
    }

    var topLevel = window.location.pathname.split('/')[1];
    /*var container = $('#dropMenu .'+topLevel).addClass('tint');
    if(container) {
        $('#dropMenu').prepend(container);
    }*/

    if (topLevel.toString().length > 0) {
        $('#dropMenu .'+topLevel).remove();
    }


    $('.slide-control').on('click', function(e){
        e.preventDefault();
        // control the sub navigation wrapping
        $('#subNav').toggleClass('nav-open');

        var el = $($(this).data('slide-pane'));
        $(el).toggle(0, "linear", function(){
            if($(this).css('display') === 'none') { //Nav is to be closed

                // if the mnobile search is open and we are closing the block, we can toggle the close Function
                // so that everything is closed properly
                if ($('.mobile-search').hasClass('search-open')) {
                    $('.display-search').click();
                }

                // hide the navigation
                // $('#mainNav').removeClass('menu-open').removeAttr('style').removeData('last');
                $('#mainNav').removeClass('menu-open');
                $('.slide-control span').removeClass('fa-angle-up').addClass('fa-angle-down');
            } else { //Nav is be opened
                // if($(window).scrollTop() > $('#mainNav').position().top) {
                //     $('#mainNav').css({
                //         'position': 'absolute',
                //         'top': $(window).scrollTop(),
                //         'width': $('.page-wrapper').outerWidth(),
                //         'z-index': 9999
                //     }).data('last', $(window).scrollTop());
                // }
                $('#mainNav').addClass('menu-open');
                $('.slide-control span').removeClass('fa-angle-down').addClass('fa-angle-up');
            }
        });
    });
    $('.close-menu button').on('click', function(){ //Close button at the  bottom of the nav has been clicked
        $('div.menu.home').toggle(0, "swing", function(){
            // $('#mainNav').removeClass('menu-open').removeAttr('style').removeData('last');
            $('#mainNav').removeClass('menu-open');
            $('#subNav').removeClass('nav-open');
            $('.slide-control span').removeClass('fa-angle-up').addClass('fa-angle-down');
        });
    });
    $('.display-search').click(function(e) {
        e.preventDefault();
        $(this).parent().parent().toggleClass('show-mobile-search');
        var el = $(this).children().children();
        el.toggleClass('fa-search fa-times');

        $('.mobile-search').slideToggle('fast').toggleClass('search-open');
    });
    // var lastScroll = 0;
    // var primaryNavHeight = $('#primaryNav').outerHeight();
    // $(window).scroll(function(e) {
    //     if($('#mainNav').data('pos') === 1) {
    //         scroll = $(window).scrollTop();
    //         navTop = $('#mainNav').data('top');
    //         //if(scroll > navTop && scroll < lastScroll) {
    //
    //         if(scroll < $('#mainNav').data('last')) {
    //             newTop = $(window).scrollTop();
    //             $('#mainNav').css('top', newTop).data('last', newTop);
    //         }
    //
    //         if(scroll > navTop) {
    //             $('#mainNav').addClass('float');
    //             $('#contentContainer').css('padding-top', primaryNavHeight);
    //         } else {
    //             $('#mainNav').removeClass('float').removeAttr('style').removeData('last');
    //             $('#contentContainer').attr('style', '');
    //         }
    //     }
    //     lastScroll = scroll;
    // });
    function displayCookieNotification() {
        // check if they have seen this warning already
        if ($.cookie('nqr-eu') == 'warned') {
            return;
        }
        $('#cookieWarning').animate({'right':'0px'},300);

        $('#cookieWarning button').click(function (e) {
            e.preventDefault();
            $('#cookieWarning').stop(true).animate({'right':'-310px'},100);
            $('#cookieWarning').remove();
        });

        $('#pecr-cookie-warning').animate({'bottom':'0px'},300);

        $('#pecr-cookie-warning button').click(function (e) {
            e.preventDefault();
            $('#pecr-cookie-warning').stop(true).animate({'bottom':'-900px'},100);
            $('#pecr-cookie-warning').remove();
        });


        // set a cookie saying it's been showed.
        console.log("GDPR: Cookie in 5 seconds");
        window.gdprfirstrequest = true;
        $.cookie('nqr-eu', 'warned', {path: '/', expires: 2000});
    }
    window.gdprfirstrequest = false;
    window.setTimeout(displayCookieNotification(), 5000);
    // Logs out the user.
    $('#log-out, #mobileSignOut').on('click', function(e) {
        e.preventDefault();
        var data = {
            domain: data_layer.site.cookie_domain,
            expires: -1,
            path: '/'
        };

        // For local testing.
        if (window.location.href.split('.').length < 2) {
            data.domain = null;
        }

        $.cookie('masq', '', data);
        $.cookie('nqdm', '', data);
        $.cookie('nqjwt', '', data);
        $.cookie('nql', '', data);
        document.location = '/';
    });

    // non meganav stuff
    // To be deleted
    // top navigation bar functions
    $('.dd-container button').on('click', function(e){
        e.preventDefault();

        // close any open dds
        $('.dd-container .dd:visible').not(this).hide(400, function () {
            $(this).parent().find('button').removeClass('active');
        });

        var dd = $(this).parent('.dd-container').find('.dd');

        if( $(this).hasClass('active') ) {
            $(this).removeClass('active');
            dd.fadeOut();
        } else {
            $(this).addClass('active');
            dd.fadeIn();
        }
    });

    $('span.dd-container').on('mouseenter', function(){
        var dd = $(this).find('.dd');
        dd.fadeIn();
    });

    $('span.dd-container').on('mouseleave', function(){
        var dd = $(this).find('.dd');
        dd.delay(200).fadeOut();
    });

    $('.dd form button').click(function (e) {
        $(this).parents('form').submit();
    });
    $('.dd form').click(function (e) {
        e.stopPropagation();
    });
    // show/hide search form
    $('#btnSearchToggle').on('click', function(event){
        event.preventDefault();
        $('#divSearchBox').toggleClass('on');
    });

    // mobile nav button
    $('#btnMobileNavToggle').on('click', function(event){
        event.preventDefault();
        $('#navMobile').toggleClass('on');
    });

    $('#toggleAdminLinks').click(function(e) {
        e.preventDefault();
        if ($(this).text().indexOf('Hide') >= 0) {
            $(this).text($(this).text().replace('Hide', 'Show'));
        } else {
            $(this).text($(this).text().replace('Show', 'Hide'));
        }
        $('.admin-links').toggle();
    });

    // multimedia gallery
    $('.panel-thumbnails li').click(function(e) {

           e.preventDefault();

           var index = $(this).data('id');

           $('.panel-thumbnails li').removeClass('current');

           $(this).addClass('current');

           $('.panel-photo li').removeClass('current');

           $('#pic' + index).addClass('current');
       });

    // $(".admin-links-v2").each(function( index ) {
    //    var items = $(this).find('.circle a');
    //    var l = items.length;
    //
    //    $(this).find('.circle a').each(function( i ) {
    //        var left = (50 - 25*Math.cos(-0.5 * Math.PI - 2*(1/l)*i*Math.PI)).toFixed(4) + "%";
    //        var top = (50 + 25*Math.sin(-0.5 * Math.PI - 2*(1/l)*i*Math.PI)).toFixed(4) + "%";
    //
    //        $(this).css({"left": left, "top": top});
    //    });
    // });
    //
    // $(".admin-links-v2").on("click", ".menu-button", function(e) {
    //    e.preventDefault();
    //    $(this).prev('.circle').toggleClass("open");
    // });
});


/* -- synchronous.js -- */

/******/ (function(modules) { // webpackBootstrap
/******/ 	// The module cache
/******/ 	var installedModules = {};
/******/
/******/ 	// The require function
/******/ 	function __webpack_require__(moduleId) {
/******/
/******/ 		// Check if module is in cache
/******/ 		if(installedModules[moduleId]) {
/******/ 			return installedModules[moduleId].exports;
/******/ 		}
/******/ 		// Create a new module (and put it into the cache)
/******/ 		var module = installedModules[moduleId] = {
/******/ 			i: moduleId,
/******/ 			l: false,
/******/ 			exports: {}
/******/ 		};
/******/
/******/ 		// Execute the module function
/******/ 		modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ 		// Flag the module as loaded
/******/ 		module.l = true;
/******/
/******/ 		// Return the exports of the module
/******/ 		return module.exports;
/******/ 	}
/******/
/******/
/******/ 	// expose the modules object (__webpack_modules__)
/******/ 	__webpack_require__.m = modules;
/******/
/******/ 	// expose the module cache
/******/ 	__webpack_require__.c = installedModules;
/******/
/******/ 	// define getter function for harmony exports
/******/ 	__webpack_require__.d = function(exports, name, getter) {
/******/ 		if(!__webpack_require__.o(exports, name)) {
/******/ 			Object.defineProperty(exports, name, { enumerable: true, get: getter });
/******/ 		}
/******/ 	};
/******/
/******/ 	// define __esModule on exports
/******/ 	__webpack_require__.r = function(exports) {
/******/ 		if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
/******/ 			Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
/******/ 		}
/******/ 		Object.defineProperty(exports, '__esModule', { value: true });
/******/ 	};
/******/
/******/ 	// create a fake namespace object
/******/ 	// mode & 1: value is a module id, require it
/******/ 	// mode & 2: merge all properties of value into the ns
/******/ 	// mode & 4: return value when already ns object
/******/ 	// mode & 8|1: behave like require
/******/ 	__webpack_require__.t = function(value, mode) {
/******/ 		if(mode & 1) value = __webpack_require__(value);
/******/ 		if(mode & 8) return value;
/******/ 		if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
/******/ 		var ns = Object.create(null);
/******/ 		__webpack_require__.r(ns);
/******/ 		Object.defineProperty(ns, 'default', { enumerable: true, value: value });
/******/ 		if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
/******/ 		return ns;
/******/ 	};
/******/
/******/ 	// getDefaultExport function for compatibility with non-harmony modules
/******/ 	__webpack_require__.n = function(module) {
/******/ 		var getter = module && module.__esModule ?
/******/ 			function getDefault() { return module['default']; } :
/******/ 			function getModuleExports() { return module; };
/******/ 		__webpack_require__.d(getter, 'a', getter);
/******/ 		return getter;
/******/ 	};
/******/
/******/ 	// Object.prototype.hasOwnProperty.call
/******/ 	__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ 	// __webpack_public_path__
/******/ 	__webpack_require__.p = "";
/******/
/******/
/******/ 	// Load entry module and return exports
/******/ 	return __webpack_require__(__webpack_require__.s = "./es6/blocks/Synchronous.js");
/******/ })
/************************************************************************/
/******/ ({

/***/ "./es6/blocks/Synchronous.js":
/*!***********************************!*\
  !*** ./es6/blocks/Synchronous.js ***!
  \***********************************/
/*! no exports provided */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _lib_synchronous_AdvertisingRefresh__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../lib/synchronous/AdvertisingRefresh */ \"./es6/lib/synchronous/AdvertisingRefresh.js\");\n/* harmony import */ var _lib_synchronous_AdvertisingRefresh__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_lib_synchronous_AdvertisingRefresh__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var _lib_synchronous_JWPlayer__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../lib/synchronous/JWPlayer */ \"./es6/lib/synchronous/JWPlayer.js\");\n/* harmony import */ var _lib_synchronous_JWPlayer__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_lib_synchronous_JWPlayer__WEBPACK_IMPORTED_MODULE_1__);\n/* harmony import */ var _lib_synchronous_PianoHelper__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../lib/synchronous/PianoHelper */ \"./es6/lib/synchronous/PianoHelper.js\");\n/* harmony import */ var _lib_synchronous_PianoHelper__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(_lib_synchronous_PianoHelper__WEBPACK_IMPORTED_MODULE_2__);\n/* harmony import */ var _lib_synchronous_PianoListener__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../lib/synchronous/PianoListener */ \"./es6/lib/synchronous/PianoListener.js\");\n/* harmony import */ var _lib_synchronous_PianoListener__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(_lib_synchronous_PianoListener__WEBPACK_IMPORTED_MODULE_3__);\n/* harmony import */ var _lib_synchronous_DataLayerHelper__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../lib/synchronous/DataLayerHelper */ \"./es6/lib/synchronous/DataLayerHelper.js\");\n/* harmony import */ var _lib_synchronous_DataLayerHelper__WEBPACK_IMPORTED_MODULE_4___default = /*#__PURE__*/__webpack_require__.n(_lib_synchronous_DataLayerHelper__WEBPACK_IMPORTED_MODULE_4__);\n/* harmony import */ var _lib_EarPiece__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../lib/EarPiece */ \"./es6/lib/EarPiece.js\");\n/* harmony import */ var _lib_EarPiece__WEBPACK_IMPORTED_MODULE_5___default = /*#__PURE__*/__webpack_require__.n(_lib_EarPiece__WEBPACK_IMPORTED_MODULE_5__);\n\n\n\n\n\n\nwindow.advertisingTimer = _lib_synchronous_AdvertisingRefresh__WEBPACK_IMPORTED_MODULE_0___default.a;\nwindow.jwVideo = _lib_synchronous_JWPlayer__WEBPACK_IMPORTED_MODULE_1___default.a;\nwindow.data_layer_helper = _lib_synchronous_DataLayerHelper__WEBPACK_IMPORTED_MODULE_4___default.a;\n_lib_synchronous_PianoHelper__WEBPACK_IMPORTED_MODULE_2___default.a.init();\n_lib_synchronous_PianoListener__WEBPACK_IMPORTED_MODULE_3___default.a.init();\n\n//# sourceURL=webpack:///./es6/blocks/Synchronous.js?");

/***/ }),

/***/ "./es6/lib/EarPiece.js":
/*!*****************************!*\
  !*** ./es6/lib/EarPiece.js ***!
  \*****************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

"use strict";
eval("\n\nvar _interopRequireDefault = __webpack_require__(/*! @babel/runtime/helpers/interopRequireDefault */ \"./node_modules/@babel/runtime/helpers/interopRequireDefault.js\");\n\nvar _typeof2 = _interopRequireDefault(__webpack_require__(/*! @babel/runtime/helpers/typeof */ \"./node_modules/@babel/runtime/helpers/typeof.js\")); // Symlinked from synchronous\n\n\n$(document).ready(function () {\n  var promoUnits = window.promoUnits || {};\n  var promoListing = {};\n  var screenWidth = $(window).width;\n  var promoUnitMode = 'desktop';\n\n  if (screenWidth < 768) {\n    promoUnitMode = 'mobile';\n  } else if (screenWidth < 992) {\n    promoUnitMode = 'tablet';\n  }\n\n  var getRandom = function getRandom(max) {\n    return Math.floor(Math.random() * max) + 1;\n  };\n\n  $.each(promoUnits, function (index, value) {\n    if (value[promoUnitMode] === 0) {\n      // if it doesn't apply to this mode, then discard\n      return;\n    }\n\n    $.each(value.position.split(','), function (vIndex, location) {\n      if ((0, _typeof2.default)(promoListing[location]) !== 'object') {\n        promoListing[location] = [];\n      }\n\n      var i = 1;\n\n      while (i <= value.boost * 10) {\n        promoListing[location].push(value);\n        i += 1;\n      }\n    });\n  });\n  $.each(promoListing, function (location) {\n    var pIndex = getRandom(promoListing[location].length);\n    pIndex -= 1;\n    var chosenPromoListing = promoListing[location][pIndex];\n    $(\"#\".concat(location)).html(\"<a href=\\\"\".concat(chosenPromoListing.destination, \"\\\" target=\\\"\").concat(chosenPromoListing.target, \"\\\">\\n                   <img src=\\\"/resources/images/\").concat(chosenPromoListing.image_id, \"/\\\" class=\\\"img-fluid promo-unit\\\" alt=\\\"\\\"></img>\"));\n  });\n});\n\n//# sourceURL=webpack:///./es6/lib/EarPiece.js?");

/***/ }),

/***/ "./es6/lib/PseudoConsole.js":
/*!**********************************!*\
  !*** ./es6/lib/PseudoConsole.js ***!
  \**********************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

"use strict";
eval("\n\nObject.defineProperty(exports, \"__esModule\", {\n  value: true\n});\nexports.default = void 0;\n/**\n * Shows a console.log only if a user is running in debug mode\n */\n\nvar _default = {\n  log: function log() {\n    if (parseInt(this.getCookie('debug'), 10) === 1) {\n      var _console;\n\n      var css = ['color: #fff', 'font-weight: 800', 'background-color: #976F2A', 'font-size: 120%', 'padding: 2px 0 2px 2px'].join('; ');\n      var reset = ['color: black', 'font-weight: normal', 'background-color: white', 'font-size: 100%', 'padding: 0'].join('; ');\n\n      for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {\n        args[_key] = arguments[_key];\n      }\n\n      (_console = console).log.apply(_console, ['%cMartini%c', css, reset].concat(args)); // eslint-disable-line\n\n    }\n  },\n  getCookie: function getCookie(v) {\n    var name = \"\".concat(v, \"=\");\n    var ca = document.cookie.split(';');\n\n    for (var i = 0; i < ca.length; i += 1) {\n      var c = ca[i];\n\n      while (c.charAt(0) === ' ') {\n        c = c.substring(1);\n      }\n\n      if (c.indexOf(name) === 0) {\n        return c.substring(name.length, c.length);\n      }\n    }\n\n    return '';\n  }\n};\nexports.default = _default;\n\n//# sourceURL=webpack:///./es6/lib/PseudoConsole.js?");

/***/ }),

/***/ "./es6/lib/synchronous/AdvertisingRefresh.js":
/*!***************************************************!*\
  !*** ./es6/lib/synchronous/AdvertisingRefresh.js ***!
  \***************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

"use strict";
eval("\n\nvar _interopRequireDefault = __webpack_require__(/*! @babel/runtime/helpers/interopRequireDefault */ \"./node_modules/@babel/runtime/helpers/interopRequireDefault.js\");\n\nObject.defineProperty(exports, \"__esModule\", {\n  value: true\n});\nexports.default = void 0;\n\nvar _PseudoConsole = _interopRequireDefault(__webpack_require__(/*! ../../lib/PseudoConsole */ \"./es6/lib/PseudoConsole.js\"));\n\nvar _default = {\n  runningTimers: {},\n  timerPositions: {},\n  timerCount: {},\n  advertRefreshTime: 16,\n  acceptableViewability: 50,\n  browserFocus: true,\n  browserSetup: false,\n  timer: function timer(name, viewability) {\n    if (!this.browserSetup) {\n      this.browserSetup = true;\n      this.setFocusListeners();\n    }\n\n    if (!this.checkName(name)) {\n      _PseudoConsole.default.log(\"DFP - Ignoring \".concat(name));\n\n      return;\n    }\n\n    if (!this.checkFocus()) {\n      _PseudoConsole.default.log(\"DFP - Browser is not focused, ignoring \".concat(name));\n\n      return;\n    }\n\n    _PseudoConsole.default.log(\"DFP - running for \".concat(name, \" at \").concat(viewability, \" percent\"));\n\n    if (viewability >= this.acceptableViewability) {\n      this.startTimer(name);\n    } else {\n      this.pauseTimer(name);\n    }\n  },\n  pauseTimer: function pauseTimer(name) {\n    // While this stops a timer, as we keep the position in memory, when we re-start it, we act\n    // as if paused.\n    _PseudoConsole.default.log(\"DFP - pausing timer for \".concat(name));\n\n    clearTimeout(this.runningTimers[name]); // set to a null not an int so we can restart it.\n\n    this.runningTimers[name] = null;\n  },\n  startTimer: function startTimer(name) {\n    var _this = this; // set some default entries\n\n\n    if (!(name in this.timerPositions)) {\n      this.timerPositions[name] = 0;\n    }\n\n    if (!(name in this.runningTimers)) {\n      this.runningTimers[name] = null;\n    }\n\n    if (!(name in this.timerCount)) {\n      this.timerCount[name] = 1;\n    } // when an advert re-loads it instantly sets the viewability to 100 triggering\n    // the timer script to re-run.  We need to see if we have a running system\n    // and not start a fresh one,\n\n\n    if (typeof this.runningTimers[name] === 'number') {\n      _PseudoConsole.default.log(\"DFP - Timer for \".concat(name, \" already running\"));\n\n      return;\n    }\n\n    _PseudoConsole.default.log(\"DFP - (re)starting a timer for \".concat(name)); // call back in a second\n\n\n    this.runningTimers[name] = window.setTimeout(function () {\n      _this.updateTimer(name);\n    }, 1000);\n  },\n  updateTimer: function updateTimer(name) {\n    var _this2 = this;\n\n    if (!this.checkFocus()) {\n      _PseudoConsole.default.log(\"DFP - Browser is not focused, pausing timer for \".concat(name)); // Pause the running times so that we retain the current state and we can\n      // continue when the browser is focused and has triggered a new event\n\n\n      this.pauseTimer(name);\n      return;\n    }\n\n    if (!this.checkRefresh(name)) {\n      _PseudoConsole.default.log(\"DFP - advert has no-refresh dom attribute, pausing timer for \".concat(name)); // Pause the running times so that we retain the current state and we can\n      // continue when the browser is focused and has triggered a new event\n\n\n      this.pauseTimer(name);\n      return;\n    } // get the current position (which is the seconds it has been running)\n\n\n    var position = this.timerPositions[name]; // increase it\n\n    position += 1;\n\n    _PseudoConsole.default.log(\"DFP - updating \".concat(name, \" to \").concat(position, \" seconds\"));\n\n    if (position > this.advertRefreshTime) {\n      // when DFP reloads it instantly triggers a 100% viewability, restarting this\n      // script, so we do not need to do anything other than reset all the things\n      this.resetTimer(name);\n      this.refreshAdvert(name);\n      return;\n    } // update the record\n\n\n    this.timerPositions[name] = position; // recurse and call the function again\n\n    this.runningTimers[name] = window.setTimeout(function () {\n      _this2.updateTimer(name);\n    }, 1000);\n  },\n  refreshAdvert: function refreshAdvert(name) {\n    // refresh the dfp advert\n    _PseudoConsole.default.log(\"DFP - Refreshing \".concat(name));\n\n    var cnt = this.timerCount[name];\n\n    _PseudoConsole.default.log(\"DFP - setting rfs to \".concat(cnt, \" for \").concat(name));\n\n    window.dfpHolder[name].setTargeting('rfs', cnt);\n    window.googletag.pubads().refresh([window.dfpHolder[name]]);\n    this.timerCount[name] += 1;\n  },\n  resetTimer: function resetTimer(name) {\n    // completely reset a timer\n    this.pauseTimer(name);\n    this.timerPositions[name] = 0;\n  },\n  checkName: function checkName(name) {\n    switch (name) {\n      case 'DFP_top_leaderboard':\n      case 'DFP_article_fluid_1':\n      case 'DFP_teads':\n      case 'DFP_out_of_page':\n      case 'DFP_on_scroll':\n      case 'DFP_standard_mpu_3':\n      case 'DFP_standard_mpu_4':\n      case 'DFP_standard_mpu_5':\n      case 'DFP_standard_mpu_6':\n      case 'DFP_standard_mpu_7':\n      case 'DFP_gallery_mpu':\n      case 'DFP_gallery_interstitial':\n        return false;\n\n      default:\n        return true;\n    }\n  },\n  checkFocus: function checkFocus() {\n    return this.browserFocus;\n  },\n  setFocusListeners: function setFocusListeners() {\n    var _this3 = this;\n\n    window.addEventListener('focus', function () {\n      _this3.setFocused();\n    }, false);\n    window.addEventListener('blur', function () {\n      _this3.setBlurred();\n    }, false);\n  },\n  setFocused: function setFocused() {\n    _PseudoConsole.default.log('DFP - Browser focused');\n\n    this.browserFocus = true;\n  },\n  setBlurred: function setBlurred() {\n    _PseudoConsole.default.log('DFP - Browser blurred');\n\n    this.browserFocus = false;\n  },\n  checkRefresh: function checkRefresh(name) {\n    _PseudoConsole.default.log(\"Checking DOM for no-refresh on \".concat(name));\n\n    var containsNoRefresh = document.getElementById(name).classList.contains('no-refresh');\n\n    _PseudoConsole.default.log(\"Does \".concat(name, \" contain 'no-refresh'? \").concat(containsNoRefresh)); // return an alternate\n\n\n    return !containsNoRefresh;\n  }\n};\nexports.default = _default;\n\n//# sourceURL=webpack:///./es6/lib/synchronous/AdvertisingRefresh.js?");

/***/ }),

/***/ "./es6/lib/synchronous/DataLayerHelper.js":
/*!************************************************!*\
  !*** ./es6/lib/synchronous/DataLayerHelper.js ***!
  \************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

"use strict";
eval("\n\nvar _interopRequireDefault = __webpack_require__(/*! @babel/runtime/helpers/interopRequireDefault */ \"./node_modules/@babel/runtime/helpers/interopRequireDefault.js\");\n\nObject.defineProperty(exports, \"__esModule\", {\n  value: true\n});\nexports.default = void 0;\n\nvar _typeof2 = _interopRequireDefault(__webpack_require__(/*! @babel/runtime/helpers/typeof */ \"./node_modules/@babel/runtime/helpers/typeof.js\"));\n\nvar _jquery = _interopRequireDefault(__webpack_require__(/*! ../../shims/jquery */ \"./es6/shims/jquery.js\"));\n\nvar _default = {\n  isArticleState: false,\n  cookieDomain: null,\n  moduleType: '',\n  localInfoVirtualFolder: '',\n  homesData: '',\n  primaryDomain: '',\n  path: '',\n  googleNetworkCode: 0,\n  articleTimestamp: 0,\n  setModuleType: function setModuleType(moduleType) {\n    this.moduleType = moduleType;\n  },\n  setLocalInfoFolder: function setLocalInfoFolder(folder) {\n    this.localInfoVirtualFolder = folder;\n  },\n  setHomesData: function setHomesData(bedroomsMinimum, priceMinimum, priceMaximum, location) {\n    var data = {\n      hmsbmn: bedroomsMinimum,\n      hmsl: location,\n      hmspmn: priceMinimum,\n      hmspmx: priceMaximum\n    };\n    this.homesData = data;\n  },\n  setIsArticle: function setIsArticle(bool) {\n    this.isArticleState = bool;\n  },\n  setCookieDomain: function setCookieDomain(domain) {\n    this.cookieDomain = domain;\n  },\n  setPrimaryDomain: function setPrimaryDomain(domain) {\n    this.primaryDomain = domain;\n  },\n  setPath: function setPath(path) {\n    this.path = path;\n  },\n  setArticleTimestamp: function setArticleTimestamp($ts) {\n    if (isNaN(parseInt($ts, 10))) {\n      return;\n    }\n\n    this.articleTimestamp = $ts;\n  },\n  setGoogleNetworkCode: function setGoogleNetworkCode(code) {\n    this.googleNetworkCode = code;\n  },\n  storeCurrentPageName: function storeCurrentPageName(pageName) {\n    var expiry = new Date();\n    expiry.setMinutes(expiry.getMinutes() + 30);\n\n    _jquery.default.cookie('nq_prev_page', pageName, {\n      expires: expiry,\n      path: '/',\n      domain: this.cookieDomain\n    });\n  },\n  getReferrerHostName: function getReferrerHostName() {\n    var ref = document.referrer;\n    var parser = document.createElement('a');\n    parser.href = ref;\n    return parser.hostname;\n  },\n  getAccount: function getAccount() {\n    if (document.location.pathname.indexOf('/admin') === 0) {\n      return 'newsquestgroupadmindev';\n    }\n\n    return 'newsquestgroupprod';\n  },\n  isMobileScreen: function isMobileScreen() {\n    if (window.screen.width < 728) {\n      return true;\n    }\n\n    return false;\n  },\n  isArticle: function isArticle() {\n    return this.isArticleState;\n  },\n  itemFromObject: function itemFromObject(key, obj) {\n    return (0, _typeof2.default)(obj) === 'object' && typeof obj[key] !== 'undefined' ? decodeURIComponent(obj[key]) : '';\n  },\n  getGoogleId: function getGoogleId() {\n    var googleId = \"/\".concat(this.googleNetworkCode, \"/\").concat(this.primaryDomain).concat(this.path);\n\n    if (googleId.charAt(googleId.length - 1) === '/') {\n      googleId = googleId.slice(0, -1);\n    }\n\n    return googleId;\n  },\n  getPathFragment: function getPathFragment(position, allowEmpty) {\n    var moduleType = this.moduleType;\n    var localInfoVirtualFolder = this.localInfoVirtualFolder;\n    var fullPath = document.location.pathname.toString();\n\n    if (moduleType.indexOf('Block') > -1 || moduleType === 'NationalNewsModule') {\n      moduleType = 'ArticleModule';\n    }\n\n    var slicedPath = fullPath.substring(1, fullPath.length - 1);\n    var pathFragment = slicedPath.split('/');\n\n    if (allowEmpty === true) {\n      if (slicedPath === '/') {\n        return position === 0 ? 'homepage' : '';\n      }\n\n      var clean = [];\n\n      for (var i = 0; i < 4; i++) {\n        if (typeof pathFragment[i] === 'undefined' || pathFragment[i].match(/[0-9]+\\./)) {\n          continue;\n        }\n\n        clean.push(pathFragment[i]);\n      }\n\n      return typeof clean[position] === 'undefined' ? '' : clean[position];\n    }\n\n    pathFragment[-1] = ''; // populate all parts of the pathFragment with a joined pathFragment\n\n    for (var _i = 0; _i < 4; _i++) {\n      if (typeof pathFragment[_i] === 'undefined') {\n        pathFragment[_i] = '';\n      }\n\n      if (pathFragment[_i] === '' || moduleType === 'ArticleModule' && pathFragment[_i].match(/[0-9]+\\./) && this.isArticleState) {\n        pathFragment[_i] = pathFragment[_i - 1];\n      } else if (_i > 0) {\n        pathFragment[_i] = \"\".concat(pathFragment[_i - 1], \"/\").concat(pathFragment[_i]);\n      }\n    } // handle homepages\n\n\n    if (fullPath === '/') {\n      pathFragment[0] = 'homepage';\n      pathFragment[1] = 'homepage';\n      pathFragment[2] = 'homepage';\n      pathFragment[3] = 'homepage';\n    } // handle local info\n\n\n    var matches = fullPath.match(/(\\w+)\\.(in|near|compare)\\.([,a-zA-Z0-9 _-]+)[/]?$/);\n    var detailMatches = fullPath.match(/(\\w+)\\.detail\\.(.*)$/);\n\n    if (detailMatches !== null && detailMatches.length > 1) {\n      matches = detailMatches;\n    }\n\n    if (matches !== null && matches.length > 1) {\n      pathFragment[0] = 'li';\n      pathFragment[1] = \"li/\".concat(localInfoVirtualFolder);\n      pathFragment[2] = \"li/\".concat(localInfoVirtualFolder, \"/\").concat(matches[1]);\n      pathFragment[3] = \"li/\".concat(localInfoVirtualFolder, \"/\").concat(matches[1]);\n    }\n\n    return pathFragment[position].toLowerCase();\n  },\n  getOmniturePath: function getOmniturePath() {\n    var path = '';\n\n    if ((0, _jquery.default)('#gallery-base-url').length === 1) {\n      path = (0, _jquery.default)('#gallery-base-url').val();\n    } else {\n      path = window.location.pathname;\n    }\n\n    return path;\n  },\n  getTrackingFromCookie: function getTrackingFromCookie(name) {\n    var backupValue = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : '';\n    var ref = this.getCookie(name);\n\n    if (!ref) {\n      return backupValue;\n    } // disabled while we are still dual running analytics\n    // $.removeCookie(name, {\n    //     path: '/',\n    //     domain: this.cookieDomain,\n    // });\n\n\n    return ref;\n  },\n  getConsentString: function getConsentString() {\n    var consent = [];\n\n    if (this.getCookie('consentUUID')) {\n      consent.push('yes');\n    } else {\n      consent.push('no');\n    }\n\n    if (this.getCookie('_sp_enable_dfp_personalized_ads') === 'true') {\n      consent.push('yes');\n    } else if (this.getCookie('_sp_enable_dfp_personalized_ads') === 'false') {\n      consent.push('no');\n    } else {\n      consent.push('none');\n    }\n\n    return consent.join(',');\n  },\n  getDayOfWeek: function getDayOfWeek() {\n    var d = new Date();\n    var weekday = [];\n    weekday[0] = 'Sunday';\n    weekday[1] = 'Monday';\n    weekday[2] = 'Tuesday';\n    weekday[3] = 'Wednesday';\n    weekday[4] = 'Thursday';\n    weekday[5] = 'Friday';\n    weekday[6] = 'Saturday';\n    var n = weekday[d.getDay()];\n    return n;\n  },\n  getAutoPlay: function getAutoPlay($hasVideo) {\n    // changed logic - the only video on the homepage would be a multimedia block, which doesn't have pathnames\n    return $hasVideo === 'yes' && window.location.pathname !== '/' ? 'yes' : 'no';\n  },\n  getArticleAge: function getArticleAge() {\n    var age = this.articleTimestamp;\n\n    if (age === 0) {\n      return '';\n    }\n\n    var time = parseInt(age, 10);\n    var currentTime = new Date().getTime() / 1000;\n    var diff = Math.round((currentTime - time) / 60);\n    return diff;\n  },\n  getSource: function getSource(id) {\n    if (id === '') {\n      return 'martini';\n    }\n\n    var typeId = parseInt(id, 10);\n\n    if (typeId === 6) {\n      return 'knowledge';\n    } else if (typeId === 8) {\n      return 'press association';\n    } else if (typeId === 17) {\n      return 'uefa';\n    }\n\n    return 'martini';\n  },\n  getCookie: function getCookie(name) {\n    var value = _jquery.default.cookie(name);\n\n    if (!value) {\n      return '';\n    }\n\n    return value;\n  },\n  getDevice: function getDevice() {\n    var md = new MobileDetect(window.navigator.userAgent);\n\n    if (md.phone() !== null) {\n      return 'phone';\n    } else if (md.tablet() !== null) {\n      return 'tablet';\n    }\n\n    return 'desktop';\n  },\n  getMobileDevice: function getMobileDevice() {\n    var md = new MobileDetect(window.navigator.userAgent);\n\n    if (md.phone() !== null) {\n      return 'yes';\n    }\n\n    return 'no';\n  },\n  getTabletDevice: function getTabletDevice() {\n    var md = new MobileDetect(window.navigator.userAgent);\n\n    if (md.tablet() !== null) {\n      return 'yes';\n    }\n\n    return 'no';\n  },\n  getSourcepointAdbockStatus: function getSourcepointAdbockStatus() {\n    if (typeof _jquery.default.cookie('nqab') === 'undefined') {\n      return 'unknown';\n    } else if (_jquery.default.cookie('nqab') === 'true') {\n      return 'yes';\n    }\n\n    return 'no';\n  },\n  getSubscriptionGateway: function getSubscriptionGateway(sagePayUser, subPlan) {\n    if (sagePayUser) {\n      return 'sagepay';\n    }\n\n    if (subPlan) {\n      return 'piano';\n    }\n\n    return '';\n  },\n  getSubscriptionLevel: function getSubscriptionLevel(subscriber, isLoggedIn) {\n    if (subscriber) {\n      return 'subscribed';\n    }\n\n    if (isLoggedIn) {\n      return 'registered';\n    }\n\n    return 'annonymous';\n  },\n  getPianoTags: function getPianoTags(pianoDomain, master, isBreaking, isEditorialOpinion, isFreeSubscriber, hasVideo, isArticle, contentLevelAccess, accessOverride, pictureGalleryId, userIsNewsquest, userLoggedIn, userHasNewsletter, isLiveArticle, whyFreeSubscriber, extraTags) {\n    var moduleType = this.moduleType;\n    var pianoTags = [];\n    pianoTags.push(moduleType);\n    pianoTags.push(pianoDomain);\n\n    if (master !== '') {\n      pianoTags.push(\"master_\".concat(master));\n    }\n\n    if (isBreaking) {\n      pianoTags.push('content_is_breaking');\n    }\n\n    if (isEditorialOpinion) {\n      pianoTags.push('content_is_editorial_opinion');\n    }\n\n    if (isFreeSubscriber) {\n      pianoTags.push('free_subscriber');\n    }\n\n    if (hasVideo) {\n      pianoTags.push('content_has_video');\n    }\n\n    if (isArticle) {\n      pianoTags.push('content_is_article');\n\n      if (contentLevelAccess !== '') {\n        pianoTags.push(\"content_level_\".concat(contentLevelAccess));\n      }\n\n      if (accessOverride) {\n        pianoTags.push('content_level_openaccess_override');\n      }\n\n      if (pictureGalleryId > 0) {\n        pianoTags.push('content_has_gallery');\n      }\n    }\n\n    if (userIsNewsquest) {\n      pianoTags.push('user_is_newsquest');\n    }\n\n    if (userLoggedIn) {\n      pianoTags.push('user_is_logged_in');\n    }\n\n    if (userHasNewsletter) {\n      pianoTags.push('user_has_newsletter');\n    }\n\n    if (isLiveArticle) {\n      pianoTags.push('content_is_live_article');\n    }\n\n    if (whyFreeSubscriber !== '') {\n      pianoTags.push(whyFreeSubscriber);\n    }\n\n    pianoTags.push(\"user_\".concat(this.getDevice()));\n\n    if (extraTags.length > 0) {\n      extraTags.forEach(function (tag) {\n        pianoTags.push(tag);\n      });\n    }\n\n    return pianoTags;\n  },\n  loadScript: function loadScript(url, async) {\n    var e = document.createElement('script');\n    var n = document.getElementsByTagName('body')[0];\n    e.type = 'text/javascript';\n    e.async = async;\n    e.src = url;\n    n.parentNode.insertBefore(e, n);\n  }\n};\nexports.default = _default;\n\n//# sourceURL=webpack:///./es6/lib/synchronous/DataLayerHelper.js?");

/***/ }),

/***/ "./es6/lib/synchronous/JWPlayer.js":
/*!*****************************************!*\
  !*** ./es6/lib/synchronous/JWPlayer.js ***!
  \*****************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

"use strict";
eval("\n\nvar _interopRequireDefault = __webpack_require__(/*! @babel/runtime/helpers/interopRequireDefault */ \"./node_modules/@babel/runtime/helpers/interopRequireDefault.js\");\n\nObject.defineProperty(exports, \"__esModule\", {\n  value: true\n});\nexports.default = void 0;\n\nvar _PseudoConsole = _interopRequireDefault(__webpack_require__(/*! ../../lib/PseudoConsole */ \"./es6/lib/PseudoConsole.js\"));\n\nvar _default = {\n  /**\n   * Whether there is already a player that autoplays for this page.\n   */\n  autoplay: true,\n\n  /**\n   * The initial volume level for the player.\n   */\n  volumeLevel: 30,\n  createdAdTimer: [],\n\n  /**\n   * Whether this is being called from a live article or not.\n   */\n  isLiveArticle: false,\n\n  /**\n   * Whether we are tracking any events.\n   */\n  tracked: false,\n\n  /**\n   * Whether the video has finished playing.\n   */\n  videoComplete: false,\n\n  /**\n   * Initialise the JWVideo.\n   * @param {string} googleId\n   */\n  init: function init(googleId) {\n    var _this = this;\n\n    var isLiveArticle = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;\n    this.isLiveArticle = isLiveArticle;\n\n    try {\n      var players = document.querySelectorAll('.jw-video');\n      players.forEach(function (player) {\n        _this.setupPlayer(player, googleId);\n      });\n    } catch (err) {// Something in old IE breaks inside this, and our local versions work fine\n    }\n  },\n\n  /**\n   * Setup a player.\n   * @param {domElement} player\n   */\n  setupPlayer: function setupPlayer(player, googleId) {\n    var _this2 = this;\n\n    var jwId = player.getAttribute('data-file');\n    var domId = player.getAttribute('id');\n    var playerId = player.getAttribute('data-player-id');\n    var videoPlayer = window.jwplayer(domId);\n    var playlistId = player.getAttribute('data-playlist-id');\n    var playlistArticleMatching = player.getAttribute('data-playlist-article-matching');\n    var playlistDisplay = player.getAttribute('data-playlist-display');\n    var prerollLimit = parseInt(player.getAttribute('data-preroll-limit'), 10);\n    var duration = parseInt(player.getAttribute('data-duration'), 10);\n    var siteId = parseInt(player.getAttribute('data-site-id'), 10);\n    var adFrequency = parseInt(player.getAttribute('data-ad-frequency'), 10);\n    var autoPlayOverride = player.getAttribute('data-autoplay');\n    var wrapperId = player.getAttribute('data-wrapper-id');\n    var playerWrapper = document.getElementById(wrapperId);\n    var advance = player.getAttribute('data-advance');\n    var disableAds = player.getAttribute('data-advertising-disabled'); // Check if this video has already been processed, as there is no need\n    // to process it multiple times (particularly useful in live articles)\n\n    var processed = playerWrapper.getAttribute('data-processed');\n\n    if (processed === 'yes') {\n      return;\n    } // Mark this video has having been processed, so that we do not attempt\n    // to re-process it in a live article\n\n\n    playerWrapper.setAttribute('data-processed', 'yes'); // We do not autoplay on multimedia block or if a video has already\n    // autoplayed\n    // TJ: turned off autoplay on request of Morgan Stevenson,  2019-06-17\n\n    var shouldAutoplay = autoPlayOverride === 'yes' && player.getAttribute('data-multimedia-block') === 'no' && this.autoplay && this.isLiveArticle === false; // const shouldAutoplay = false;\n    // We do not popout if the article is a sport article\n\n    var shouldPopout = player.getAttribute('data-popout') === 'yes' && !this.isMobile() && !this.isTablet(); // Create our default options for the video set up\n\n    var videoOptions = {};\n    videoOptions.playlist = this.getPlaylist(jwId);\n    videoOptions.setTimeEvents = true;\n\n    if (disableAds === '0') {\n      videoOptions.advertising = this.getAdSettings(duration, prerollLimit, googleId, shouldAutoplay, adFrequency);\n    }\n\n    if (playlistId !== '' && playlistId !== 'none') {\n      videoOptions.related = this.getRelated(playlistId, siteId, playlistDisplay, playlistArticleMatching, advance);\n    } // Check whether a video has already been assigned as an autoplay video\n\n\n    if (shouldAutoplay) {\n      // Stop any move videos on the page autoplaying\n      this.autoplay = false; // Set the autoplay options to the videoOptions object\n\n      videoOptions.autostart = 'viewable';\n      videoOptions.mute = true;\n    }\n\n    if (shouldPopout) {\n      videoOptions.floating = {};\n      videoOptions.floating.dismissible = true;\n    } // turn off autopause\n\n\n    videoOptions.autoPause = {};\n    videoOptions.autoPause.viewability = true;\n    this.debug(\"Player ID: \".concat(playerId), videoOptions, 'jw-settings');\n    videoPlayer.setup(videoOptions);\n    videoPlayer.on('ready', function (e) {\n      if (shouldAutoplay === true) {\n        // We need to do this when the player is ready, otherwise the\n        // button will be created and displayed before the video is\n        _this2.createMuteButton(videoPlayer, domId); // If the user unmutes the video using the control button in\n        // the player then hide the custom muted button overlay we add\n\n\n        videoPlayer.on('mute', function () {\n          if (videoPlayer.getMute() === false) {\n            document.getElementById(\"mute-\".concat(domId)).style.display = 'none';\n          }\n        }); // Only videos that can autoplay are allowed to float, but only\n        // on desktop (otherwise the video floats over content and / or\n        // side ads). If they do not popout and float they should pause\n        // when they scroll out of view, and then remain paused.\n        // TJ: Due to advertising, using the \"screenTop\" and \"pause\" method casuses large issues\n        // using the viewability stop and start is much more reliable and does not break adverts.\n\n        if (shouldPopout === true && _this2.isMobile() === false && _this2.isTablet() === false) {\n          _this2.popoutPlayer(videoPlayer);\n        }\n      }\n\n      _this2.debug('Ready', e, 'jw-settings');\n    });\n    videoPlayer.on('pause', function (e) {\n      _this2.debug('Pause', e, 'jw-settings', true);\n\n      if (e.pauseReason === 'viewable') {// TJ: Disabling play button as it looks poor\n        // this.createPlayButton(videoPlayer, domId);\n      }\n    }); // handle an advery countdown\n\n    videoPlayer.on('adTime', function (adEvent) {\n      // check we've not seen anything for this yet\n      if (typeof _this2.createdAdTimer[domId] === 'undefined') {\n        _this2.createdAdTimer[domId] = false;\n      } // create if required\n\n\n      if (_this2.createdAdTimer[domId] === false) {\n        _this2.debug('adTime called for the first time', adEvent, 'jw-settings');\n\n        _this2.createdAdTimer[domId] = true;\n\n        _this2.createAdTimer(adEvent, domId);\n      }\n    });\n    videoPlayer.on('adPlay', function (adPlay) {\n      _this2.debug('adPlay called', adPlay, 'jw-settings');\n    });\n    videoPlayer.on('play', function () {\n      // Mark the video playback is not complete\n      _this2.videoComplete = false;\n    });\n    videoPlayer.on('complete', function () {\n      // Mark the video playback is complete\n      _this2.videoComplete = true;\n    });\n  },\n  createAdTimer: function createAdTimer(adEvent, domId) {\n    var _this3 = this;\n\n    var duration = Math.floor(adEvent.duration);\n\n    if (adEvent.position > 0) {\n      duration -= Math.floor(adEvent.position);\n    } else {\n      // lower it by one because this appears to fire a bit late\n      duration -= 1;\n    }\n\n    this.createCountDown(duration, domId);\n    window.setTimeout(function () {\n      duration -= 1;\n\n      _this3.updateAdTimer(duration, domId);\n    }, 1000);\n  },\n  updateAdTimer: function updateAdTimer(seconds, domId) {\n    var _this4 = this;\n\n    var duration = seconds;\n    var time = this.secondsToTime(duration);\n    var ele = \"cd-\".concat(domId);\n\n    if (seconds > 0) {\n      document.getElementById(ele).innerHTML = \"Ad: \".concat(time);\n      window.setTimeout(function () {\n        duration -= 1;\n\n        _this4.updateAdTimer(duration, domId);\n      }, 1000);\n    } else {\n      document.getElementById(ele).style.display = 'none';\n    }\n  },\n  createCountDown: function createCountDown(seconds, domId) {\n    var eleId = \"cd-\".concat(domId);\n    var startTime = this.secondsToTime(seconds);\n    var div = document.createElement('div');\n    div.classList.add('jw-timer');\n    div.innerHTML = \"Ad: \".concat(startTime);\n    div.id = eleId;\n    document.getElementById(domId).parentElement.appendChild(div);\n  },\n  createAdvertisingUrl: function createAdvertisingUrl(googleId, shouldAutoplay) {\n    var autoPlay = shouldAutoplay ? 1 : 0;\n    var adServerUrl = \"https://securepubads.g.doubleclick.net/gampad/ads?iu=\".concat(googleId) + \"&sz=640x480&cust_params=cue%3Dpre%26atp%3D\".concat(autoPlay, \"&gdfp_req=1&env=vp&output=xml_vast3\") + '&unviewed_position_start=1&url=&description_url=__page-url__&correlator=';\n    return adServerUrl;\n  },\n\n  /**\n   * Create a mute button for the player.\n   * @param {jwplayer} videoPlayer\n   * @param {string} domId\n   */\n  createMuteButton: function createMuteButton(videoPlayer, domId) {\n    var _this5 = this;\n\n    var btnId = \"mute-\".concat(domId);\n\n    var callback = function callback() {\n      videoPlayer.setMute(false);\n      videoPlayer.setVolume(_this5.volumeLevel);\n      document.getElementById(btnId).style.display = 'none';\n    };\n\n    this.createButton(domId, btnId, 'fa-volume-mute', callback);\n  },\n  createPlayButton: function createPlayButton(videoPlayer, domId) {\n    var btnId = \"play-\".concat(domId);\n    var muteId = \"mute-\".concat(domId); // check if we have an existing muted display\n\n    if (videoPlayer.getMute() === true && document.getElementById(muteId)) {\n      document.getElementById(muteId).style.display = 'none';\n    }\n\n    var callback = function callback() {\n      videoPlayer.play(false);\n      document.getElementById(btnId).style.display = 'none';\n\n      if (videoPlayer.getMute() === true && document.getElementById(muteId)) {\n        document.getElementById(muteId).style.display = 'block';\n      }\n    };\n\n    this.createButton(domId, btnId, 'fa-play', callback);\n  },\n  createButton: function createButton(domId, btnId, icon, callback) {\n    // don't over-do\n    // if it already exists, then simply show it\n    if (document.getElementById(btnId) !== null) {\n      document.getElementById(btnId).style.display = 'block';\n      return;\n    }\n\n    var button = document.createElement('button');\n    button.id = btnId;\n    button.classList.add('jw-inner');\n    button.addEventListener('click', callback);\n    var span = document.createElement('span');\n    span.classList.add('jw-inner-font');\n    span.innerHTML = \"<i class=\\\"fas \".concat(icon, \"\\\"></i>\");\n    button.appendChild(span); // Add the button to the container\n\n    document.getElementById(domId).parentElement.appendChild(button);\n  },\n\n  /**\n   * Handles a popout player.\n   * @param {jwplayer} videoPlayer\n   */\n  popoutPlayer: function popoutPlayer(videoPlayer) {\n    var _this6 = this;\n\n    var playerEl = document.getElementById('playerDock'); // Get the player element position from top of document\n\n    var playerOffsetTop = this.getElementOffsetTop(playerEl); // Window onscroll event handler\n\n    window.onscroll = function () {\n      // Call scroll event view handler. Only do this if the video has\n      // completed playing\n      if (!_this6.videoComplete) {\n        _this6.onScrollViewHandler(playerEl, videoPlayer, playerOffsetTop);\n      }\n    };\n  },\n\n  /**\n   * Returns video player position from top of document.\n   * @param {domElement} playerEl\n   * @return {integer}\n   */\n  getElementOffsetTop: function getElementOffsetTop(playerEl) {\n    var boundingClientRect = playerEl.getBoundingClientRect();\n    var bodyEl = document.body;\n    var docEl = document.documentElement;\n    var scrollTop = window.pageYOffset || docEl.scrollTop || bodyEl.scrollTop;\n    var clientTop = docEl.clientTop || bodyEl.clientTop || 0;\n    var diffTop = scrollTop - clientTop;\n    return Math.round(boundingClientRect.top + diffTop);\n  },\n\n  /**\n   * Returns the current y scroll position.\n   * @return {integer}\n   */\n  getScrollTop: function getScrollTop() {\n    var docEl = document.documentElement;\n    return (window.pageYOffset || docEl.scrollTop) - (docEl.clientTop || 0);\n  },\n\n  /**\n   * Determines if the inline player is visible in the viewport or not. If\n   * not, add the .popout-player class and float the video. Otherwise,\n   * remove the class to put the player back to the inline position.\n   * @return {integer}\n   */\n  onScrollViewHandler: function onScrollViewHandler(playerEl, videoPlayer, playerOffsetTop) {\n    if (this.getScrollTop() >= playerOffsetTop) {\n      if (!this.tracked) {\n        _PseudoConsole.default.log('VID: setting tracking for event52');\n\n        this.tracked = true;\n        var s = window.s;\n        s.suppressPageView = true;\n        s.events = s.apl(s.events, 'event52', ',', 1);\n        s.linkTrackVars = 'eVar2,eVar4,eVar5,eVar6,eVar10,eVar16,eVar19,eVar25,eVar43,events';\n        s.linkTrackEvents = 'event52';\n        s.tl(true, 'o', 'video float');\n      }\n\n      playerEl.classList.add('popout-player');\n    } else {\n      playerEl.classList.remove('popout-player');\n    } // Update the player size so the controls are adjusted\n\n\n    videoPlayer.resize();\n  },\n\n  /**\n   * Check if this device is a mobile.\n   * @return {boolean}\n   */\n  isMobile: function isMobile() {\n    if (window.screen.width < 768) {\n      return true;\n    }\n\n    return false;\n  },\n\n  /**\n   * Check if this device is a tablet.\n   * @return {boolean}\n   */\n  isTablet: function isTablet() {\n    if (window.screen.width >= 768 && window.screen.width <= 1024) {\n      return true;\n    }\n\n    return false;\n  },\n  secondsToTime: function secondsToTime(secs) {\n    var divisorForMinutes = secs % (60 * 60);\n    var minutes = Math.floor(divisorForMinutes / 60);\n    var divisorForSeconds = divisorForMinutes % 60;\n    var seconds = Math.ceil(divisorForSeconds);\n    seconds = seconds.toString().padStart(2, '0');\n    return \"\".concat(minutes, \":\").concat(seconds);\n  },\n\n  /**\n   * Take a json obect and format it and export it to the page for debugging\n   * @param  {object}  json\n   * @param  {string}  target\n   * @param  {Boolean} append\n   * @return {void}\n   */\n  debug: function debug(name, json, target) {\n    var qs = new URLSearchParams(window.location.search);\n\n    if (parseInt(qs.get('debug'), 10) !== 1) {\n      return;\n    } // debug mode\n\n\n    var hr = document.createElement('hr');\n    var header = document.createElement('h4');\n    header.innerHTML = name;\n    var preTag = document.createElement('pre');\n    preTag.innerHTML = JSON.stringify(json, null, 4);\n    preTag.style.whiteSpace = 'pre-wrap';\n    var adP = document.getElementById(target);\n    adP.insertBefore(preTag, adP.firstChild);\n    adP.insertBefore(header, adP.firstChild);\n    adP.insertBefore(hr, adP.firstChild);\n  },\n  getPlaylist: function getPlaylist(id) {\n    return \"https://cdn.jwplayer.com/v2/media/\".concat(id);\n  },\n  getRelated: function getRelated(playlistId, siteId, displayMode, playlistArticleMatching, advance) {\n    var playList = \"https://cdn.jwplayer.com/v2/playlists/\".concat(playlistId);\n    var args = \"tags=site%3D\".concat(siteId);\n\n    if (playlistArticleMatching === 'yes') {\n      args += '&search=__CONTEXTUAL__';\n    }\n\n    var url = \"\".concat(playList, \"?\").concat(args);\n    var options = {};\n    options.file = url;\n    options.displayMode = displayMode;\n\n    if (advance === 'no') {\n      options.oncomplete = 'show';\n    } else {\n      options.oncomplete = 'autoplay';\n      options.autoplaytimer = parseInt(advance, 10);\n    }\n\n    options.onclick = 'play';\n    return options;\n  },\n  getAdSettings: function getAdSettings(duration, prerollLimit, googleId, shouldAutoplay, adFrequency) {\n    var startOn = 1;\n\n    if (duration <= prerollLimit) {\n      startOn = 2;\n    }\n\n    var frequency = adFrequency;\n\n    if (frequency < 1) {\n      frequency = 1;\n    }\n\n    var advertising = {};\n    advertising.client = 'googima';\n    advertising.autoplayadsmuted = true;\n    advertising.tag = this.createAdvertisingUrl(googleId, shouldAutoplay);\n    advertising.vastLoadTimeout = 7500;\n    var rules = {};\n    rules.startOn = startOn;\n    rules.frequency = frequency;\n    advertising.rules = rules;\n    var bidders = [];\n    var spotX = {\n      name: 'SpotX',\n      id: '264093'\n    };\n    bidders[0] = spotX;\n    advertising.bids = {};\n    advertising.bids.bidders = bidders;\n    advertising.bids.settings = {\n      mediationLayerAdServer: 'dfp',\n      floorPriceCents: 0,\n      floorPriceCurrency: 'gbp',\n      bidTimeout: 1000\n    };\n    return advertising;\n  }\n};\nexports.default = _default;\n\n//# sourceURL=webpack:///./es6/lib/synchronous/JWPlayer.js?");

/***/ }),

/***/ "./es6/lib/synchronous/PianoHelper.js":
/*!********************************************!*\
  !*** ./es6/lib/synchronous/PianoHelper.js ***!
  \********************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

"use strict";
eval("\n\nvar _interopRequireDefault = __webpack_require__(/*! @babel/runtime/helpers/interopRequireDefault */ \"./node_modules/@babel/runtime/helpers/interopRequireDefault.js\");\n\nObject.defineProperty(exports, \"__esModule\", {\n  value: true\n});\nexports.default = void 0;\n\nvar _typeof2 = _interopRequireDefault(__webpack_require__(/*! @babel/runtime/helpers/typeof */ \"./node_modules/@babel/runtime/helpers/typeof.js\"));\n\nvar _slicedToArray2 = _interopRequireDefault(__webpack_require__(/*! @babel/runtime/helpers/slicedToArray */ \"./node_modules/@babel/runtime/helpers/slicedToArray.js\"));\n\nvar _jquery = _interopRequireDefault(__webpack_require__(/*! ../../shims/jquery */ \"./es6/shims/jquery.js\"));\n\nvar _Scroll = _interopRequireDefault(__webpack_require__(/*! ../../lib/utils/Scroll */ \"./es6/lib/utils/Scroll.js\"));\n\nvar _Strings = _interopRequireDefault(__webpack_require__(/*! ../../lib/utils/Strings */ \"./es6/lib/utils/Strings.js\"));\n\nvar _default = {\n  piano: null,\n  dataLayer: {},\n  init: function init() {\n    var _this = this;\n\n    (0, _jquery.default)(document).on('click', '#piano-helper-init', function (e) {\n      e.preventDefault();\n\n      _this.show();\n    });\n    (0, _jquery.default)(document).on('click', '#show-piano-divs', function (e) {\n      e.preventDefault();\n\n      _this.showPianoDivs();\n    });\n    (0, _jquery.default)(document).on('click', '#datalayer-helper-init', function (e) {\n      e.preventDefault();\n\n      _this.showLaunch();\n    });\n  },\n  show: function show() {\n    this.list();\n    (0, _jquery.default)('#piano-helper-modal').modal();\n  },\n  showLaunch: function showLaunch() {\n    this.listLaunch();\n    (0, _jquery.default)('#launch-helper-modal').modal();\n  },\n  list: function list() {\n    this.getParams(); // Object.entries(this.piano.customParams).forEach((item) => {\n    //     const [k, v] = item;\n    //     $('.piano-custom-params-available').append(`<li>${k}: ${v}</li>`);\n    // });\n    // if (Object.entries(this.piano.customParams).length === 0) {\n    //     $('.piano-custom-params-available').replaceWith('<p>No custom params</p>');\n    // }\n\n    Object.entries(this.piano.customVariables).forEach(function (item) {\n      var _item = (0, _slicedToArray2.default)(item, 2),\n          k = _item[0],\n          v = _item[1];\n\n      (0, _jquery.default)('.piano-custom-variables-available').append(\"<li>\".concat(k, \": \").concat(v, \"</li>\"));\n    });\n    this.piano.tags.split(',').forEach(function (tag) {\n      (0, _jquery.default)('.piano-tags-available').append(\"<li>\".concat(tag, \"</li>\"));\n    });\n  },\n  showPianoDivs: function showPianoDivs() {\n    (0, _jquery.default)('*[id^=\"piano\"]').each(function (i, e) {\n      var d = (0, _jquery.default)(e);\n\n      if (d.attr('id') === 'piano-helper-modal' || d.attr('id') === 'piano-helper-init') {\n        return;\n      }\n\n      d.html(d.attr('id'));\n      d.css({\n        border: '5px solid black',\n        backgroundColor: '#ffffff',\n        textAlign: 'center',\n        display: 'block',\n        zIndex: 9999999999\n      });\n    });\n    (0, _jquery.default)('#piano-helper-modal').modal('hide');\n  },\n  getParams: function getParams() {\n    var defaultVals = {\n      customVariables: {},\n      customParams: {},\n      tags: ''\n    };\n    var tp = window.tp || defaultVals;\n    this.piano = tp;\n  },\n  getLaunchParams: function getLaunchParams() {\n    return window.data_layer;\n  },\n  jumpLaunch: function jumpLaunch(e) {\n    var target = (0, _jquery.default)(e.currentTarget).val();\n\n    _Scroll.default.scrollTo(\"launch-block-\".concat(target, \"-list\"), 75);\n  },\n  listLaunch: function listLaunch() {\n    var _this2 = this;\n\n    var params = window.data_layer;\n    this.listItems(params, 'data_layer.');\n    Object.entries(this.dataLayer).forEach(function (item) {\n      var _item2 = (0, _slicedToArray2.default)(item, 2),\n          k = _item2[0],\n          v = _item2[1];\n\n      var _k$split = k.split('-'),\n          _k$split2 = (0, _slicedToArray2.default)(_k$split, 2),\n          l = _k$split2[0],\n          n = _k$split2[1];\n\n      if ((0, _jquery.default)(\"#launch-block-\".concat(l)).length === 0) {\n        _this2.createBlock(l);\n\n        (0, _jquery.default)('#launch-jump').append(\"<option value=\\\"\".concat(l, \"\\\" \").concat(_Strings.default.ucFirst(l), \" items</option>\"));\n      } // eliminate the top level\n\n\n      var levels = n.split('.'); // throw away the first one\n\n      levels.shift(); // get the category\n\n      var topLevel = _Strings.default.ucFirst(levels.shift()); // create the rest of the string\n\n\n      var secondLevel = levels.join(' ').replace(/_/g, ' ');\n      secondLevel = _Strings.default.titleCase(secondLevel);\n      secondLevel = secondLevel.replace('Id', 'ID');\n      secondLevel = secondLevel.replace('Cmp', 'CMP');\n      (0, _jquery.default)(\"#launch-block-\".concat(l, \"-list\")).append(\"\\n            <li class=\\\"border mb-3\\\">\\n                <b class=\\\"pl-1\\\">\".concat(secondLevel, \":</b>\\n                <div class=\\\"container\\\">\\n                    <div class=\\\"row\\\">\\n                        <div class=\\\"col-2 text-right\\\">Current value:</div>\\n                        <div class=\\\"col-10\\\"><code>\").concat(v, \"</code></div>\\n                        <div class=\\\"col-2 text-right\\\">JavaScript:</div>\\n                        <div class=\\\"col-10\\\"><code>\").concat(n, \"</code><span class=\\\"pl-2\\\"><i class=\\\"far fa-copy text-black-50\\\" style=\\\"cursor:pointer;\\\"></i></span></div>\\n                        <div class=\\\"col-2 text-right\\\">Launch code:</div>\\n                        <div class=\\\"col-10\\\"><code>_satellite.getVar(\\\"\").concat(topLevel, \": \").concat(secondLevel, \"\\\");</code><span class=\\\"pl-2\\\"><i class=\\\"far fa-copy text-black-50\\\" style=\\\"cursor:pointer;\\\"></i></span></div>\\n                    </div>\\n                </div>\\n            </li>\"));\n    });\n    (0, _jquery.default)('.fa-copy').on('click', function (e) {\n      // Frick copying things in javascript.\n      var container = e.target.parentNode.parentNode;\n      var text = (0, _jquery.default)(container).find('code')[0].textContent;\n      (0, _jquery.default)('<input style=\"position:fixed;top:0;z-index:999999\" id=\"copy\">').appendTo('#launch-helper-modal');\n      (0, _jquery.default)('#copy').val(text).focus().select();\n      document.execCommand('copy');\n      (0, _jquery.default)('#copy').remove();\n      (0, _jquery.default)('<div style=\"position:fixed;top:50%;left:50%;\" class=\"bg-light\"></div>');\n      e.target.classList.remove('text-black-50');\n      e.target.classList.remove('far');\n      e.target.classList.add('text-success');\n      e.target.classList.add('fa');\n      setTimeout(function () {\n        e.target.classList.remove('text-success');\n        e.target.classList.remove('fa');\n        e.target.classList.add('text-black-50');\n        e.target.classList.add('far');\n      }, 5000);\n    });\n  },\n  createBlock: function createBlock(category) {\n    var container = (0, _jquery.default)('<div class=\"card\"></div>').attr('id', \"launch-block-\".concat(category));\n    var headerContainer = (0, _jquery.default)('<div class=\"card-header\"></div>');\n    var header = (0, _jquery.default)('<h4></h4>').html(\"\".concat(_Strings.default.ucFirst(category), \" items\"));\n    var toggle = (0, _jquery.default)('<span class=\"float-right collapsed collapse-icon-container\"></span>').attr('data-toggle', 'collapse').attr('data-target', \"#launch-block-\".concat(category, \"-list-body\")).html('<i class=\"fas fa-plus-circle\"></i>').attr('id', \"launch-block-\".concat(category, \"-cross\")).attr('role', 'button');\n    var listHolder = (0, _jquery.default)('<div class=\"card-body collapse\"></div>').attr('id', \"launch-block-\".concat(category, \"-list-body\")); // .attr('data-parent', '.modal-launch-template'); // uncomment for accordion\n\n    var list = (0, _jquery.default)('<ul></ul>').attr('id', \"launch-block-\".concat(category, \"-list\")).attr('style', 'margin-top: 20px');\n    listHolder.append(list);\n    header.append(toggle);\n    headerContainer.append(header);\n    container.append(headerContainer);\n    container.append(listHolder);\n    (0, _jquery.default)('.modal-launch-template').append(container);\n  },\n  listItems: function listItems(items, key) {\n    var _this3 = this;\n\n    var toplevel = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : '';\n    Object.entries(items).forEach(function (item) {\n      var _item3 = (0, _slicedToArray2.default)(item, 2),\n          k = _item3[0],\n          v = _item3[1];\n\n      var top = '';\n\n      if (toplevel === '') {\n        top = k;\n      } else {\n        top = toplevel;\n      }\n\n      var value = '';\n\n      if (v === null || v === false) {\n        value = '';\n      } else {\n        value = v;\n      }\n\n      if ((0, _typeof2.default)(value) === 'object') {\n        _this3.listItems(value, \"\".concat(key).concat(k, \".\"), top);\n      } else {\n        _this3.dataLayer[\"\".concat(toplevel, \"-\").concat(key).concat(k)] = value;\n      }\n    });\n  }\n};\nexports.default = _default;\n\n//# sourceURL=webpack:///./es6/lib/synchronous/PianoHelper.js?");

/***/ }),

/***/ "./es6/lib/synchronous/PianoListener.js":
/*!**********************************************!*\
  !*** ./es6/lib/synchronous/PianoListener.js ***!
  \**********************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

"use strict";
eval("/* WEBPACK VAR INJECTION */(function(process) {\n\nvar _interopRequireDefault = __webpack_require__(/*! @babel/runtime/helpers/interopRequireDefault */ \"./node_modules/@babel/runtime/helpers/interopRequireDefault.js\");\n\nObject.defineProperty(exports, \"__esModule\", {\n  value: true\n});\nexports.default = void 0;\n\nvar _regenerator = _interopRequireDefault(__webpack_require__(/*! @babel/runtime/regenerator */ \"./node_modules/@babel/runtime/regenerator/index.js\"));\n\nvar _asyncToGenerator2 = _interopRequireDefault(__webpack_require__(/*! @babel/runtime/helpers/asyncToGenerator */ \"./node_modules/@babel/runtime/helpers/asyncToGenerator.js\"));\n\nvar _DataLayer = _interopRequireDefault(__webpack_require__(/*! ../../shims/DataLayer */ \"./es6/shims/DataLayer.js\"));\n\nvar _default = {\n  init: function init() {\n    var _this = this;\n\n    window.addEventListener('message', function (e) {\n      var data = e.data;\n      var src = data.src;\n\n      switch (src) {\n        case 'send-email':\n          _this.handleEmail(data);\n\n          break;\n\n        default:\n          break;\n      }\n    });\n  },\n  handleEmail: function () {\n    var _handleEmail = (0, _asyncToGenerator2.default)(\n    /*#__PURE__*/\n    _regenerator.default.mark(function _callee(data) {\n      var settings;\n      return _regenerator.default.wrap(function _callee$(_context) {\n        while (1) {\n          switch (_context.prev = _context.next) {\n            case 0:\n              settings = {\n                method: 'POST',\n                headers: {\n                  'Content-Type': 'application/x-www-form-urlencoded'\n                },\n                body: \"email=\".concat(data.email)\n              };\n              _context.next = 3;\n              return fetch('/ws/public/email/', settings);\n\n            case 3:\n              // Force into nextTick to ensure that request has completed, then redirect user\n              process.nextTick(function () {\n                document.cookie = \"newsletterSignUpBulletins=\".concat(data.newsletterId.toString(), \"; expires=; path=/;\"); // If the user is already logged in, take them directly to the emails page.\n\n                if (typeof _DataLayer.default.user.is_logged_in !== 'undefined' && _DataLayer.default.user.is_logged_in === 'yes') {\n                  window.location = '/my/account/profile/#emails';\n                } else {\n                  window.location = '/my/account/register/';\n                }\n              });\n\n            case 4:\n            case \"end\":\n              return _context.stop();\n          }\n        }\n      }, _callee);\n    }));\n\n    function handleEmail(_x) {\n      return _handleEmail.apply(this, arguments);\n    }\n\n    return handleEmail;\n  }()\n};\nexports.default = _default;\n/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./../../../node_modules/process/browser.js */ \"./node_modules/process/browser.js\")))\n\n//# sourceURL=webpack:///./es6/lib/synchronous/PianoListener.js?");

/***/ }),

/***/ "./es6/lib/utils/Scroll.js":
/*!*********************************!*\
  !*** ./es6/lib/utils/Scroll.js ***!
  \*********************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

"use strict";
eval("\n\nObject.defineProperty(exports, \"__esModule\", {\n  value: true\n});\nexports.default = void 0;\nvar _default = {\n  scrollTo: function scrollTo(target, offset) {\n    var element = document.getElementById(target);\n    var elementPosition = element.getBoundingClientRect().top;\n    var offsetPosition = elementPosition - offset;\n    window.scrollTo({\n      top: offsetPosition,\n      behavior: 'smooth'\n    });\n  }\n};\nexports.default = _default;\n\n//# sourceURL=webpack:///./es6/lib/utils/Scroll.js?");

/***/ }),

/***/ "./es6/lib/utils/Strings.js":
/*!**********************************!*\
  !*** ./es6/lib/utils/Strings.js ***!
  \**********************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

"use strict";
eval("\n\nObject.defineProperty(exports, \"__esModule\", {\n  value: true\n});\nexports.default = void 0;\nvar _default = {\n  ucFirst: function ucFirst(word) {\n    return word.charAt(0).toUpperCase() + word.slice(1);\n  },\n  titleCase: function titleCase(sentence) {\n    var _this = this;\n\n    var words = sentence.split(' ');\n    var titleCase = [];\n    words.forEach(function (w) {\n      titleCase.push(_this.ucFirst(w));\n    });\n    return titleCase.join(' ');\n  }\n};\nexports.default = _default;\n\n//# sourceURL=webpack:///./es6/lib/utils/Strings.js?");

/***/ }),

/***/ "./es6/shims/DataLayer.js":
/*!********************************!*\
  !*** ./es6/shims/DataLayer.js ***!
  \********************************/
/*! no static exports found */
/***/ (function(module, exports) {

eval("module.exports = window.data_layer;\n\n//# sourceURL=webpack:///./es6/shims/DataLayer.js?");

/***/ }),

/***/ "./es6/shims/jquery.js":
/*!*****************************!*\
  !*** ./es6/shims/jquery.js ***!
  \*****************************/
/*! no static exports found */
/***/ (function(module, exports) {

eval("module.exports = window.jQuery;\n\n//# sourceURL=webpack:///./es6/shims/jquery.js?");

/***/ }),

/***/ "./node_modules/@babel/runtime/helpers/arrayWithHoles.js":
/*!***************************************************************!*\
  !*** ./node_modules/@babel/runtime/helpers/arrayWithHoles.js ***!
  \***************************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

eval("function _arrayWithHoles(arr) {\n  if (Array.isArray(arr)) return arr;\n}\n\nmodule.exports = _arrayWithHoles;\n\n//# sourceURL=webpack:///./node_modules/@babel/runtime/helpers/arrayWithHoles.js?");

/***/ }),

/***/ "./node_modules/@babel/runtime/helpers/asyncToGenerator.js":
/*!*****************************************************************!*\
  !*** ./node_modules/@babel/runtime/helpers/asyncToGenerator.js ***!
  \*****************************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

eval("function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {\n  try {\n    var info = gen[key](arg);\n    var value = info.value;\n  } catch (error) {\n    reject(error);\n    return;\n  }\n\n  if (info.done) {\n    resolve(value);\n  } else {\n    Promise.resolve(value).then(_next, _throw);\n  }\n}\n\nfunction _asyncToGenerator(fn) {\n  return function () {\n    var self = this,\n        args = arguments;\n    return new Promise(function (resolve, reject) {\n      var gen = fn.apply(self, args);\n\n      function _next(value) {\n        asyncGeneratorStep(gen, resolve, reject, _next, _throw, \"next\", value);\n      }\n\n      function _throw(err) {\n        asyncGeneratorStep(gen, resolve, reject, _next, _throw, \"throw\", err);\n      }\n\n      _next(undefined);\n    });\n  };\n}\n\nmodule.exports = _asyncToGenerator;\n\n//# sourceURL=webpack:///./node_modules/@babel/runtime/helpers/asyncToGenerator.js?");

/***/ }),

/***/ "./node_modules/@babel/runtime/helpers/interopRequireDefault.js":
/*!**********************************************************************!*\
  !*** ./node_modules/@babel/runtime/helpers/interopRequireDefault.js ***!
  \**********************************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

eval("function _interopRequireDefault(obj) {\n  return obj && obj.__esModule ? obj : {\n    \"default\": obj\n  };\n}\n\nmodule.exports = _interopRequireDefault;\n\n//# sourceURL=webpack:///./node_modules/@babel/runtime/helpers/interopRequireDefault.js?");

/***/ }),

/***/ "./node_modules/@babel/runtime/helpers/iterableToArrayLimit.js":
/*!*********************************************************************!*\
  !*** ./node_modules/@babel/runtime/helpers/iterableToArrayLimit.js ***!
  \*********************************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

eval("function _iterableToArrayLimit(arr, i) {\n  var _arr = [];\n  var _n = true;\n  var _d = false;\n  var _e = undefined;\n\n  try {\n    for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) {\n      _arr.push(_s.value);\n\n      if (i && _arr.length === i) break;\n    }\n  } catch (err) {\n    _d = true;\n    _e = err;\n  } finally {\n    try {\n      if (!_n && _i[\"return\"] != null) _i[\"return\"]();\n    } finally {\n      if (_d) throw _e;\n    }\n  }\n\n  return _arr;\n}\n\nmodule.exports = _iterableToArrayLimit;\n\n//# sourceURL=webpack:///./node_modules/@babel/runtime/helpers/iterableToArrayLimit.js?");

/***/ }),

/***/ "./node_modules/@babel/runtime/helpers/nonIterableRest.js":
/*!****************************************************************!*\
  !*** ./node_modules/@babel/runtime/helpers/nonIterableRest.js ***!
  \****************************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

eval("function _nonIterableRest() {\n  throw new TypeError(\"Invalid attempt to destructure non-iterable instance\");\n}\n\nmodule.exports = _nonIterableRest;\n\n//# sourceURL=webpack:///./node_modules/@babel/runtime/helpers/nonIterableRest.js?");

/***/ }),

/***/ "./node_modules/@babel/runtime/helpers/slicedToArray.js":
/*!**************************************************************!*\
  !*** ./node_modules/@babel/runtime/helpers/slicedToArray.js ***!
  \**************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

eval("var arrayWithHoles = __webpack_require__(/*! ./arrayWithHoles */ \"./node_modules/@babel/runtime/helpers/arrayWithHoles.js\");\n\nvar iterableToArrayLimit = __webpack_require__(/*! ./iterableToArrayLimit */ \"./node_modules/@babel/runtime/helpers/iterableToArrayLimit.js\");\n\nvar nonIterableRest = __webpack_require__(/*! ./nonIterableRest */ \"./node_modules/@babel/runtime/helpers/nonIterableRest.js\");\n\nfunction _slicedToArray(arr, i) {\n  return arrayWithHoles(arr) || iterableToArrayLimit(arr, i) || nonIterableRest();\n}\n\nmodule.exports = _slicedToArray;\n\n//# sourceURL=webpack:///./node_modules/@babel/runtime/helpers/slicedToArray.js?");

/***/ }),

/***/ "./node_modules/@babel/runtime/helpers/typeof.js":
/*!*******************************************************!*\
  !*** ./node_modules/@babel/runtime/helpers/typeof.js ***!
  \*******************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

eval("function _typeof2(obj) { if (typeof Symbol === \"function\" && typeof Symbol.iterator === \"symbol\") { _typeof2 = function _typeof2(obj) { return typeof obj; }; } else { _typeof2 = function _typeof2(obj) { return obj && typeof Symbol === \"function\" && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : typeof obj; }; } return _typeof2(obj); }\n\nfunction _typeof(obj) {\n  if (typeof Symbol === \"function\" && _typeof2(Symbol.iterator) === \"symbol\") {\n    module.exports = _typeof = function _typeof(obj) {\n      return _typeof2(obj);\n    };\n  } else {\n    module.exports = _typeof = function _typeof(obj) {\n      return obj && typeof Symbol === \"function\" && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : _typeof2(obj);\n    };\n  }\n\n  return _typeof(obj);\n}\n\nmodule.exports = _typeof;\n\n//# sourceURL=webpack:///./node_modules/@babel/runtime/helpers/typeof.js?");

/***/ }),

/***/ "./node_modules/@babel/runtime/node_modules/regenerator-runtime/runtime.js":
/*!*********************************************************************************!*\
  !*** ./node_modules/@babel/runtime/node_modules/regenerator-runtime/runtime.js ***!
  \*********************************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

eval("/**\n * Copyright (c) 2014-present, Facebook, Inc.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\nvar runtime = (function (exports) {\n  \"use strict\";\n\n  var Op = Object.prototype;\n  var hasOwn = Op.hasOwnProperty;\n  var undefined; // More compressible than void 0.\n  var $Symbol = typeof Symbol === \"function\" ? Symbol : {};\n  var iteratorSymbol = $Symbol.iterator || \"@@iterator\";\n  var asyncIteratorSymbol = $Symbol.asyncIterator || \"@@asyncIterator\";\n  var toStringTagSymbol = $Symbol.toStringTag || \"@@toStringTag\";\n\n  function wrap(innerFn, outerFn, self, tryLocsList) {\n    // If outerFn provided and outerFn.prototype is a Generator, then outerFn.prototype instanceof Generator.\n    var protoGenerator = outerFn && outerFn.prototype instanceof Generator ? outerFn : Generator;\n    var generator = Object.create(protoGenerator.prototype);\n    var context = new Context(tryLocsList || []);\n\n    // The ._invoke method unifies the implementations of the .next,\n    // .throw, and .return methods.\n    generator._invoke = makeInvokeMethod(innerFn, self, context);\n\n    return generator;\n  }\n  exports.wrap = wrap;\n\n  // Try/catch helper to minimize deoptimizations. Returns a completion\n  // record like context.tryEntries[i].completion. This interface could\n  // have been (and was previously) designed to take a closure to be\n  // invoked without arguments, but in all the cases we care about we\n  // already have an existing method we want to call, so there's no need\n  // to create a new function object. We can even get away with assuming\n  // the method takes exactly one argument, since that happens to be true\n  // in every case, so we don't have to touch the arguments object. The\n  // only additional allocation required is the completion record, which\n  // has a stable shape and so hopefully should be cheap to allocate.\n  function tryCatch(fn, obj, arg) {\n    try {\n      return { type: \"normal\", arg: fn.call(obj, arg) };\n    } catch (err) {\n      return { type: \"throw\", arg: err };\n    }\n  }\n\n  var GenStateSuspendedStart = \"suspendedStart\";\n  var GenStateSuspendedYield = \"suspendedYield\";\n  var GenStateExecuting = \"executing\";\n  var GenStateCompleted = \"completed\";\n\n  // Returning this object from the innerFn has the same effect as\n  // breaking out of the dispatch switch statement.\n  var ContinueSentinel = {};\n\n  // Dummy constructor functions that we use as the .constructor and\n  // .constructor.prototype properties for functions that return Generator\n  // objects. For full spec compliance, you may wish to configure your\n  // minifier not to mangle the names of these two functions.\n  function Generator() {}\n  function GeneratorFunction() {}\n  function GeneratorFunctionPrototype() {}\n\n  // This is a polyfill for %IteratorPrototype% for environments that\n  // don't natively support it.\n  var IteratorPrototype = {};\n  IteratorPrototype[iteratorSymbol] = function () {\n    return this;\n  };\n\n  var getProto = Object.getPrototypeOf;\n  var NativeIteratorPrototype = getProto && getProto(getProto(values([])));\n  if (NativeIteratorPrototype &&\n      NativeIteratorPrototype !== Op &&\n      hasOwn.call(NativeIteratorPrototype, iteratorSymbol)) {\n    // This environment has a native %IteratorPrototype%; use it instead\n    // of the polyfill.\n    IteratorPrototype = NativeIteratorPrototype;\n  }\n\n  var Gp = GeneratorFunctionPrototype.prototype =\n    Generator.prototype = Object.create(IteratorPrototype);\n  GeneratorFunction.prototype = Gp.constructor = GeneratorFunctionPrototype;\n  GeneratorFunctionPrototype.constructor = GeneratorFunction;\n  GeneratorFunctionPrototype[toStringTagSymbol] =\n    GeneratorFunction.displayName = \"GeneratorFunction\";\n\n  // Helper for defining the .next, .throw, and .return methods of the\n  // Iterator interface in terms of a single ._invoke method.\n  function defineIteratorMethods(prototype) {\n    [\"next\", \"throw\", \"return\"].forEach(function(method) {\n      prototype[method] = function(arg) {\n        return this._invoke(method, arg);\n      };\n    });\n  }\n\n  exports.isGeneratorFunction = function(genFun) {\n    var ctor = typeof genFun === \"function\" && genFun.constructor;\n    return ctor\n      ? ctor === GeneratorFunction ||\n        // For the native GeneratorFunction constructor, the best we can\n        // do is to check its .name property.\n        (ctor.displayName || ctor.name) === \"GeneratorFunction\"\n      : false;\n  };\n\n  exports.mark = function(genFun) {\n    if (Object.setPrototypeOf) {\n      Object.setPrototypeOf(genFun, GeneratorFunctionPrototype);\n    } else {\n      genFun.__proto__ = GeneratorFunctionPrototype;\n      if (!(toStringTagSymbol in genFun)) {\n        genFun[toStringTagSymbol] = \"GeneratorFunction\";\n      }\n    }\n    genFun.prototype = Object.create(Gp);\n    return genFun;\n  };\n\n  // Within the body of any async function, `await x` is transformed to\n  // `yield regeneratorRuntime.awrap(x)`, so that the runtime can test\n  // `hasOwn.call(value, \"__await\")` to determine if the yielded value is\n  // meant to be awaited.\n  exports.awrap = function(arg) {\n    return { __await: arg };\n  };\n\n  function AsyncIterator(generator) {\n    function invoke(method, arg, resolve, reject) {\n      var record = tryCatch(generator[method], generator, arg);\n      if (record.type === \"throw\") {\n        reject(record.arg);\n      } else {\n        var result = record.arg;\n        var value = result.value;\n        if (value &&\n            typeof value === \"object\" &&\n            hasOwn.call(value, \"__await\")) {\n          return Promise.resolve(value.__await).then(function(value) {\n            invoke(\"next\", value, resolve, reject);\n          }, function(err) {\n            invoke(\"throw\", err, resolve, reject);\n          });\n        }\n\n        return Promise.resolve(value).then(function(unwrapped) {\n          // When a yielded Promise is resolved, its final value becomes\n          // the .value of the Promise<{value,done}> result for the\n          // current iteration.\n          result.value = unwrapped;\n          resolve(result);\n        }, function(error) {\n          // If a rejected Promise was yielded, throw the rejection back\n          // into the async generator function so it can be handled there.\n          return invoke(\"throw\", error, resolve, reject);\n        });\n      }\n    }\n\n    var previousPromise;\n\n    function enqueue(method, arg) {\n      function callInvokeWithMethodAndArg() {\n        return new Promise(function(resolve, reject) {\n          invoke(method, arg, resolve, reject);\n        });\n      }\n\n      return previousPromise =\n        // If enqueue has been called before, then we want to wait until\n        // all previous Promises have been resolved before calling invoke,\n        // so that results are always delivered in the correct order. If\n        // enqueue has not been called before, then it is important to\n        // call invoke immediately, without waiting on a callback to fire,\n        // so that the async generator function has the opportunity to do\n        // any necessary setup in a predictable way. This predictability\n        // is why the Promise constructor synchronously invokes its\n        // executor callback, and why async functions synchronously\n        // execute code before the first await. Since we implement simple\n        // async functions in terms of async generators, it is especially\n        // important to get this right, even though it requires care.\n        previousPromise ? previousPromise.then(\n          callInvokeWithMethodAndArg,\n          // Avoid propagating failures to Promises returned by later\n          // invocations of the iterator.\n          callInvokeWithMethodAndArg\n        ) : callInvokeWithMethodAndArg();\n    }\n\n    // Define the unified helper method that is used to implement .next,\n    // .throw, and .return (see defineIteratorMethods).\n    this._invoke = enqueue;\n  }\n\n  defineIteratorMethods(AsyncIterator.prototype);\n  AsyncIterator.prototype[asyncIteratorSymbol] = function () {\n    return this;\n  };\n  exports.AsyncIterator = AsyncIterator;\n\n  // Note that simple async functions are implemented on top of\n  // AsyncIterator objects; they just return a Promise for the value of\n  // the final result produced by the iterator.\n  exports.async = function(innerFn, outerFn, self, tryLocsList) {\n    var iter = new AsyncIterator(\n      wrap(innerFn, outerFn, self, tryLocsList)\n    );\n\n    return exports.isGeneratorFunction(outerFn)\n      ? iter // If outerFn is a generator, return the full iterator.\n      : iter.next().then(function(result) {\n          return result.done ? result.value : iter.next();\n        });\n  };\n\n  function makeInvokeMethod(innerFn, self, context) {\n    var state = GenStateSuspendedStart;\n\n    return function invoke(method, arg) {\n      if (state === GenStateExecuting) {\n        throw new Error(\"Generator is already running\");\n      }\n\n      if (state === GenStateCompleted) {\n        if (method === \"throw\") {\n          throw arg;\n        }\n\n        // Be forgiving, per 25.3.3.3.3 of the spec:\n        // https://people.mozilla.org/~jorendorff/es6-draft.html#sec-generatorresume\n        return doneResult();\n      }\n\n      context.method = method;\n      context.arg = arg;\n\n      while (true) {\n        var delegate = context.delegate;\n        if (delegate) {\n          var delegateResult = maybeInvokeDelegate(delegate, context);\n          if (delegateResult) {\n            if (delegateResult === ContinueSentinel) continue;\n            return delegateResult;\n          }\n        }\n\n        if (context.method === \"next\") {\n          // Setting context._sent for legacy support of Babel's\n          // function.sent implementation.\n          context.sent = context._sent = context.arg;\n\n        } else if (context.method === \"throw\") {\n          if (state === GenStateSuspendedStart) {\n            state = GenStateCompleted;\n            throw context.arg;\n          }\n\n          context.dispatchException(context.arg);\n\n        } else if (context.method === \"return\") {\n          context.abrupt(\"return\", context.arg);\n        }\n\n        state = GenStateExecuting;\n\n        var record = tryCatch(innerFn, self, context);\n        if (record.type === \"normal\") {\n          // If an exception is thrown from innerFn, we leave state ===\n          // GenStateExecuting and loop back for another invocation.\n          state = context.done\n            ? GenStateCompleted\n            : GenStateSuspendedYield;\n\n          if (record.arg === ContinueSentinel) {\n            continue;\n          }\n\n          return {\n            value: record.arg,\n            done: context.done\n          };\n\n        } else if (record.type === \"throw\") {\n          state = GenStateCompleted;\n          // Dispatch the exception by looping back around to the\n          // context.dispatchException(context.arg) call above.\n          context.method = \"throw\";\n          context.arg = record.arg;\n        }\n      }\n    };\n  }\n\n  // Call delegate.iterator[context.method](context.arg) and handle the\n  // result, either by returning a { value, done } result from the\n  // delegate iterator, or by modifying context.method and context.arg,\n  // setting context.delegate to null, and returning the ContinueSentinel.\n  function maybeInvokeDelegate(delegate, context) {\n    var method = delegate.iterator[context.method];\n    if (method === undefined) {\n      // A .throw or .return when the delegate iterator has no .throw\n      // method always terminates the yield* loop.\n      context.delegate = null;\n\n      if (context.method === \"throw\") {\n        // Note: [\"return\"] must be used for ES3 parsing compatibility.\n        if (delegate.iterator[\"return\"]) {\n          // If the delegate iterator has a return method, give it a\n          // chance to clean up.\n          context.method = \"return\";\n          context.arg = undefined;\n          maybeInvokeDelegate(delegate, context);\n\n          if (context.method === \"throw\") {\n            // If maybeInvokeDelegate(context) changed context.method from\n            // \"return\" to \"throw\", let that override the TypeError below.\n            return ContinueSentinel;\n          }\n        }\n\n        context.method = \"throw\";\n        context.arg = new TypeError(\n          \"The iterator does not provide a 'throw' method\");\n      }\n\n      return ContinueSentinel;\n    }\n\n    var record = tryCatch(method, delegate.iterator, context.arg);\n\n    if (record.type === \"throw\") {\n      context.method = \"throw\";\n      context.arg = record.arg;\n      context.delegate = null;\n      return ContinueSentinel;\n    }\n\n    var info = record.arg;\n\n    if (! info) {\n      context.method = \"throw\";\n      context.arg = new TypeError(\"iterator result is not an object\");\n      context.delegate = null;\n      return ContinueSentinel;\n    }\n\n    if (info.done) {\n      // Assign the result of the finished delegate to the temporary\n      // variable specified by delegate.resultName (see delegateYield).\n      context[delegate.resultName] = info.value;\n\n      // Resume execution at the desired location (see delegateYield).\n      context.next = delegate.nextLoc;\n\n      // If context.method was \"throw\" but the delegate handled the\n      // exception, let the outer generator proceed normally. If\n      // context.method was \"next\", forget context.arg since it has been\n      // \"consumed\" by the delegate iterator. If context.method was\n      // \"return\", allow the original .return call to continue in the\n      // outer generator.\n      if (context.method !== \"return\") {\n        context.method = \"next\";\n        context.arg = undefined;\n      }\n\n    } else {\n      // Re-yield the result returned by the delegate method.\n      return info;\n    }\n\n    // The delegate iterator is finished, so forget it and continue with\n    // the outer generator.\n    context.delegate = null;\n    return ContinueSentinel;\n  }\n\n  // Define Generator.prototype.{next,throw,return} in terms of the\n  // unified ._invoke helper method.\n  defineIteratorMethods(Gp);\n\n  Gp[toStringTagSymbol] = \"Generator\";\n\n  // A Generator should always return itself as the iterator object when the\n  // @@iterator function is called on it. Some browsers' implementations of the\n  // iterator prototype chain incorrectly implement this, causing the Generator\n  // object to not be returned from this call. This ensures that doesn't happen.\n  // See https://github.com/facebook/regenerator/issues/274 for more details.\n  Gp[iteratorSymbol] = function() {\n    return this;\n  };\n\n  Gp.toString = function() {\n    return \"[object Generator]\";\n  };\n\n  function pushTryEntry(locs) {\n    var entry = { tryLoc: locs[0] };\n\n    if (1 in locs) {\n      entry.catchLoc = locs[1];\n    }\n\n    if (2 in locs) {\n      entry.finallyLoc = locs[2];\n      entry.afterLoc = locs[3];\n    }\n\n    this.tryEntries.push(entry);\n  }\n\n  function resetTryEntry(entry) {\n    var record = entry.completion || {};\n    record.type = \"normal\";\n    delete record.arg;\n    entry.completion = record;\n  }\n\n  function Context(tryLocsList) {\n    // The root entry object (effectively a try statement without a catch\n    // or a finally block) gives us a place to store values thrown from\n    // locations where there is no enclosing try statement.\n    this.tryEntries = [{ tryLoc: \"root\" }];\n    tryLocsList.forEach(pushTryEntry, this);\n    this.reset(true);\n  }\n\n  exports.keys = function(object) {\n    var keys = [];\n    for (var key in object) {\n      keys.push(key);\n    }\n    keys.reverse();\n\n    // Rather than returning an object with a next method, we keep\n    // things simple and return the next function itself.\n    return function next() {\n      while (keys.length) {\n        var key = keys.pop();\n        if (key in object) {\n          next.value = key;\n          next.done = false;\n          return next;\n        }\n      }\n\n      // To avoid creating an additional object, we just hang the .value\n      // and .done properties off the next function object itself. This\n      // also ensures that the minifier will not anonymize the function.\n      next.done = true;\n      return next;\n    };\n  };\n\n  function values(iterable) {\n    if (iterable) {\n      var iteratorMethod = iterable[iteratorSymbol];\n      if (iteratorMethod) {\n        return iteratorMethod.call(iterable);\n      }\n\n      if (typeof iterable.next === \"function\") {\n        return iterable;\n      }\n\n      if (!isNaN(iterable.length)) {\n        var i = -1, next = function next() {\n          while (++i < iterable.length) {\n            if (hasOwn.call(iterable, i)) {\n              next.value = iterable[i];\n              next.done = false;\n              return next;\n            }\n          }\n\n          next.value = undefined;\n          next.done = true;\n\n          return next;\n        };\n\n        return next.next = next;\n      }\n    }\n\n    // Return an iterator with no values.\n    return { next: doneResult };\n  }\n  exports.values = values;\n\n  function doneResult() {\n    return { value: undefined, done: true };\n  }\n\n  Context.prototype = {\n    constructor: Context,\n\n    reset: function(skipTempReset) {\n      this.prev = 0;\n      this.next = 0;\n      // Resetting context._sent for legacy support of Babel's\n      // function.sent implementation.\n      this.sent = this._sent = undefined;\n      this.done = false;\n      this.delegate = null;\n\n      this.method = \"next\";\n      this.arg = undefined;\n\n      this.tryEntries.forEach(resetTryEntry);\n\n      if (!skipTempReset) {\n        for (var name in this) {\n          // Not sure about the optimal order of these conditions:\n          if (name.charAt(0) === \"t\" &&\n              hasOwn.call(this, name) &&\n              !isNaN(+name.slice(1))) {\n            this[name] = undefined;\n          }\n        }\n      }\n    },\n\n    stop: function() {\n      this.done = true;\n\n      var rootEntry = this.tryEntries[0];\n      var rootRecord = rootEntry.completion;\n      if (rootRecord.type === \"throw\") {\n        throw rootRecord.arg;\n      }\n\n      return this.rval;\n    },\n\n    dispatchException: function(exception) {\n      if (this.done) {\n        throw exception;\n      }\n\n      var context = this;\n      function handle(loc, caught) {\n        record.type = \"throw\";\n        record.arg = exception;\n        context.next = loc;\n\n        if (caught) {\n          // If the dispatched exception was caught by a catch block,\n          // then let that catch block handle the exception normally.\n          context.method = \"next\";\n          context.arg = undefined;\n        }\n\n        return !! caught;\n      }\n\n      for (var i = this.tryEntries.length - 1; i >= 0; --i) {\n        var entry = this.tryEntries[i];\n        var record = entry.completion;\n\n        if (entry.tryLoc === \"root\") {\n          // Exception thrown outside of any try block that could handle\n          // it, so set the completion value of the entire function to\n          // throw the exception.\n          return handle(\"end\");\n        }\n\n        if (entry.tryLoc <= this.prev) {\n          var hasCatch = hasOwn.call(entry, \"catchLoc\");\n          var hasFinally = hasOwn.call(entry, \"finallyLoc\");\n\n          if (hasCatch && hasFinally) {\n            if (this.prev < entry.catchLoc) {\n              return handle(entry.catchLoc, true);\n            } else if (this.prev < entry.finallyLoc) {\n              return handle(entry.finallyLoc);\n            }\n\n          } else if (hasCatch) {\n            if (this.prev < entry.catchLoc) {\n              return handle(entry.catchLoc, true);\n            }\n\n          } else if (hasFinally) {\n            if (this.prev < entry.finallyLoc) {\n              return handle(entry.finallyLoc);\n            }\n\n          } else {\n            throw new Error(\"try statement without catch or finally\");\n          }\n        }\n      }\n    },\n\n    abrupt: function(type, arg) {\n      for (var i = this.tryEntries.length - 1; i >= 0; --i) {\n        var entry = this.tryEntries[i];\n        if (entry.tryLoc <= this.prev &&\n            hasOwn.call(entry, \"finallyLoc\") &&\n            this.prev < entry.finallyLoc) {\n          var finallyEntry = entry;\n          break;\n        }\n      }\n\n      if (finallyEntry &&\n          (type === \"break\" ||\n           type === \"continue\") &&\n          finallyEntry.tryLoc <= arg &&\n          arg <= finallyEntry.finallyLoc) {\n        // Ignore the finally entry if control is not jumping to a\n        // location outside the try/catch block.\n        finallyEntry = null;\n      }\n\n      var record = finallyEntry ? finallyEntry.completion : {};\n      record.type = type;\n      record.arg = arg;\n\n      if (finallyEntry) {\n        this.method = \"next\";\n        this.next = finallyEntry.finallyLoc;\n        return ContinueSentinel;\n      }\n\n      return this.complete(record);\n    },\n\n    complete: function(record, afterLoc) {\n      if (record.type === \"throw\") {\n        throw record.arg;\n      }\n\n      if (record.type === \"break\" ||\n          record.type === \"continue\") {\n        this.next = record.arg;\n      } else if (record.type === \"return\") {\n        this.rval = this.arg = record.arg;\n        this.method = \"return\";\n        this.next = \"end\";\n      } else if (record.type === \"normal\" && afterLoc) {\n        this.next = afterLoc;\n      }\n\n      return ContinueSentinel;\n    },\n\n    finish: function(finallyLoc) {\n      for (var i = this.tryEntries.length - 1; i >= 0; --i) {\n        var entry = this.tryEntries[i];\n        if (entry.finallyLoc === finallyLoc) {\n          this.complete(entry.completion, entry.afterLoc);\n          resetTryEntry(entry);\n          return ContinueSentinel;\n        }\n      }\n    },\n\n    \"catch\": function(tryLoc) {\n      for (var i = this.tryEntries.length - 1; i >= 0; --i) {\n        var entry = this.tryEntries[i];\n        if (entry.tryLoc === tryLoc) {\n          var record = entry.completion;\n          if (record.type === \"throw\") {\n            var thrown = record.arg;\n            resetTryEntry(entry);\n          }\n          return thrown;\n        }\n      }\n\n      // The context.catch method must only be called with a location\n      // argument that corresponds to a known catch block.\n      throw new Error(\"illegal catch attempt\");\n    },\n\n    delegateYield: function(iterable, resultName, nextLoc) {\n      this.delegate = {\n        iterator: values(iterable),\n        resultName: resultName,\n        nextLoc: nextLoc\n      };\n\n      if (this.method === \"next\") {\n        // Deliberately forget the last sent value so that we don't\n        // accidentally pass it on to the delegate.\n        this.arg = undefined;\n      }\n\n      return ContinueSentinel;\n    }\n  };\n\n  // Regardless of whether this script is executing as a CommonJS module\n  // or not, return the runtime object so that we can declare the variable\n  // regeneratorRuntime in the outer scope, which allows this module to be\n  // injected easily by `bin/regenerator --include-runtime script.js`.\n  return exports;\n\n}(\n  // If this script is executing as a CommonJS module, use module.exports\n  // as the regeneratorRuntime namespace. Otherwise create a new empty\n  // object. Either way, the resulting object will be used to initialize\n  // the regeneratorRuntime variable at the top of this file.\n   true ? module.exports : undefined\n));\n\ntry {\n  regeneratorRuntime = runtime;\n} catch (accidentalStrictMode) {\n  // This module should not be running in strict mode, so the above\n  // assignment should always work unless something is misconfigured. Just\n  // in case runtime.js accidentally runs in strict mode, we can escape\n  // strict mode using a global Function call. This could conceivably fail\n  // if a Content Security Policy forbids using Function, but in that case\n  // the proper solution is to fix the accidental strict mode problem. If\n  // you've misconfigured your bundler to force strict mode and applied a\n  // CSP to forbid Function, and you're not willing to fix either of those\n  // problems, please detail your unique predicament in a GitHub issue.\n  Function(\"r\", \"regeneratorRuntime = r\")(runtime);\n}\n\n\n//# sourceURL=webpack:///./node_modules/@babel/runtime/node_modules/regenerator-runtime/runtime.js?");

/***/ }),

/***/ "./node_modules/@babel/runtime/regenerator/index.js":
/*!**********************************************************!*\
  !*** ./node_modules/@babel/runtime/regenerator/index.js ***!
  \**********************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

eval("module.exports = __webpack_require__(/*! regenerator-runtime */ \"./node_modules/@babel/runtime/node_modules/regenerator-runtime/runtime.js\");\n\n\n//# sourceURL=webpack:///./node_modules/@babel/runtime/regenerator/index.js?");

/***/ }),

/***/ "./node_modules/process/browser.js":
/*!*****************************************!*\
  !*** ./node_modules/process/browser.js ***!
  \*****************************************/
/*! no static exports found */
/***/ (function(module, exports) {

eval("// shim for using process in browser\nvar process = module.exports = {};\n\n// cached from whatever global is present so that test runners that stub it\n// don't break things.  But we need to wrap it in a try catch in case it is\n// wrapped in strict mode code which doesn't define any globals.  It's inside a\n// function because try/catches deoptimize in certain engines.\n\nvar cachedSetTimeout;\nvar cachedClearTimeout;\n\nfunction defaultSetTimout() {\n    throw new Error('setTimeout has not been defined');\n}\nfunction defaultClearTimeout () {\n    throw new Error('clearTimeout has not been defined');\n}\n(function () {\n    try {\n        if (typeof setTimeout === 'function') {\n            cachedSetTimeout = setTimeout;\n        } else {\n            cachedSetTimeout = defaultSetTimout;\n        }\n    } catch (e) {\n        cachedSetTimeout = defaultSetTimout;\n    }\n    try {\n        if (typeof clearTimeout === 'function') {\n            cachedClearTimeout = clearTimeout;\n        } else {\n            cachedClearTimeout = defaultClearTimeout;\n        }\n    } catch (e) {\n        cachedClearTimeout = defaultClearTimeout;\n    }\n} ())\nfunction runTimeout(fun) {\n    if (cachedSetTimeout === setTimeout) {\n        //normal enviroments in sane situations\n        return setTimeout(fun, 0);\n    }\n    // if setTimeout wasn't available but was latter defined\n    if ((cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && setTimeout) {\n        cachedSetTimeout = setTimeout;\n        return setTimeout(fun, 0);\n    }\n    try {\n        // when when somebody has screwed with setTimeout but no I.E. maddness\n        return cachedSetTimeout(fun, 0);\n    } catch(e){\n        try {\n            // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally\n            return cachedSetTimeout.call(null, fun, 0);\n        } catch(e){\n            // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error\n            return cachedSetTimeout.call(this, fun, 0);\n        }\n    }\n\n\n}\nfunction runClearTimeout(marker) {\n    if (cachedClearTimeout === clearTimeout) {\n        //normal enviroments in sane situations\n        return clearTimeout(marker);\n    }\n    // if clearTimeout wasn't available but was latter defined\n    if ((cachedClearTimeout === defaultClearTimeout || !cachedClearTimeout) && clearTimeout) {\n        cachedClearTimeout = clearTimeout;\n        return clearTimeout(marker);\n    }\n    try {\n        // when when somebody has screwed with setTimeout but no I.E. maddness\n        return cachedClearTimeout(marker);\n    } catch (e){\n        try {\n            // When we are in I.E. but the script has been evaled so I.E. doesn't  trust the global object when called normally\n            return cachedClearTimeout.call(null, marker);\n        } catch (e){\n            // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error.\n            // Some versions of I.E. have different rules for clearTimeout vs setTimeout\n            return cachedClearTimeout.call(this, marker);\n        }\n    }\n\n\n\n}\nvar queue = [];\nvar draining = false;\nvar currentQueue;\nvar queueIndex = -1;\n\nfunction cleanUpNextTick() {\n    if (!draining || !currentQueue) {\n        return;\n    }\n    draining = false;\n    if (currentQueue.length) {\n        queue = currentQueue.concat(queue);\n    } else {\n        queueIndex = -1;\n    }\n    if (queue.length) {\n        drainQueue();\n    }\n}\n\nfunction drainQueue() {\n    if (draining) {\n        return;\n    }\n    var timeout = runTimeout(cleanUpNextTick);\n    draining = true;\n\n    var len = queue.length;\n    while(len) {\n        currentQueue = queue;\n        queue = [];\n        while (++queueIndex < len) {\n            if (currentQueue) {\n                currentQueue[queueIndex].run();\n            }\n        }\n        queueIndex = -1;\n        len = queue.length;\n    }\n    currentQueue = null;\n    draining = false;\n    runClearTimeout(timeout);\n}\n\nprocess.nextTick = function (fun) {\n    var args = new Array(arguments.length - 1);\n    if (arguments.length > 1) {\n        for (var i = 1; i < arguments.length; i++) {\n            args[i - 1] = arguments[i];\n        }\n    }\n    queue.push(new Item(fun, args));\n    if (queue.length === 1 && !draining) {\n        runTimeout(drainQueue);\n    }\n};\n\n// v8 likes predictible objects\nfunction Item(fun, array) {\n    this.fun = fun;\n    this.array = array;\n}\nItem.prototype.run = function () {\n    this.fun.apply(null, this.array);\n};\nprocess.title = 'browser';\nprocess.browser = true;\nprocess.env = {};\nprocess.argv = [];\nprocess.version = ''; // empty string to avoid regexp issues\nprocess.versions = {};\n\nfunction noop() {}\n\nprocess.on = noop;\nprocess.addListener = noop;\nprocess.once = noop;\nprocess.off = noop;\nprocess.removeListener = noop;\nprocess.removeAllListeners = noop;\nprocess.emit = noop;\nprocess.prependListener = noop;\nprocess.prependOnceListener = noop;\n\nprocess.listeners = function (name) { return [] }\n\nprocess.binding = function (name) {\n    throw new Error('process.binding is not supported');\n};\n\nprocess.cwd = function () { return '/' };\nprocess.chdir = function (dir) {\n    throw new Error('process.chdir is not supported');\n};\nprocess.umask = function() { return 0; };\n\n\n//# sourceURL=webpack:///./node_modules/process/browser.js?");

/***/ })

/******/ });