/* Minification failed. Returning unminified contents.
(16443,9-10): run-time error JS1010: Expected identifier: .
(16443,9-10): run-time error JS1195: Expected expression: .
(1259,15-23): run-time error JS1300: Strict-mode does not allow assignment to undefined variables: pauseAll
 */
(function () {
    'use strict';

    try {


        var getParameterByName = function (name, url) {
            if (!url) {
                url = window.location.href;
            }
            name = name.replace(/[\[\]]/g, "\\$&");
            var regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"),
                results = regex.exec(url);
            if (!results) return null;
            if (!results[2]) return '';
            return decodeURIComponent(results[2].replace(/\+/g, " "));
        }

        var getCookie = function(name) {
            var dc = document.cookie;
            var prefix = name + "=";
            var begin = dc.indexOf("; " + prefix);
            if (begin == -1) {
                begin = dc.indexOf(prefix);
                if (begin != 0) return null;
            }
            else {
                begin += 2;
                var end = document.cookie.indexOf(";", begin);
                if (end == -1) {
                    end = dc.length;
                }
            }

            return decodeURI(dc.substring(begin + prefix.length, end));
        }

        var setCookie = function (name, value, days) {
            var expires = "";
            if (days) {
                var date = new Date();
                date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
                expires = "; expires=" + date.toUTCString();
            }
            document.cookie = name + "=" + value + expires + "; path=/";
        }

        var debugParam = getParameterByName('debug');

        var debugcookieParam = getParameterByName('debugcookie');
        if (typeof (debugcookieParam) != 'undefined' && debugcookieParam != null && debugcookieParam) {
            //setup debug cookie for 1 month
            setCookie('enable-console', 'true', 31);
        }

        var consoleCookie = getCookie('enable-console');
        


        var enableConsole = (typeof (debugParam) != 'undefined' && debugParam != null && debugParam) || (typeof (consoleCookie) != 'undefined' && consoleCookie !== null);

        if (enableConsole) {
            window.debug = window.console;

            window.console = {
                log: function (log) {
                    if (typeof (window.debug) !== 'undefined') {
                        debug.log(arguments.length > 1 ? arguments : log);
                    }
                },
                warn: function (log) {
                    if (typeof (window.debug) !== 'undefined') {
                        debug.warn(arguments.length > 1 ? arguments : log);
                    }
                },
                error: function (log) {
                    if (typeof (window.debug) !== 'undefined') {
                        debug.error(arguments.length > 1 ? arguments : log);
                    }
                },
                info: function (log) {
                    if (typeof (window.debug) !== 'undefined') {
                        debug.info(arguments.length > 1 ? arguments : log);
                    }
                },
                debug: function (log) {
                    if (typeof (window.debug) !== 'undefined') {
                        debug.debug(arguments.length > 1 ? arguments : log);
                    }
                }
            }
        }
        else {
            window.console = {
                log: function (log) {
                },
                warn: function (log) {
                },
                error: function (log) {
                },
                info: function (log) {
                },
                debug: function (log) {
                }
            }
        }
    }
    catch (e) {
        if (typeof (window.console) !== 'undefined') {
            window.console.log(e);
        }
    }
})();
;
//try to avoid using this approach if possible and find a solution that works in IE
//however, sometimes needs must..

if (!String.prototype.endsWith) {
    String.prototype.endsWith = function (searchString, position) {
        var subjectString = this.toString();
        if (typeof position !== 'number' || !isFinite(position)
            || Math.floor(position) !== position || position > subjectString.length) {
            position = subjectString.length;
        }
        position -= searchString.length;
        var lastIndex = subjectString.indexOf(searchString, position);
        return lastIndex !== -1 && lastIndex === position;
    };
}

// https://tc39.github.io/ecma262/#sec-array.prototype.find
if (!Array.prototype.find) {
  Object.defineProperty(Array.prototype, 'find', {
    value: function(predicate) {
      // 1. Let O be ? ToObject(this value).
      if (this == null) {
        throw TypeError('"this" is null or not defined');
      }

      var o = Object(this);

      // 2. Let len be ? ToLength(? Get(O, "length")).
      var len = o.length >>> 0;

      // 3. If IsCallable(predicate) is false, throw a TypeError exception.
      if (typeof predicate !== 'function') {
        throw TypeError('predicate must be a function');
      }

      // 4. If thisArg was supplied, let T be thisArg; else let T be undefined.
      var thisArg = arguments[1];

      // 5. Let k be 0.
      var k = 0;

      // 6. Repeat, while k < len
      while (k < len) {
        // a. Let Pk be ! ToString(k).
        // b. Let kValue be ? Get(O, Pk).
        // c. Let testResult be ToBoolean(? Call(predicate, T, « kValue, k, O »)).
        // d. If testResult is true, return kValue.
        var kValue = o[k];
        if (predicate.call(thisArg, kValue, k, o)) {
          return kValue;
        }
        // e. Increase k by 1.
        k++;
      }

      // 7. Return undefined.
      return undefined;
    },
    configurable: true,
    writable: true
  });
}
;
window.EFL = window.EFL || {};
window.EFL.eventDispatcher = (function () {
    'use strict';
    var events = [];

    function addEvent(eventName, scopeId) {
        var event = typeof (scopeId) !== 'undefined' && typeof (scopeId) == 'string' ? scopeId + eventName : eventName;

        if (typeof (events[event]) === 'undefined') {
            events[event] = [];
        }
    }

    function clearEvent(eventName, scopeId) {
        var event = typeof (scopeId) !== 'undefined' && typeof (scopeId) == 'string' ? scopeId + eventName : eventName;
        events[event] = [];
    }

    function registerHandler(eventName, handler, scopeId) {
        var event = typeof (scopeId) !== 'undefined' && typeof (scopeId) == 'string' ? scopeId + eventName : eventName;
        addEvent(eventName, scopeId);
        events[event].push(handler);
    }

    function deregisterHandler(eventName, handler, scopeId) {
        var event = typeof (scopeId) !== 'undefined' && typeof (scopeId) == 'string' ? scopeId + eventName : eventName;
        var index = events[event].indexOf(handler);

        if (index !== -1) {
            events[event].splice(index, 1);
        }
    }

    function dispatch(eventName, arg, scopeId) {
        var dispatchSuccessful = false;
        var event = typeof (scopeId) !== 'undefined' && typeof (scopeId) == 'string' ? scopeId + eventName : eventName;

        if (typeof (events[event]) !== 'undefined') {

            for (var i = 0; i < events[event].length; i++) {
                events[event][i](arg);
            }

            if(events[event].length > 0){
                dispatchSuccessful = true;
            }
        }

        return dispatchSuccessful;
    }

    return {
        addEvent: addEvent,
        clearEvent: clearEvent,
        registerHandler: registerHandler,
        deregisterHandler: deregisterHandler,
        dispatch: dispatch
    };

})();
;
/*
Add mobile device-specific classes to the <html> tag
*/

(function ($) {
    'use strict';

    var init = function() {
        var device_class = '';

        // Add device-specific classes
        if (window.isMobile && window.isMobile.any) {
            // Nokia Lumia was incorrectly flagging apple and android as true
            if (window.isMobile.windows.device) {
                window.isMobile.android.device = false;
                window.isMobile.android.phone = false;
                window.isMobile.android.tablet = false;
                window.isMobile.apple.device = false;
                window.isMobile.apple.phone = false;
                window.isMobile.apple.tablet = false;
            }

            if (window.isMobile.apple.phone) {
                device_class = 'apple-device apple-phone';
            }

            else if (window.isMobile.apple.ipod) {
                device_class = 'apple-device apple-ipod';
            }

            else if (window.isMobile.apple.tablet) {
                device_class = 'apple-device apple-tablet';
            }

            else if (window.isMobile.android.phone) {
                device_class = 'android-device android-phone';
            }

            else if (window.isMobile.android.tablet) {
                device_class = 'android-device android-tablet';
            }

            else if (window.isMobile.windows.phone) {
                device_class = 'windows-device windows-phone';
            }

            else if (window.isMobile.windows.tablet) {
                device_class = 'windows-device windows-tablet';
            }

            $('html').addClass(device_class);
        }

        var isDesktopSafari = !!navigator.platform && /MacIntel/.test(navigator.platform) && !!navigator.userAgent && /Safari/.test(navigator.userAgent) && !/Chrome/.test(navigator.userAgent);

        if (isDesktopSafari) {
            $('html').addClass('desktop-safari');
        }
    };

    init();

})(window.jQuery);
;
window.EFL = window.EFL || {};

/* Requires Event Dispatcher */

/* Usage: var timer = new Timer($element, new Date(2020,05,04)); */

/* Options:
   scopeId - give the event dispatcher a scope to reduce bleeding into other widgets,
   onTickEventName - change the event that gets fired with event dispatcher on time tick
   onCompleteEventName - change the event that gets fired with event dispatcher on time complete
   onTick - event handler that gets called every time tick
   onComplete - event handler that gets called once the timer is complete
*/

window.EFL.Timer = (function () {
    return function (targetElement, targetDate, options) {
        var empty = '';
        var onTickEventName = 'on-timer-tick';
        var onCompleteEventName = 'on-timer-complete';

        options = options || {};
        options.scopeId = options.scopeId || empty;
        options.onTickEventName = options.onTickEventName || onTickEventName;
        options.onCompleteEventName = options.onCompleteEventName || onCompleteEventName;
        
        if (typeof (options.onTick) === 'function') {
            window.EFL.eventDispatcher.registerHandler(options.onTickEventName, options.onTick, options.scopeId)
        }

        if (typeof (options.onComplete) === 'function') {
            window.EFL.eventDispatcher.registerHandler(options.onCompleteEventName, options.onComplete, options.scopeId)
        }

        targetElement.first()
        .countdown(targetDate)
        .on('update.countdown', onTimeTick)
        .on('finish.countdown', onTimerComplete);

        function onTimeTick(event) {
            window.EFL.eventDispatcher.dispatch(options.onTickEventName, event, options.scopeId)
        }

        function onTimerComplete(event) {
            window.EFL.eventDispatcher.dispatch(options.onCompleteEventName, event, options.scopeId)
        }

        return {
            addTickListener: function (handler) {
                window.EFL.eventDispatcher.registerHandler(options.onTickEventName, handler, options.scopeId)
            },
            removeTickListener: function (handler) {
                window.EFL.eventDispatcher.deregisterHandler(options.onTickEventName, handler, options.scopeId)
            },
            addCompleteListener: function (handler) {
                window.EFL.eventDispatcher.registerHandler(options.onCompleteEventName, handler, options.scopeId)
            },
            removeCompleteListener: function (handler) {
                window.EFL.eventDispatcher.deregisterHandler(options.onCompleteEventName, handler, options.scopeId)
            }
        }
    }
})();;
//Function to return the current bootstrap state as a string
function findBootstrapEnvironment() {
    var envs = ["ExtraSmall", "Small", "Medium", "Large", "ExtraLarge"];
    var envValues = ["xs", "sm", "md", "lg", "xl"];

    var $el = $('<div>');
    $el.appendTo($('body'));

    for (var i = envValues.length - 1; i >= 0; i--) {
        var envVal = envValues[i];

        $el.addClass('hidden-' + envVal);
        if ($el.is(':hidden')) {
            $el.remove();
            return envVal;
        }
    }
};
/* 
 * These 2 letter codes are alpha-2 iso country codes
 * https://www.nationsonline.org/oneworld/country_code_list.htm
 * 
 * NeuLion use them so we can find the country of the user from a neulion config service call and this dictionary
 * 
 * Usage : countries.AF or countries["AF"] will return Afghanistan
 * 
 */

var countries = {
    "AF": "Afghanistan",
    "AX": "&#197;land Islands",
    "AL": "Albania",
    "DZ": "Algeria",
    "AS": "American Samoa",
    "AD": "Andorra",
    "AO": "Angola",
    "AI": "Anguilla",
    "AG": "Antigua and Barbuda",
    "AR": "Argentina",
    "AM": "Armenia",
    "AW": "Aruba",
    "AU": "Australia",
    "AT": "Austria",
    "AZ": "Azerbaijan",
    "BS": "Bahamas",
    "BH": "Bahrain",
    "BD": "Bangladesh",
    "BB": "Barbados",
    "BY": "Belarus",
    "BE": "Belgium",
    "BZ": "Belize",
    "BJ": "Benin",
    "BM": "Bermuda",
    "BT": "Bhutan",
    "BO": "Bolivia",
    "BQ": "Bonaire, Sint Eustatius and Saba",
    "BA": "Bosnia and Herzegovina",
    "BW": "Botswana",
    "BR": "Brazil",
    "IO": "British Indian Ocean Territory",
    "VG": "British Virgin Islands",
    "BN": "Brunei",
    "BG": "Bulgaria",
    "BF": "Burkina Faso",
    "BI": "Burundi",
    "CV": "Cabo Verde",
    "KH": "Cambodia",
    "CM": "Cameroon",
    "CA": "Canada",
    "KY": "Cayman Islands",
    "CF": "Central African Republic",
    "TD": "Chad",
    "CL": "Chile",
    "CN": "China",
    "CX": "Christmas Island",
    "CC": "Cocos (Keeling) Islands",
    "CO": "Colombia",
    "KM": "Comoros",
    "CG": "Congo",
    "CD": "Congo (DRC)",
    "CK": "Cook Islands",
    "CR": "Costa Rica",
    "CI": "C&#244;te d’Ivoire",
    "HR": "Croatia",
    "CU": "Cuba",
    "CW": "Cura&#231;ao",
    "CY": "Cyprus",
    "CZ": "Czechia",
    "DK": "Denmark",
    "DJ": "Djibouti",
    "DM": "Dominica",
    "DO": "Dominican Republic",
    "EC": "Ecuador",
    "EG": "Egypt",
    "SV": "El Salvador",
    "GQ": "Equatorial Guinea",
    "ER": "Eritrea",
    "EE": "Estonia",
    "ET": "Ethiopia",
    "FK": "Falkland Islands",
    "FO": "Faroe Islands",
    "FJ": "Fiji",
    "FI": "Finland",
    "FR": "France",
    "GF": "French Guiana",
    "PF": "French Polynesia",
    "GA": "Gabon",
    "GM": "Gambia",
    "GE": "Georgia",
    "DE": "Germany",
    "GH": "Ghana",
    "GI": "Gibraltar",
    "GR": "Greece",
    "GL": "Greenland",
    "GD": "Grenada",
    "GP": "Guadeloupe",
    "GU": "Guam",
    "GT": "Guatemala",
    "GG": "Guernsey",
    "GN": "Guinea",
    "GW": "Guinea-Bissau",
    "GY": "Guyana",
    "HT": "Haiti",
    "HN": "Honduras",
    "HK": "Hong Kong SAR",
    "HU": "Hungary",
    "IS": "Iceland",
    "IN": "India",
    "ID": "Indonesia",
    "IR": "Iran",
    "IQ": "Iraq",
    "IE": "Ireland",
    "IM": "Isle of Man",
    "IL": "Israel",
    "IT": "Italy",
    "JM": "Jamaica",
    "JP": "Japan",
    "JE": "Jersey",
    "JO": "Jordan",
    "KZ": "Kazakhstan",
    "KE": "Kenya",
    "KI": "Kiribati",
    "KR": "Korea",
    "XK": "Kosovo",
    "KW": "Kuwait",
    "KG": "Kyrgyzstan",
    "LA": "Laos",
    "LV": "Latvia",
    "LB": "Lebanon",
    "LS": "Lesotho",
    "LR": "Liberia",
    "LY": "Libya",
    "LI": "Liechtenstein",
    "LT": "Lithuania",
    "LU": "Luxembourg",
    "MO": "Macao SAR",
    "MK": "Macedonia, FYRO",
    "MG": "Madagascar",
    "MW": "Malawi",
    "MY": "Malaysia",
    "MV": "Maldives",
    "ML": "Mali",
    "MT": "Malta",
    "MH": "Marshall Islands",
    "MQ": "Martinique",
    "MR": "Mauritania",
    "MU": "Mauritius",
    "YT": "Mayotte",
    "MX": "Mexico",
    "FM": "Micronesia",
    "MD": "Moldova",
    "MC": "Monaco",
    "MN": "Mongolia",
    "ME": "Montenegro",
    "MS": "Montserrat",
    "MA": "Morocco",
    "MZ": "Mozambique",
    "MM": "Myanmar",
    "NA": "Namibia",
    "NR": "Nauru",
    "NP": "Nepal",
    "NL": "Netherlands",
    "NC": "New Caledonia",
    "NZ": "New Zealand",
    "NI": "Nicaragua",
    "NE": "Niger",
    "NG": "Nigeria",
    "NU": "Niue",
    "NF": "Norfolk Island",
    "KP": "North Korea",
    "MP": "Northern Mariana Islands",
    "NO": "Norway",
    "OM": "Oman",
    "PK": "Pakistan",
    "PW": "Palau",
    "PS": "Palestinian Authority",
    "PA": "Panama",
    "PG": "Papua New Guinea",
    "PY": "Paraguay",
    "PE": "Peru",
    "PH": "Philippines",
    "PN": "Pitcairn Islands",
    "PL": "Poland",
    "PT": "Portugal",
    "PR": "Puerto Rico",
    "QA": "Qatar",
    "RE": "R&#233;union",
    "RO": "Romania",
    "RU": "Russia",
    "RW": "Rwanda",
    "BL": "Saint Barth&#233;lemy",
    "KN": "Saint Kitts and Nevis",
    "LC": "Saint Lucia",
    "MF": "Saint Martin",
    "PM": "Saint Pierre and Miquelon",
    "VC": "Saint Vincent and the Grenadines",
    "WS": "Samoa",
    "SM": "San Marino",
    "ST": "S&#227;o Tom&#233; and Pr&#237;ncipe",
    "SA": "Saudi Arabia",
    "SN": "Senegal",
    "RS": "Serbia",
    "SC": "Seychelles",
    "SL": "Sierra Leone",
    "SG": "Singapore",
    "SX": "Sint Maarten",
    "SK": "Slovakia",
    "SI": "Slovenia",
    "SB": "Solomon Islands",
    "SO": "Somalia",
    "ZA": "South Africa",
    "SS": "South Sudan",
    "ES": "Spain",
    "LK": "Sri Lanka",
    "SH": "St Helena, Ascension, Tristan da Cunha",
    "SD": "Sudan",
    "SR": "Suriname",
    "SJ": "Svalbard and Jan Mayen",
    "SZ": "Swaziland",
    "SE": "Sweden",
    "CH": "Switzerland",
    "SY": "Syria",
    "TW": "Taiwan",
    "TJ": "Tajikistan",
    "TZ": "Tanzania",
    "TH": "Thailand",
    "TL": "Timor-Leste",
    "TG": "Togo",
    "TK": "Tokelau",
    "TO": "Tonga",
    "TT": "Trinidad and Tobago",
    "TN": "Tunisia",
    "TR": "Turkey",
    "TM": "Turkmenistan",
    "TC": "Turks and Caicos Islands",
    "TV": "Tuvalu",
    "UM": "U.S. Outlying Islands",
    "VI": "U.S. Virgin Islands",
    "UG": "Uganda",
    "UA": "Ukraine",
    "AE": "United Arab Emirates",
    "GB": "United Kingdom",
    "US": "United States",
    "UY": "Uruguay",
    "UZ": "Uzbekistan",
    "VU": "Vanuatu",
    "VA": "Vatican City",
    "VE": "Venezuela",
    "VN": "Vietnam",
    "WF": "Wallis and Futuna",
    "YE": "Yemen",
    "ZM": "Zambia",
    "ZW": "Zimbabwe",
};;
// Helper function for working with cookies and local storage
// Provides the same get/set/remove functions for either storage method
// expires should be passed as a duration in milliseconds (use time.minute, etc to help)

// For cookies: https://github.com/carhartl/jquery-cookie
// For local storage: http://www.jstorage.info/

window.EFL = window.EFL || {};
window.EFL.local = (function () {
    'use strict';

    return {
        cookies: {
            get: $.cookie,
            set: function(key, value, expires) {
                var options = { path: '/' };

                if (expires) {
                    var expiry = new Date();
                    expiry.setTime(expiry.getTime() + expires)
                    options.expires = expiry;
                }

                $.cookie(key, value, options);
            },
            remove: function(key) {
                $.removeCookie(key, { path: '/' });
            }
        },
        storage: {
            get: function(key) {
                var result = $.jStorage.get(key);

                return (result == null) ? undefined : result;
            },
            set: function(key, value, expires) {
                var options = {};

                if (expires) {
                    options.TTL = expires;
                }

                $.jStorage.set(key, value, options);
            },
            remove: $.jStorage.deleteKey
        },
        time: {
            minute: 1000 * 60,
            hour:   1000 * 60 * 60,
            day:    1000 * 60 * 60 * 24
        }
    };
})();;
/* Global analytics controller which orchestrates pushing into the datalayer from across the code base */

/*To use, either use one of the functions below or attach the [data-analytics-track] or [data-analytics-page-view] attributes to a DOM element for automatic tracking.
With [data-analytics-track] passing any value will assume it is an event and bind it to the event based tracking, for example [data-analytics-track="click"]. */

/* Example use cases */

/*
// attach click handler to elements (reads data attributes - data-track-event, data-track-category, data-track-action, data-track-label)
window.EFL.analyticsController.attach($('[data-analytics-track]'), 'click');

//attach with specific keypress, but otherwise same as above
window.EFL.analyticsController.attachKeyPress($('[data-analytics-track]'), 13);

//track from code
window.EFL.analyticsController.track({event:'a', category:'b', action:'c', label:'d'});

//track with eventDispatcher
window.EFL.eventDispatcher.dispatch('analytics-track', {event:'a', category:'b', action:'c', label:'d'});

//track virtual page view with code
window.EFL.analyticsController.pageView('sign-in/success', 'Sign-in-Form-Success');

// track virtual page view from DOM
<div data-analytics-page-view="true" data-virtualpageuRL="sign-in/success" data-virtualpagetitle="Sign-in-Form-Success"></div>
*/

window.EFL = window.EFL || {};
window.EFL.analyticsController = (function () {
    window.dataLayer = window.dataLayer || [];
    var enterKeyCode = 13;
    var analyticsTrigger = '[data-analytics-track]';
    var analyticsPageViewTrigger = '[data-analytics-page-view]';

    function attachEventBasedTracking(elements, eventType) {
        elements.each(function (index, element) {
            element = $(element);
            attachTracking(element, eventType);
        });
    }

    /* May be redundant and handled by click, but included just in case */
    function attachKeyPressBasedTracking(elements, eventkeyCode) {
        elements.each(function (index, element) {
            element = $(element);
            attachKeyPressTracking(element, eventkeyCode);
        });
    }

    function attachTracking(element, eventType) {
        if (typeof (eventType) !== 'undefined') {
            element.off(eventType, trackEvent).on(eventType, trackEvent);
        }
    }

    function attachKeyPressTracking(element, eventkeyCode) {
        if (typeof (eventType) !== 'undefined') {
            element.off("keypress").on(eventType, function (event) {
                if (event.which == enterKeyCode) {
                    trackEvent();
                }
            });
        }
    }

    function trackEvent() {
        var element = $(this);
        track(element);
    }

    function track(element) {
        var data = {};
        data.event = element.data('trackEvent') || '',
            data.category = element.data('trackCategory') || '',
            data.action = element.data('trackAction') || '',
            data.label = element.data('trackLabel') || '';

        analyticsTrack(data);
    }

    function analyticsTrack(data, dataLayer) {
        dataLayer = dataLayer || window.dataLayer;
        dataLayer.push(data);
    }

    function trackVirtualPageView(virtualPageURL, virtualPageTitle, eventName, dataLayer) {
        var data, tagsFired;

        virtualPageURL = virtualPageURL.toString();

        giftStatus = GetGiftStatus();

        eventName = eventName || 'content-view';
        dataLayer = dataLayer || window.dataLayer;

        // If the virtual url doesn't start with a slash, then make it relative to the current URL
        if (virtualPageURL.charAt(0) !== '/') {
            var pathname = window.location.pathname;
            if (pathname.charAt(pathname.length - 1) !== "/") {
                pathname = pathname + "/";
            }
            virtualPageURL = pathname + virtualPageURL;
        }

        data = {
            'event': eventName,
            'virtualPageURL': virtualPageURL || '',
            'virtualPageTitle': virtualPageTitle || ''
        };

        if (giftStatus.length > 0) {
            data['gift-status-hit'] = giftStatus;
        }

        tagsFired = dataLayer.push(data);
        tagsFired = tagsFired || (!isNaN(parseFloat(tagsFired)) && isFinite(tagsFired));

        if (tagsFired) {
            console.log("analyticsController trackVirtualPageView " + data.virtualPageURL);
        }
        else {
            console.log("analyticsController trackVirtualPageView " + data.virtualPageURL + " did not fire any tags");
        }
    }

    function GetGiftStatus() {
        var url = window.location.href;
        if (url.indexOf('register=gift') > 0 && url.indexOf('code=') > 0) {
            return "gift-redemption";
        }
        else if (url.indexOf('register=gift') > 0) {
            return "gift-payment";
        }
        else if (url.indexOf('register=true') > 0) {
            return "(not set)";
        }
        else {
            return "";
        }
    }

    function trackVirtualPageViewFromAttributes(element) {
        trackVirtualPageView(element.data('virtualpageurl') || '', element.data('virtualpagetitle') || '');
    }

    // Some DOM element should only trigger tracking events when they are seen by the user
    var elementTracking = {
        elements: [],

        add: function (element) {
            this.elements.push(element);
        },

        checkAllElements: function () {

            for (var x = 0; x < elementTracking.elements.length; x++) {
                elementTracking.checkElement(elementTracking.elements[x]);
            }
            
        },

        checkElement: function (element) {
            function isElementHidden(el) {
                return ($(el)[0].offsetParent === null);
            }

            function elementInViewport(element) {
                var $element = $(element);

                var viewportTop = $(window).scrollTop();
                var viewportBottom = viewportTop + $(window).height();

                var elementTop = $element.offset().top;
                var elementBottom = elementTop + $element.height();

                return ((elementBottom <= viewportBottom) && (elementTop >= viewportTop));
            }

            // Check the element is both visible and in the viewport
            if (!isElementHidden(element) && elementInViewport(element)) {

                var shouldTrack = true;

                if (window.EPiCookiePro !== null && typeof window.EPiCookiePro !== 'undefined' && !EPiCookiePro.IsCookieCategoryAllowed(EPiCookiePro.ConsentCategories.Performance)) {
                    shouldTrack = false;
                }

                if (shouldTrack) {
                    track(element);

                    // Remove it from the array so we don't check it again
                    var index = this.elements.indexOf(element);
                    if (index > -1) {
                        this.elements.splice(index, 1);
                    }
                }

                
            }
        },

        init: function () {
            if (this.elements.length > 0) {
                $(window).on('throttled-resize', elementTracking.checkAllElements);
                $(window).on('scroll', elementTracking.checkAllElements);
                $(window).on('element-tracking-event', elementTracking.checkAllElements);
                if (window.EPiCookiePro !== null && typeof window.EPiCookiePro !== 'undefined' && EPiCookiePro.IsCookieCategoryAllowed(EPiCookiePro.ConsentCategories.Performance)) {
                    elementTracking.checkAllElements();
                }
            }
        }
    };

    //hook up event based tracking
    window.EFL.eventDispatcher.registerHandler('analytics-track', analyticsTrack);

    $(function () {
        $(analyticsPageViewTrigger).each(function (index, value) {
            var element = $(value);
            trackVirtualPageViewFromAttributes(element);
        });

        $(analyticsTrigger).each(function (index, value) {
            var element = $(value);
            var type = element.data('analyticsTrack');

            // elements can trigger events immediately (but not if they're hidden with CSS)
            if ((typeof type === 'undefined' || type === '')) {
                elementTracking.add(element);
            } else {
                attachEventBasedTracking(element, type);
            }
        });

        elementTracking.init();
    });

    //hook up object based tracking
    return {
        attach: attachEventBasedTracking,
        attachKeyPress: attachKeyPressBasedTracking,
        track: analyticsTrack,
        pageView: trackVirtualPageView,
        giftStatus: GetGiftStatus
    };
})();;
window.EFL = window.EFL || {};

/*Note: If a property doesn't exists in the JSON its likely that its being dynamically populated */

window.EFL.analyticsReference = {
    analyticsRef0: { event: "test-event", category: "test-category", action: "test-action", label: "test-label" },
    analyticsRef1: { event: "video-engagement", category: "VideoEngagement" }, //action -  name of video, label - play %
    analyticsRef3: { event: "offsite-links", category: "Offsite Links" },//action -  name , label - url
    analyticsRef4: { event: "download-collateral", category: "Download Collateral" },//action -  type , label - name
    analyticsRef9: { event: "accordion-interaction", category: "Accordion Interaction" }
}
;
(function ($) {
    'use strict';

    var settings = {
        downloadPDFTriggers: '.link-icon-PDF',
        searchValue: 'data-search-value'
    };

    $(settings.downloadPDFTriggers).on('click', function (element) {
        var link = $(this);
        var label = link.attr('href');
        window.EFL.analyticsController.track({ event: 'download-collateral', category: 'Download Collateral', action: 'PDF', label: label });
    });

    $('body').on('shown.bs.tab', function (event) {
        var title = $(event.target).data('title');
  
        if (typeof (title) !== 'undefined' && (event.relatedTarget !== undefined)) {
            window.EFL.analyticsController.pageView(window.location.pathname + title, title);
        }
    });

    $("a:not([data-track-event], [data-dropdown-trigger])").on("click", function (e) { trackLink(e, this); });

})(window.jQuery);

function trackLink(e, target) {
    var link = $(target);

    var baseURI = window.location.host;

    var openLink = function (href) {
        var isEmail = href.indexOf('mailto:') >= 0;
        var isTelephone = href.indexOf('tel:') >= 0;

        if (isEmail || isTelephone) {
            window.location.href = href;
        }
        else {
            window.open(href);
        }
    };

    var openLinkInSameWindowWithDelay = function (href) {
        setTimeout(function () { window.location.href = href; }, 200);
    };

    // abandon if no active link or link within domain
    if (link.parent().hasClass('partner-logo')) return;
    if (link.length !== 1 || baseURI === link[0].host) return;

    var doNotTreatAsExternal = false;

    if (typeof notExternal !== 'undefined') {
        var i;
        for (i = 0; i < notExternal.length; i++) {
            if (notExternal[i] === link[0].host) {
                doNotTreatAsExternal = true;
            }
        }
    }

    // cancel event and record outbound link
    e.preventDefault();

    var href = link[0].href;
    var title = link.attr('title') || 'label-not-set';

    window.EFL.analyticsController.track({ event: 'offsite-links', category: 'Offsite Links', action: title, label: href });

    if (!doNotTreatAsExternal) {
        openLink(href);
    }
    else {
        if (link[0].target === '_blank') {
            //open link straight away.. its in a new window so don't need to worry about track event above not completing
            openLink(href);
        }
        else {
            //open with a delay to allow track event above to complete
            openLinkInSameWindowWithDelay(href);
        }
    }
};
//Returns 1 if we're in UK Summer Time (GMT+1)
//Returns 0 if we're in Winter time (GMT+0)
function isBSTinEffect(date) {
    if (date == undefined) {
        //base off today
        var d = new Date();
    }
    else
    {
        //base off the passed in date
        d = date;
    }

    // Loop over the 31 days of March for the current year
    for (var i = 31; i > 0; i--) {
        var tmp = new Date(d.getFullYear(), 2, i);

        // If it's Sunday
        if (tmp.getDay() == 0) {
            // last Sunday of March
            lSoM = tmp;

            // And stop the loop
            break;
        }
    }

    // Loop over the 31 days of October for the current year
    for (var i = 31; i > 0; i--) {
        var tmp = new Date(d.getFullYear(), 9, i);

        // If it's Sunday
        if (tmp.getDay() == 0) {
            // last Sunday of October
            lSoO = tmp;

            // And stop the loop
            break;
        }
    }

    // 0 = DST off (GMT)
    // 1 = DST on  (BST)
    if (d < lSoM || d >= lSoO) return 0;
    else return 1;
};
$("[data-recaptcha-enabled]").submit(function (e) {
    var $form = $(this);
    var $token = $form.find("#RecaptchaToken");

    if ($token.val()) {
        return true;
    }

    e.preventDefault();
    e.stopPropagation();
    grecaptcha.ready(function () {
        grecaptcha.execute(window.RECAPTCHA_SITE_KEY, { action: 'submit' }).then(function (token) {
            $token.val(token);
            $form.submit();
        });
    });
});;
/* eslint-disable indent */
/* MatchCentre orchestrates the Opta widgets throughout the site.

Please see https://sites.google.com/a/realise.com/english-football-league/home/front-end/match-centre/match-centre-js for futher information and usage */

window.EFL = window.EFL || {};
window.EFL.MatchCentre = (function ($) {
    "use strict";
    //console.info("Match centre JS init");

    var _widgets = [];

	var body = $('body');

	var hash = window.location.hash;;

    var widgetsPausedGlobally = false;
    var pauseRunning = false;

    var kickoff, isInBuildUp, thirtyMinsPrior, sixtyMinsPrior, scrollDistance;

    var loadNewWidgets = function (element) {
        var opta_widget_tags = $(element).find('opta-widget[load="false"]');

        if (opta_widget_tags.length) {
            opta_widget_tags.removeAttr('load');
            Opta.start();
        }
    },
        resumeFocusedWidgets = function (element) {
            if (widgetsPausedGlobally) {
                return;
            }
          var widget_containers = $(element).find('.Opta');

          widget_containers.each(function () {
              var element = $(this);
              var widget_id = element.attr('id');
              if (widget_id && Opta && Opta.widgets) {
                  var Widget = Opta.widgets[widget_id];
                  Widget.resume();
                  console.info("Resumed", Widget.attr.widget);
              }
          });
        },
        getWidgetsPausedGlobally = function () {
            return widgetsPausedGlobally;
        },
        toggleWidgets = function () {
            // get status display elements, this will be updated for the user
            var displayStatus = $(this).parent('.control-header').find('.enable-stats-state');
            var enabledStatus = displayStatus.find('.enabled');
            var disabledStatus = displayStatus.find('.disabled');

            if (widgetsPausedGlobally)
            {
                resumeAllWidgets();
                // update the status display for the user
                disabledStatus.addClass('hidden');
                enabledStatus.removeClass('hidden');
            }
            else
            {
                pauseAllWidgets();
                // update the status display for the user
                enabledStatus.addClass('hidden');
                disabledStatus.removeClass('hidden');
            }
            return true;
        },

        resumeAllWidgets = function () {
            var widget_containers = $('.Opta');
            widgetsPausedGlobally = false;
            widget_containers.each(function () {
                var element = $(this);
                if (!element.is(':visible'))
                {
                    //widget is on a hidden element so skip and don't resume it
                    return true;
                }
                var widget_id = element.attr('id');
                if (widget_id && Opta) {
                    var Widget = Opta.widgets[widget_id];
                    //console.log("resuming", widget_id);
                    Widget.resume();
                }
                console.info("Resumed", Widget.attr.widget);
            });
        },
        pauseAllWidgets = function () {
            var widget_containers = $('.Opta');
            widgetsPausedGlobally = true;
            widget_containers.each(function () {
                var element = $(this);
                if (!element.is(':visible')) {
                    //widget is on a hidden element so no need to pause it
                    return true;
                }
                var widget_id = element.attr('id');
                if (widget_id && Opta) {
                    var Widget = Opta.widgets[widget_id];
                    //console.log("pausing", widget_id);
                    Widget.pause();
                }
                console.info("Paused", Widget.attr.widget);
            });
        },
      pauseHiddenWidgets = function (element) {
          var widget_containers = $(element).find('.Opta');

          widget_containers.each(function () {
              var element = $(this);
              var widget_id = element.attr('id');
              if (widget_id && Opta) {
                  var Widget = Opta.widgets[widget_id];

                  Widget.pause();
              }
              console.info("Paused", Widget.attr.widget);
          });
      },
      resizeImages = function (element) {
          //replace all 20 pixel imagery with higher quality images - global
          element.find('.Opta-Crest img, .Opta-Image img, .Opta-Team-Crest img')
            .each(function (index, element) {
                element = $(element);
                var src = element.attr('src');

                if (src.indexOf('dimensions=20') !== -1) {
                    src = src.replace('dimensions=20', 'dimensions=150');
                    element.attr('src', src);
                }
                if (src.indexOf('dimensions=65') !== -1) {
                    src = src.replace('dimensions=65', 'dimensions=150');
                    element.attr('src', src);
                }
            });

      },
      registerWidget = function (id, element, onDrawn, onError) {
          _widgets.push({
              id: id,
              element: element,
              onDrawn: onDrawn,
              onError: onError || function () {
                  //console.log()
              }
          });
      },
      onWidgetDrawn = function (optaItem) {
          var id = optaItem.widget.attr_original['data-widget-id'];

          for (var i = 0; i < _widgets.length; i++) {
              if (_widgets[i].id === id) {
                  _widgets[i].onDrawn(_widgets[i].element, optaItem);
                  resizeImages(_widgets[i].element);
              }
          }
          if (widgetsPausedGlobally && !pauseRunning) {
              pauseRunning = true;
              pauseAll = setTimeout(function () {
                  pauseAllWidgets();
                  pauseRunning = false;
              }, 1000);
              
          }
      },
        onWidgetError = function (optaItem) {
            console.error("match centre - onWidgetError: " + new Date());
            console.error(optaItem);

            var id = optaItem.widget.attr_original['data-widget-id'];

            for (var i = 0; i < _widgets.length; i++) {
                if (_widgets[i].id === id) {
                    _widgets[i].onError(_widgets[i].element, optaItem);
                }
            }
        };


    //Wire up events
    if (typeof (Opta) !== 'undefined') {
        Opta.events.subscribe('widget.drawn', onWidgetDrawn);
        Opta.events.subscribe('widget.error', onWidgetError);
    } else {
        console.log('Opta unavailable');
    }

    body.on('shown.bs.tab', function (event) {
        
        // if there's a hash and a tab that matches, load this instead of initial tab
		if (typeof (hash) !== 'undefined'
			&& hash !== ""
			&& $(hash).find('opta-widget').length > 0
			&& $('.nav-tabs a[href="' + hash + '"]:visible').length > 0)
		{   
			var target = $(hash);

			//reset hash to not always load it
			hash = undefined;


            // set 'opta-widget[load]' to true
            if (!widgetsPausedGlobally) {
                $(target).find('opta-widget').attr('load', true);
            }
        } else {
            var target = $($(event.target).attr('href'));
        }      

        if (!widgetsPausedGlobally) {
            loadNewWidgets(target);
            resumeFocusedWidgets(target);
        }

        if (window.EFL.adition && window.EFL.adition.functions) {
            window.EFL.adition.functions.check_for_instances();
            window.EFL.eventDispatcher.dispatch('update-adition-scroll-lock');
        }
    });
    body.on('hidden.bs.tab', function (event) {
        var target = $($(event.target).attr('href'));
        pauseHiddenWidgets(target);
    });

    $('.main.match-centre .nav-tabs').on('shown.bs.tab', 'a[data-toggle="tab"]', function (e) {
        if ($('body').hasClass('sticky-hero')) {
            $('html, body').animate({
                scrollTop: $('.scroll-lock-trigger').offset().top - $('.header-wrap').height()
            }, 200);
        }
    });

    //trigger any initial behaviour on initial tab such as analytics
    $('li.active>a[data-toggle="tab"]').each(function (index, value) {
        $(value).trigger("shown.bs.tab");
    });

    //actions based on time up until kickoff
    $(function () {
        kickoff = new Date(parseInt($('#ko').val()));

        if (isInBuildUp = checkIsInBuildUp()) {
            setBuildUpState();
        }

        if (thirtyMinsPrior = thirtyMinuteCheck()) {
            setLiveState();
        }
        if (sixtyMinsPrior = sixtyMinuteCheck()) {
            setLiveState();
        }

        if (typeof (kickoff) !== "undefined") {
            window.EFL.eventDispatcher.dispatch("on-kickoff-set", kickoff);

            $('[data-countdown="kickoff"]')
            .first()
            .countdown(kickoff)
            .on('update.countdown', onTimeTick)
            .on('finish.countdown', onTimerComplete);
        }
    });


    function onTimeTick(event) {

        if (!isInBuildUp) {
            if (isInBuildUp = checkIsInBuildUp()) {
                setBuildUpState();
            }
        }
        if (!thirtyMinsPrior) {
            if (thirtyMinsPrior = thirtyMinuteCheck()) {
                setLiveState();
            }
        }

        if (!sixtyMinsPrior) {
            if (sixtyMinsPrior = sixtyMinuteCheck()) {
                setLiveState();
            }
        }
        

        window.EFL.eventDispatcher.dispatch("on-kickoff-countdown-tick", { "kickoff": kickoff, "event": event });
    }

    function onTimerComplete(event) {
        if (!body.hasClass('latestscores-disabled')) {
            body.addClass('latestscores');
        }
        if (!body.hasClass('livecommentary-disabled')) {
            body.addClass('livecommentary');
        }
        if (!body.hasClass('lineup-disabled')) {
            body.addClass('lineup');
        }
        if (!body.hasClass('stats-disabled')) {
            body.addClass('stats');
        }
        if (!body.hasClass('latesttable-disabled')) {
            body.addClass('latesttable');
        }

        window.EFL.eventDispatcher.dispatch("on-kickoff-countdown-complete", { "kickoff": kickoff, "event": event });
    }

    function checkIsInBuildUp() {
        return isOnSameDay(new Date(), kickoff) && isAfterHour(new Date(), 9);
    }

    function sixtyMinuteCheck() {
        return IsLessThanKickOff(kickoff,60);

    } 
    function thirtyMinuteCheck() {
        return IsLessThanKickOff(kickoff,30);

    }

    function setBuildUpState() {
        if (isInBuildUp) {
            if (!body.hasClass('livecommentary-disabled')) {
                body.addClass('livecommentary');
            }
        }
    }

    function setLiveState() {
        if (thirtyMinsPrior) {
            if (!body.hasClass('stats-disabled')) {
                body.addClass('stats');
            }
        }
        if (sixtyMinsPrior) {
            if (!body.hasClass('lineup-disabled')) {
                body.addClass('lineup');
            }
        }
    }

    function isOnSameDay(a, b) {
        if (typeof (a) !== 'undefined' && typeof (b) !== 'undefined') {
            return a.getDate() === b.getDate()
                && a.getMonth() === b.getMonth()
                && a.getFullYear() === b.getFullYear();
        }
    }

    function isAfterHour(date, hour) {
        if (typeof (date) !== 'undefined' && typeof (hour) !== 'undefined') {
            return date.getHours() > hour;
        }
    }

    function IsLessThanKickOff(date, minsBefore) {
        var MS_PER_MINUTE = 60000;
        var priorToKickoff = new Date(date - minsBefore * MS_PER_MINUTE);
        return new Date() > priorToKickoff;
    }

    function ensureDoubleDigits(value) {
        if (value.toString().length < 2) {
            value = '0' + value;
        }
        return value;
    }

    //poll for match report until its available
    var pollForMatchReportTimeout;
    var body = $('body');
    function pollForMatchReport() {
        clearTimeout(pollForMatchReportTimeout);
        if (body.hasClass('match-centre') && !body.hasClass('matchreport')) {

            var location = window.location.href.split('?')[0]
            location = location.split('#')[0];

            if (location.substr(location.length - 1) !== '/') {
                location = location + '/';
            }

			var url = location + 'GetMatchReport';

			var fetch = $.ajax({
				'method': 'GET',
				'url': url,
			});

			fetch.done(function (report, textStatus, jqXHR) {
					if (report !== 'false') {
						$('#report').append(report);
						if (!body.hasClass('matchreport-disabled')) {
							body.addClass('matchreport');
						}
						body.addClass('asithappened');
						body.removeClass('preview');
						body.removeClass('tickets');
						if (!body.hasClass('matchreport-disabled')) {
							$('.match-report-tab a').tab('show');
						}
						$('.live-tab a').attr('data-title', 'as-it-happened');
						window.EFL.galleries.loadGalleries();
					}
					else {
						pollForMatchReportTimeout = setTimeout(pollForMatchReport, 30 * 1000);
					}
			});

			fetch.fail(function (jqXHR, textStatus, errorThrown) {
				console.log({
					errorThrown: errorThrown,
					statusText: textStatus,
					status: jqXHR.status,
					response: jqXHR.responseJSON
				});
			});
        }
    }

    if ($('#report').length > 0 && $('#report *').length == 0) {
        pollForMatchReport();
    }

    //poll for match report until its available
    var pollForManualLineupTimeout;
    var body = $('body');
    function pollForManualLineup() {
        clearTimeout(pollForManualLineupTimeout);
        if (body.hasClass('match-centre') && !body.hasClass('lineup-loaded') && $('#lineup .lineup-container').length > 0) {

            var location = window.location.href.split('?')[0]
            location = location.split('#')[0];

            if (location.substr(location.length - 1) !== '/') {
                location = location + '/';
            }

            var url = location + 'GetManualLineup';

            $.get(url, function (lineup) {
                if (lineup !== 'false') {
                    $('#lineup .lineup-container').empty().append(lineup);
                    body.addClass('lineup-loaded');
                    $('.lineup-tab a').tab('show');
                }
                else {
                    pollForManualLineupTimeout = setTimeout(pollForManualLineup, 10 * 1000);
                }
            });
        }
    }
    if ($('#lineup .lineup-container .widget-lineup').length == 0) {
        pollForManualLineup();
    }

    //deeplinking
    if (location.hash) {
        $('.nav-tabs a').tab();
        var tab = $('.nav-tabs a[href="' + location.hash + '"]');
        if (tab.parent(':visible').length == 1) {
            tab.tab('show');
            //brute force
            $(".tab-content > .active").removeClass("active").addClass("fade");
            var selector = tab.data('target');
            if (!selector) {
                selector = tab.attr('href');
                selector = selector && selector.replace(/.*(?=#[^\s]*$)/, ''); // strip for ie7
            }
            var $target = $(selector);
            $target.addClass("active").removeClass("fade");
        }

        // reset hash to allow future clicked on tabs to load widgets correctly
        if (body.hasClass('match-centre') && !body.hasClass('home')) {
            //window.location.hash = '';
        }
    }

    //console.info("match centre: " + new Date());

    // toggle the widgets from running when the user clicks the switch within the video container
    $('.video-container').on('click', '.switch-checkbox', toggleWidgets);

    return {
        registerWidget: registerWidget,
        resizeImages: resizeImages,
        pauseWidget: pauseHiddenWidgets,
        pauseWidgets: pauseAllWidgets,
        resumeWidget: resumeFocusedWidgets,
        resumeWidgets: resumeAllWidgets,
        toggleStats: toggleWidgets,
        areWidgetsPaused: getWidgetsPausedGlobally
    };


})(window.jQuery);
;
(function ($) {
    'use strict';

    var settings = {
        trigger: '[data-switch-checkbox=""]',
        changeEvent: 'switch-change'
    };

    function init(index, element) {
        var element = $(this); // DOM element
        var scopeId = element.data('id');
        var label = element.find('label');
        var input = element.find('input');
        var isChecked = input.is(':checked');
        var screenReaderLabel = element.find('.js-toggle-status');

        window.EFL.eventDispatcher.registerHandler('switch-changed', change, scopeId);

        //allow clicking entire control for toggle

        function onClick(event) {
            if (event.target.nodeName.toLowerCase() === 'label' || event.target.nodeName.toLowerCase() === 'a' || $(event.target).hasClass('key-moments-description')) {
                activate();
            }
            triggerChangeEvent();

            event.preventDefault();
        }

        element.on('click', onClick);
        element.parent().find('.key-moments-description').on('click', onClick);
        element.on('keypress', function (event) {
            if (event.which == 13 || event.keyCode == 13) {
                activate();
                triggerChangeEvent();
                event.preventDefault();
            }
        });

        function activate() {
            input.prop('checked', !isChecked);
            isChecked = input.is(':checked');
        }

        function change(isSwitched) {
            isChecked = isSwitched;
            input.prop('checked', isChecked);
            triggerChangeEvent();
        }

        function triggerChangeEvent() {

            if (input.is(':checked')) {
                element.addClass('checked');
                screenReaderLabel.empty().append("key moments filter active");
            }
            else {
                element.removeClass('checked');
                screenReaderLabel.empty().append("key moments filter removed");
            }

            element.trigger(settings.changeEvent, [input.is(':checked')]);
        }

        window.EFL.eventDispatcher.dispatch('switch-ready', null, scopeId);
    }

    $(function () {
        $(settings.trigger).each(init);
    })
})(jQuery);
;
(function(cc) {
  // stop from running again, if accidently included more than once.
  if (cc.hasInitialised) return;

  var util = {
    // http://stackoverflow.com/questions/3446170/escape-string-for-use-in-javascript-regex
    escapeRegExp: function(str) {
      return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&');
    },

    hasClass: function(element, selector) {
      var s = ' ';
      return element.nodeType === 1 &&
        (s + element.className + s).replace(/[\n\t]/g, s).indexOf(s + selector + s) >= 0;
    },

    addClass: function(element, className) {
      element.className += ' ' + className;
    },

    removeClass: function(element, className) {
      var regex = new RegExp('\\b' + this.escapeRegExp(className) + '\\b');
      element.className = element.className.replace(regex, '');
    },

    interpolateString: function(str, callback) {
      var marker = /{{([a-z][a-z0-9\-_]*)}}/ig;
      return str.replace(marker, function(matches) {
        return callback(arguments[1]) || '';
      })
    },

    getCookie: function (name) {
      return window.EFL.local.cookies.get(name);
    },

    setCookie: function (name, value, expiryDays) {
      window.EFL.local.cookies.set(name, value, (expiryDays || 365) * window.EFL.local.time.day)
    },

    // only used for extending the initial options
    deepExtend: function(target, source) {
      for (var prop in source) {
        if (source.hasOwnProperty(prop)) {
          if (prop in target && this.isPlainObject(target[prop]) && this.isPlainObject(source[prop])) {
            this.deepExtend(target[prop], source[prop]);
          } else {
            target[prop] = source[prop];
          }
        }
      }
      return target;
    },

    // only used for throttling the 'mousemove' event (used for animating the revoke button when `animateRevokable` is true)
    throttle: function(callback, limit) {
      var wait = false;
      return function() {
        if (!wait) {
          callback.apply(this, arguments);
          wait = true;
          setTimeout(function() {
            wait = false;
          }, limit);
        }
      }
    },

    // only used for hashing json objects (used for hash mapping palette objects, used when custom colours are passed through JavaScript)
    hash: function(str) {
      var hash = 0,
        i, chr, len;
      if (str.length === 0) return hash;
      for (i = 0, len = str.length; i < len; ++i) {
        chr = str.charCodeAt(i);
        hash = ((hash << 5) - hash) + chr;
        hash |= 0;
      }
      return hash;
    },

    normaliseHex: function(hex) {
      if (hex[0] == '#') {
        hex = hex.substr(1);
      }
      if (hex.length == 3) {
        hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2];
      }
      return hex;
    },

    // used to get text colors if not set
    getContrast: function(hex) {
      hex = this.normaliseHex(hex);
      var r = parseInt(hex.substr(0, 2), 16);
      var g = parseInt(hex.substr(2, 2), 16);
      var b = parseInt(hex.substr(4, 2), 16);
      var yiq = ((r * 299) + (g * 587) + (b * 114)) / 1000;
      return (yiq >= 128) ? '#000' : '#fff';
    },

    // used to change color on highlight
    getLuminance: function(hex) {
      var num = parseInt(this.normaliseHex(hex), 16), 
          amt = 38,
          R = (num >> 16) + amt,
          B = (num >> 8 & 0x00FF) + amt,
          G = (num & 0x0000FF) + amt;
      var newColour = (0x1000000 + (R<255?R<1?0:R:255)*0x10000 + (B<255?B<1?0:B:255)*0x100 + (G<255?G<1?0:G:255)).toString(16).slice(1);
      return '#'+newColour;
    },

    isMobile: function() {
      return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
    },

    isPlainObject: function(obj) {
      // The code "typeof obj === 'object' && obj !== null" allows Array objects
      return typeof obj === 'object' && obj !== null && obj.constructor == Object;
    },
  };

  // valid cookie values
  cc.status = {
    deny: 'deny',
    allow: 'allow',
    dismiss: 'dismiss'
  };

  // detects the `transitionend` event name
  cc.transitionEnd = (function() {
    var el = document.createElement('div');
    var trans = {
      t: "transitionend",
      OT: "oTransitionEnd",
      msT: "MSTransitionEnd",
      MozT: "transitionend",
      WebkitT: "webkitTransitionEnd",
    };

    for (var prefix in trans) {
      if (trans.hasOwnProperty(prefix) && typeof el.style[prefix + 'ransition'] != 'undefined') {
        return trans[prefix];
      }
    }
    return '';
  }());

  cc.hasTransition = !!cc.transitionEnd;

  // array of valid regexp escaped statuses
  var __allowedStatuses = Object.keys(cc.status).map(util.escapeRegExp);

  // contains references to the custom <style> tags
  cc.customStyles = {};

  cc.Popup = (function() {

    var defaultOptions = {

      // if false, this prevents the popup from showing (useful for giving to control to another piece of code)
      enabled: true,

      // optional (expecting a HTML element) if passed, the popup is appended to this element. default is `document.body`
      container: null,

      // defaults cookie options - it is RECOMMENDED to set these values to correspond with your server
      cookie: {
        // This is the name of this cookie - you can ignore this
        name: 'cookieconsent_status',

        // The cookies expire date, specified in days (specify -1 for no expiry)
        expiryDays: 365,
      },

      // these callback hooks are called at certain points in the program execution
      onPopupOpen: function() {},
      onPopupClose: function() {},
      onInitialise: function(status) {},
      onStatusChange: function(status, chosenBefore) {},
      onRevokeChoice: function() {},

      // each item defines the inner text for the element that it references
      content: {
        header: 'Cookies used on the website!',
        message: 'This website uses cookies to ensure you get the best experience on our website.',
        dismiss: 'Got it!',
        allow: 'Allow cookies',
        deny: 'Decline',
        link: 'Learn more',
        href: 'http://cookiesandyou.com',
        close: '&#x274c;',
      },

      // This is the HTML for the elements above. The string {{header}} will be replaced with the equivalent text below.
      // You can remove "{{header}}" and write the content directly inside the HTML if you want.
      //
      //  - ARIA rules suggest to ensure controls are tabbable (so the browser can find the first control),
      //    and to set the focus to the first interactive control (http://w3c.github.io/aria-in-html/)
      elements: {
        header: '<span class="cc-header">{{header}}</span>&nbsp;',
        message: '<span id="cookieconsent:desc" class="cc-message">{{message}}</span>',
        messagelink: '<span id="cookieconsent:desc" class="cc-message">{{message}} <a aria-label="learn more about cookies" role=button tabindex="0" class="cc-link" href="{{href}}" target="_blank">{{link}}</a></span>',
        dismiss: '<a aria-label="dismiss cookie message" role=button tabindex="0" class="cc-btn cc-dismiss">{{dismiss}}</a>',
        allow: '<a aria-label="allow cookies" role=button tabindex="0"  class="cc-btn cc-allow">{{allow}}</a>',
        deny: '<a aria-label="deny cookies" role=button tabindex="0" class="cc-btn cc-deny">{{deny}}</a>',
        link: '<a aria-label="learn more about cookies" role=button tabindex="0" class="cc-link" href="{{href}}" target="_blank">{{link}}</a>',
        close: '<span aria-label="dismiss cookie message" role=button tabindex="0" class="cc-close">{{close}}</span>',

        //compliance: compliance is also an element, but it is generated by the application, depending on `type` below
      },

      // The placeholders {{classes}} and {{children}} both get replaced during initialisation:
      //  - {{classes}} is where additional classes get added
      //  - {{children}} is where the HTML children are placed
      window: '<div role="dialog" aria-live="polite" aria-label="cookieconsent" aria-describedby="cookieconsent:desc" class="cc-window {{classes}}"><!--googleoff: all-->{{children}}<!--googleon: all--></div>',

      // This is the html for the revoke button. This only shows up after the user has selected their level of consent
      // It can be enabled of disabled using the `revokable` option
      revokeBtn: '<div class="cc-revoke {{classes}}">Cookie Policy</div>',

      // define types of 'compliance' here. '{{value}}' strings in here are linked to `elements`
      compliance: {
        'info': '<div class="cc-compliance">{{dismiss}}</div>',
        'opt-in': '<div class="cc-compliance cc-highlight">{{dismiss}}{{allow}}</div>',
        'opt-out': '<div class="cc-compliance cc-highlight">{{deny}}{{dismiss}}</div>',
      },

      // select your type of popup here
      type: 'info', // refers to `compliance` (in other words, the buttons that are displayed)

      // define layout layouts here
      layouts: {
        // the 'block' layout tend to be for square floating popups
        'basic': '{{messagelink}}{{compliance}}',
        'basic-close': '{{messagelink}}{{compliance}}{{close}}',
        'basic-header': '{{header}}{{message}}{{link}}{{compliance}}',

        // add a custom layout here, then add some new css with the class '.cc-layout-my-cool-layout'
        //'my-cool-layout': '<div class="my-special-layout">{{message}}{{compliance}}</div>{{close}}',
      },

      // default layout (see above)
      layout: 'basic',

      // this refers to the popup windows position. we currently support:
      //  - banner positions: top, bottom
      //  - floating positions: top-left, top-right, bottom-left, bottom-right
      //
      // adds a class `cc-floating` or `cc-banner` which helps when styling
      position: 'bottom', // default position is 'bottom'

      // Available styles
      //    -block (default, no extra classes)
      //    -edgeless
      //    -classic
      // use your own style name and use `.cc-theme-STYLENAME` class in CSS to edit .
      // Note: style "wire" is used for the configurator, but has no CSS styles of its own, only palette is used.
      theme: 'block',

      // The popup is `fixed` by default, but if you want it to be static (inline with the page content), set this to false
      // Note: by default, we animate the height of the popup from 0 to full size
      static: false,

      // if you want custom colours, pass them in here. this object should look like this.
      // ideally, any custom colours/themes should be created in a separate style sheet, as this is more efficient.
      //   {
      //     popup: {background: '#000000', text: '#fff', link: '#fff'},
      //     button: {background: 'transparent', border: '#f8e71c', text: '#f8e71c'},
      //     highlight: {background: '#f8e71c', border: '#f8e71c', text: '#000000'},
      //   }
      // `highlight` is optional and extends `button`. if it exists, it will apply to the first button
      // only background needs to be defined for every element. if not set, other colors can be calculated from it
      palette: null,

      // Some countries REQUIRE that a user can change their mind. You can configure this yourself.
      // Most of the time this should be false, but the `cookieconsent.law` can change this to `true` if it detects that it should
      revokable: false,

      // if true, the revokable button will tranlate in and out
      animateRevokable: true,

      // used to disable link on existing layouts
      // replaces element messagelink with message and removes content of link
      showLink: true,

      // set value as scroll range to enable
      dismissOnScroll: false,

      // set value as time in milliseconds to autodismiss after set time
      dismissOnTimeout: false,

      // The application automatically decideds whether the popup should open.
      // Set this to false to prevent this from happening and to allow you to control the behaviour yourself
      autoOpen: true,

      // By default the created HTML is automatically appended to the container (which defaults to <body>). You can prevent this behaviour
      // by setting this to false, but if you do, you must attach the `element` yourself, which is a public property of the popup instance:
      // 
      //     var instance = cookieconsent.factory(options);
      //     document.body.appendChild(instance.element);
      //
      autoAttach: true,

      // simple whitelist/blacklist for pages. specify page by:
      //   - using a string : '/index.html'           (matches '/index.html' exactly) OR
      //   - using RegExp   : /\/page_[\d]+\.html/    (matched '/page_1.html' and '/page_2.html' etc)
      whitelistPage: [],
      blacklistPage: [],

      // If this is defined, then it is used as the inner html instead of layout. This allows for ultimate customisation.
      // Be sure to use the classes `cc-btn` and `cc-allow`, `cc-deny` or `cc-dismiss`. They enable the app to register click
      // handlers. You can use other pre-existing classes too. See `src/styles` folder.
      overrideHTML: null,
    };

    function CookiePopup() {
      this.initialise.apply(this, arguments);
    }

    CookiePopup.prototype.initialise = function(options) {
      if (this.options) {
        this.destroy(); // already rendered
      }

      // set options back to default options
      util.deepExtend(this.options = {}, defaultOptions);

      // merge in user options
      if (util.isPlainObject(options)) {
        util.deepExtend(this.options, options);
      }

      // returns true if `onComplete` was called
      if (checkCallbackHooks.call(this)) {
        // user has already answered
        this.options.enabled = false;
      }

      // apply blacklist / whitelist
      if (arrayContainsMatches(this.options.blacklistPage, location.pathname)) {
        this.options.enabled = false;
      }
      if (arrayContainsMatches(this.options.whitelistPage, location.pathname)) {
        this.options.enabled = true;
      }

      // the full markup either contains the wrapper or it does not (for multiple instances)
      var cookiePopup = this.options.window
        .replace('{{classes}}', getPopupClasses.call(this).join(' '))
        .replace('{{children}}', getPopupInnerMarkup.call(this));

      // if user passes html, use it instead
      var customHTML = this.options.overrideHTML;
      if (typeof customHTML == 'string' && customHTML.length) {
        cookiePopup = customHTML;
      }

      // if static, we need to grow the element from 0 height so it doesn't jump the page
      // content. we wrap an element around it which will mask the hidden content
      if (this.options.static) {
        // `grower` is a wrapper div with a hidden overflow whose height is animated
        var wrapper = appendMarkup.call(this, '<div class="cc-grower">' + cookiePopup + '</div>');

        wrapper.style.display = ''; // set it to visible (because appendMarkup hides it)
        this.element = wrapper.firstChild; // get the `element` reference from the wrapper
        this.element.style.display = 'none';
        util.addClass(this.element, 'cc-invisible');
      } else {
        this.element = appendMarkup.call(this, cookiePopup);
      }

      applyAutoDismiss.call(this);

      applyRevokeButton.call(this);

      if (this.options.autoOpen) {
        this.autoOpen();
      }
    };

    CookiePopup.prototype.destroy = function() {
      if (this.onButtonClick && this.element) {
        this.element.removeEventListener('click', this.onButtonClick);
        this.onButtonClick = null;
      }

      if (this.dismissTimeout) {
        clearTimeout(this.dismissTimeout);
        this.dismissTimeout = null;
      }

      if (this.onWindowScroll) {
        window.removeEventListener('scroll', this.onWindowScroll);
        this.onWindowScroll = null;
      }

      if (this.onMouseMove) {
        window.removeEventListener('mousemove', this.onMouseMove);
        this.onMouseMove = null;
      }

      if (this.element && this.element.parentNode) {
        this.element.parentNode.removeChild(this.element);
      }
      this.element = null;

      if (this.revokeBtn && this.revokeBtn.parentNode) {
        this.revokeBtn.parentNode.removeChild(this.revokeBtn);
      }
      this.revokeBtn = null;

      removeCustomStyle(this.options.palette);
      this.options = null;
    };

    CookiePopup.prototype.open = function(callback) {
      if (!this.element) return;

      if (!this.isOpen()) {
        if (cc.hasTransition) {
          this.fadeIn();
        } else {
          this.element.style.display = '';
        }

        if (this.options.revokable) {
          this.toggleRevokeButton();
        }
        this.options.onPopupOpen.call(this);
      }

      return this;
    };

    CookiePopup.prototype.close = function(showRevoke) {
      if (!this.element) return;

      if (this.isOpen()) {
        if (cc.hasTransition) {
          this.fadeOut();
        } else {
          this.element.style.display = 'none';
        }

        if (showRevoke && this.options.revokable) {
          this.toggleRevokeButton(true);
        }
        this.options.onPopupClose.call(this);

        $(window).trigger('throttled-resize'); // Recalculate page layout
      }

      return this;
    };

    CookiePopup.prototype.fadeIn = function() {
      var el = this.element;

      if (!cc.hasTransition || !el)
        return;

      // This should always be called AFTER fadeOut (which is governed by the 'transitionend' event).
      // 'transitionend' isn't all that reliable, so, if we try and fadeIn before 'transitionend' has
      // has a chance to run, then we run it ourselves
      if (this.afterTransition) {
        afterFadeOut.call(this, el)
      }

      if (util.hasClass(el, 'cc-invisible')) {
        el.style.display = '';

        if (this.options.static) {
          var height = this.element.clientHeight;
          this.element.parentNode.style.maxHeight = height + 'px';
        }

        var fadeInTimeout = 20; // (ms) DO NOT MAKE THIS VALUE SMALLER. See below

        // Although most browsers can handle values less than 20ms, it should remain above this value.
        // This is because we are waiting for a "browser redraw" before we remove the 'cc-invisible' class.
        // If the class is remvoed before a redraw could happen, then the fadeIn effect WILL NOT work, and
        // the popup will appear from nothing. Therefore we MUST allow enough time for the browser to do
        // its thing. The actually difference between using 0 and 20 in a set timeout is neglegible anyway
        this.openingTimeout = setTimeout(afterFadeIn.bind(this, el), fadeInTimeout);
      }
    };

    CookiePopup.prototype.fadeOut = function() {
      var el = this.element;

      if (!cc.hasTransition || !el)
        return;

      if (this.openingTimeout) {
        clearTimeout(this.openingTimeout);
        afterFadeIn.bind(this, el);
      }

      if (!util.hasClass(el, 'cc-invisible')) {
        if (this.options.static) {
          this.element.parentNode.style.maxHeight = '';
        }

        this.afterTransition = afterFadeOut.bind(this, el);
        el.addEventListener(cc.transitionEnd, this.afterTransition);

        util.addClass(el, 'cc-invisible');
      }
    };

    CookiePopup.prototype.isOpen = function() {
      return this.element && this.element.style.display == '' && (cc.hasTransition ? !util.hasClass(this.element, 'cc-invisible') : true);
    };

    CookiePopup.prototype.toggleRevokeButton = function(show) {
      if (this.revokeBtn) this.revokeBtn.style.display = show ? '' : 'none';
    };

    CookiePopup.prototype.revokeChoice = function(preventOpen) {
      this.options.enabled = true;
      this.clearStatus();

      this.options.onRevokeChoice.call(this);

      if (!preventOpen) {
        this.autoOpen();
      }
    };

    // returns true if the cookie has a valid value
    CookiePopup.prototype.hasAnswered = function(options) {
      return Object.keys(cc.status).indexOf(this.getStatus()) >= 0;
    };

    // returns true if the cookie indicates that consent has been given
    CookiePopup.prototype.hasConsented = function(options) {
      var val = this.getStatus();
      return val == cc.status.allow || val == cc.status.dismiss;
    };

    // opens the popup if no answer has been given
    CookiePopup.prototype.autoOpen = function(options) {
      !this.hasAnswered() && this.options.enabled && this.open();
    };

    CookiePopup.prototype.setStatus = function(status) {
      var c = this.options.cookie;
      var value = util.getCookie(c.name);
      var chosenBefore = Object.keys(cc.status).indexOf(value) >= 0;

      // if `status` is valid
      if (Object.keys(cc.status).indexOf(status) >= 0) {
        util.setCookie(c.name, status, c.expiryDays);

        this.options.onStatusChange.call(this, status, chosenBefore);
      } else {
        this.clearStatus();
      }
    };

    CookiePopup.prototype.getStatus = function() {
      return util.getCookie(this.options.cookie.name);
    };

    CookiePopup.prototype.clearStatus = function() {
      window.EFL.local.cookies.remove(c.name)
    };

    // This needs to be called after 'fadeIn'. This is the code that actually causes the fadeIn to work
    // There is a good reason why it's called in a timeout. Read 'fadeIn';
    function afterFadeIn(el) {
      this.openingTimeout = null;
      util.removeClass(el, 'cc-invisible');
    }

    // This is called on 'transitionend' (only on the transition of the fadeOut). That's because after we've faded out, we need to
    // set the display to 'none' (so there aren't annoying invisible popups all over the page). If for whenever reason this function
    // is not called (lack of support), the open/close mechanism will still work.
    function afterFadeOut(el) {
      el.style.display = 'none'; // after close and before open, the display should be none
      el.removeEventListener(cc.transitionEnd, this.afterTransition);
      this.afterTransition = null;
    }

    // this function calls the `onComplete` hook and returns true (if needed) and returns false otherwise
    function checkCallbackHooks() {
      var complete = this.options.onInitialise.bind(this);

      if (!window.navigator.cookieEnabled) {
        complete(cc.status.deny);
        return true;
      }

      if (window.CookiesOK || window.navigator.CookiesOK) {
        complete(cc.status.allow);
        return true;
      }

      var allowed = Object.keys(cc.status);
      var answer = this.getStatus();
      var match = allowed.indexOf(answer) >= 0;

      if (match) {
        complete(answer);
      }
      return match;
    }

    function getPositionClasses() {
      var positions = this.options.position.split('-'); // top, bottom, left, right
      var classes = [];

      // top, left, right, bottom
      positions.forEach(function(cur) {
        classes.push('cc-' + cur);
      });

      return classes;
    }

    function getPopupClasses() {
      var opts = this.options;
      var positionStyle = (opts.position == 'top' || opts.position == 'bottom') ? 'banner' : 'floating';

      if (util.isMobile()) {
        positionStyle = 'floating';
      }

      var classes = [
        'cc-' + positionStyle, // floating or banner
        'cc-type-' + opts.type, // add the compliance type
        'cc-theme-' + opts.theme, // add the theme
      ];

      if (opts.static) {
        classes.push('cc-static');
      }

      classes.push.apply(classes, getPositionClasses.call(this));

      // we only add extra styles if `palette` has been set to a valid value
      var didAttach = attachCustomPalette.call(this, this.options.palette);

      // if we override the palette, add the class that enables this
      if (this.customStyleSelector) {
        classes.push(this.customStyleSelector);
      }

      return classes;
    }

    function getPopupInnerMarkup() {
      var interpolated = {};
      var opts = this.options;

      // removes link if showLink is false
      if (!opts.showLink) {
        opts.elements.link = '';
        opts.elements.messagelink = opts.elements.message;
      }

      Object.keys(opts.elements).forEach(function(prop) {
        interpolated[prop] = util.interpolateString(opts.elements[prop], function(name) {
          var str = opts.content[name];
          return (name && typeof str == 'string' && str.length) ? str : '';
        })
      });

      // checks if the type is valid and defaults to info if it's not
      var complianceType = opts.compliance[opts.type];
      if (!complianceType) {
        complianceType = opts.compliance.info;
      }

      // build the compliance types from the already interpolated `elements`
      interpolated.compliance = util.interpolateString(complianceType, function(name) {
        return interpolated[name];
      });

      // checks if the layout is valid and defaults to basic if it's not
      var layout = opts.layouts[opts.layout];
      if (!layout) {
        layout = opts.layouts.basic;
      }

      return util.interpolateString(layout, function(match) {
        return interpolated[match];
      });
    }

    function appendMarkup(markup) {
      var opts = this.options;
      var div = document.createElement('div');
      var cont = (opts.container && opts.container.nodeType === 1) ? opts.container : document.body;

      div.innerHTML = markup;

      var el = div.children[0];

      el.style.display = 'none';

      if (util.hasClass(el, 'cc-window') && cc.hasTransition) {
        util.addClass(el, 'cc-invisible');
      }

      // save ref to the function handle so we can unbind it later
      this.onButtonClick = handleButtonClick.bind(this);

      el.addEventListener('click', this.onButtonClick);

      if (opts.autoAttach) {
        if (!cont.firstChild) {
          cont.appendChild(el);
        } else {
          cont.insertBefore(el, cont.firstChild)
        }
      }

      return el;
    }

    function handleButtonClick(event) {
      var targ = event.target;
      if (util.hasClass(targ, 'cc-btn')) {

        var matches = targ.className.match(new RegExp("\\bcc-(" + __allowedStatuses.join('|') + ")\\b"));
        var match = (matches && matches[1]) || false;

        if (match) {
          this.setStatus(match);
          this.close(true);
        }
      }
      if (util.hasClass(targ, 'cc-close')) {
        this.setStatus(cc.status.dismiss);
        this.close(true);
      }
      if (util.hasClass(targ, 'cc-revoke')) {
        this.revokeChoice();
      }
    }

    // I might change this function to use inline styles. I originally chose a stylesheet because I could select many elements with a
    // single rule (something that happened a lot), the apps has changed slightly now though, so inline styles might be more applicable.
    function attachCustomPalette(palette) {
      var hash = util.hash(JSON.stringify(palette));
      var selector = 'cc-color-override-' + hash;
      var isValid = util.isPlainObject(palette);

      this.customStyleSelector = isValid ? selector : null;

      if (isValid) {
        addCustomStyle(hash, palette, '.' + selector);
      }
      return isValid;
    }

    function addCustomStyle(hash, palette, prefix) {

      // only add this if a style like it doesn't exist
      if (cc.customStyles[hash]) {
        // custom style already exists, so increment the reference count
        ++cc.customStyles[hash].references;
        return;
      }

      var colorStyles = {};
      var popup = palette.popup;
      var button = palette.button;
      var highlight = palette.highlight;

      // needs background colour, text and link will be set to black/white if not specified
      if (popup) {
        // assumes popup.background is set
        popup.text = popup.text ? popup.text : util.getContrast(popup.background);
        popup.link = popup.link ? popup.link : popup.text;
        colorStyles[prefix + '.cc-window'] = [
          'color: ' + popup.text,
          'background-color: ' + popup.background
        ];
        colorStyles[prefix + '.cc-revoke'] = [
          'color: ' + popup.text,
          'background-color: ' + popup.background
        ];
        colorStyles[prefix + ' .cc-link,' + prefix + ' .cc-link:active,' + prefix + ' .cc-link:visited'] = [
          'color: ' + popup.link
        ];

        if (button) {
          // assumes button.background is set
          button.text = button.text ? button.text : util.getContrast(button.background);
          button.border = button.border ? button.border : 'transparent';
          colorStyles[prefix + ' .cc-btn'] = [
            'color: ' + button.text,
            'border-color: ' + button.border,
            'background-color: ' + button.background
          ];
          
          if(button.background != 'transparent') 
            colorStyles[prefix + ' .cc-btn:hover, ' + prefix + ' .cc-btn:focus'] = [
              'background-color: ' + getHoverColour(button.background)
            ];

          if (highlight) {
            //assumes highlight.background is set
            highlight.text = highlight.text ? highlight.text : util.getContrast(highlight.background);
            highlight.border = highlight.border ? highlight.border : 'transparent';
            colorStyles[prefix + ' .cc-highlight .cc-btn:first-child'] = [
              'color: ' + highlight.text,
              'border-color: ' + highlight.border,
              'background-color: ' + highlight.background
            ];
          } else {
            // sets highlight text color to popup text. background and border are transparent by default.
            colorStyles[prefix + ' .cc-highlight .cc-btn:first-child'] = [
              'color: ' + popup.text
            ];
          }
        }

      }

      // this will be interpretted as CSS. the key is the selector, and each array element is a rule
      var style = document.createElement('style');
      document.head.appendChild(style);

      // custom style doesn't exist, so we create it
      cc.customStyles[hash] = {
        references: 1,
        element: style.sheet
      };

      var ruleIndex = -1;
      for (var prop in colorStyles) {
        if (colorStyles.hasOwnProperty(prop)) {
          style.sheet.insertRule(prop + '{' + colorStyles[prop].join(';') + '}', ++ruleIndex);
        }
      }
    }

    function getHoverColour(hex) {
      hex = util.normaliseHex(hex);
      // for black buttons
      if (hex == '000000') {
        return '#222';
      }
      return util.getLuminance(hex);
    }

    function removeCustomStyle(palette) {
      if (util.isPlainObject(palette)) {
        var hash = util.hash(JSON.stringify(palette));
        var customStyle = cc.customStyles[hash];
        if (customStyle && !--customStyle.references) {
          var styleNode = customStyle.element.ownerNode;
          if (styleNode && styleNode.parentNode) {
            styleNode.parentNode.removeChild(styleNode);
          }
          cc.customStyles[hash] = null;
        }
      }
    }

    function arrayContainsMatches(array, search) {
      for (var i = 0, l = array.length; i < l; ++i) {
        var str = array[i];
        // if regex matches or string is equal, return true
        if ((str instanceof RegExp && str.test(search)) ||
          (typeof str == 'string' && str.length && str === search)) {
          return true;
        }
      }
      return false;
    }

    function applyAutoDismiss() {
      var setStatus = this.setStatus.bind(this);

      var delay = this.options.dismissOnTimeout;
      if (typeof delay == 'number' && delay >= 0) {
        this.dismissTimeout = window.setTimeout(function() {
          setStatus(cc.status.dismiss);
        }, Math.floor(delay));
      }

      var scrollRange = this.options.dismissOnScroll;
      if (typeof scrollRange == 'number' && scrollRange >= 0) {
        var onWindowScroll = function(evt) {
          if (window.pageYOffset > Math.floor(scrollRange)) {
            setStatus(cc.status.dismiss);

            window.removeEventListener('scroll', onWindowScroll);
            this.onWindowScroll = null;
          }
        };

        this.onWindowScroll = onWindowScroll;
        window.addEventListener('scroll', onWindowScroll);
      }
    }

    function applyRevokeButton() {
      // revokable is true if advanced compliance is selected
      if (this.options.type != 'info') this.options.revokable = true;
      // animateRevokable false for mobile devices
      if (util.isMobile()) this.options.animateRevokable = false;

      if (this.options.revokable) {
        var classes = getPositionClasses.call(this);
        if (this.options.animateRevokable) {
          classes.push('cc-animate');
        }
        if (this.customStyleSelector) {
          classes.push(this.customStyleSelector)
        }
        var revokeBtn = this.options.revokeBtn.replace('{{classes}}', classes.join(' '));
        this.revokeBtn = appendMarkup.call(this, revokeBtn);

        var btn = this.revokeBtn;
        if (this.options.animateRevokable) {
          var wait = false;
          var onMouseMove = util.throttle(function(evt) {
            var active = false;
            var minY = 20;
            var maxY = (window.innerHeight - 20);

            if (util.hasClass(btn, 'cc-top') && evt.clientY < minY) active = true;
            if (util.hasClass(btn, 'cc-bottom') && evt.clientY > maxY) active = true;

            if (active) {
              if (!util.hasClass(btn, 'cc-active')) {
                util.addClass(btn, 'cc-active');
              }
            } else {
              if (util.hasClass(btn, 'cc-active')) {
                util.removeClass(btn, 'cc-active');
              }
            }
          }, 200);

          this.onMouseMove = onMouseMove;
          window.addEventListener('mousemove', onMouseMove);
        }
      }
    }

    return CookiePopup
  }());

  cc.Location = (function() {

    // An object containing all the location services we have already set up.
    // When using a service, it could either return a data structure in plain text (like a JSON object) or an executable script
    // When the response needs to be executed by the browser, then `isScript` must be set to true, otherwise it won't work.

    // When the service uses a script, the chances are that you'll have to use the script to make additional requests. In these
    // cases, the services `callback` property is called with a `done` function. When performing async operations, this must be called
    // with the data (or Error), and `cookieconsent.locate` will take care of the rest
    var defaultOptions = {

      // The default timeout is 5 seconds. This is mainly needed to catch JSONP requests that error.
      // Otherwise there is no easy way to catch JSONP errors. That means that if a JSONP fails, the
      // app will take `timeout` milliseconds to react to a JSONP network error.
      timeout: 5000,

      // the order that services will be attempted in
      services: [
        'freegeoip',
        'ipinfo',
        'maxmind'

        /*

        // 'ipinfodb' requires some options, so we define it using an object
        // this object will be passed to the function that defines the service

        {
          name: 'ipinfodb',
          interpolateUrl: {
            // obviously, this is a fake key
            api_key: 'vOgI3748dnIytIrsJcxS7qsDf6kbJkE9lN4yEDrXAqXcKUNvjjZPox3ekXqmMMld'
          },
        },

        // as well as defining an object, you can define a function that returns an object

        function () {
          return {name: 'ipinfodb'};
        },

        */
      ],

      serviceDefinitions: {

        freegeoip: function() {
          return {
            // This service responds with JSON, but they do not have CORS set, so we must use JSONP and provide a callback
            // The `{callback}` is automatically rewritten by the tool
            url: '//freegeoip.net/json/?callback={callback}',
            isScript: true, // this is JSONP, therefore we must set it to run as a script
            callback: function(done, response) {
              try{
                var json = JSON.parse(response);
                return json.error ? toError(json) : {
                  code: json.country_code
                };
              } catch (err) {
                return toError({error: 'Invalid response ('+err+')'});
              }
            }
          }
        },

        ipinfo: function() {
          return {
            // This service responds with JSON, so we simply need to parse it and return the country code
            url: '//ipinfo.io',
            headers: ['Accept: application/json'],
            callback: function(done, response) {
              try{
                var json = JSON.parse(response);
                return json.error ? toError(json) : {
                  code: json.country
                };
              } catch (err) {
                return toError({error: 'Invalid response ('+err+')'});
              }
            }
          }
        },

        // This service requires an option to define `key`. Options are proived using objects or functions
        ipinfodb: function(options) {
          return {
            // This service responds with JSON, so we simply need to parse it and return the country code
            url: '//api.ipinfodb.com/v3/ip-country/?key={api_key}&format=json&callback={callback}',
            isScript: true, // this is JSONP, therefore we must set it to run as a script
            callback: function(done, response) {
              try{
                var json = JSON.parse(response);
                return json.statusCode == 'ERROR' ? toError({error: json.statusMessage}) : {
                  code: json.countryCode
                };
              } catch (err) {
                return toError({error: 'Invalid response ('+err+')'});
              }
            }
          }
        },

        maxmind: function() {
          return {
            // This service responds with a JavaScript file which defines additional functionality. Once loaded, we must
            // make an additional AJAX call. Therefore we provide a `done` callback that can be called asynchronously
            url: '//js.maxmind.com/js/apis/geoip2/v2.1/geoip2.js',
            isScript: true, // this service responds with a JavaScript file, so it must be run as a script
            callback: function(done) {
              // if everything went okay then `geoip2` WILL be defined
              if (!window.geoip2) {
                done(new Error('Unexpected response format. The downloaded script should have exported `geoip2` to the global scope'));
                return;
              }

              geoip2.country(function(location) {
                try {
                  done({
                    code: location.country.iso_code
                  });
                } catch (err) {
                  done(toError(err));
                }
              }, function(err) {
                done(toError(err));
              });

              // We can't return anything, because we need to wait for the second AJAX call to return.
              // Then we can 'complete' the service by passing data or an error to the `done` callback.
            }
          }
        },
      },
    };

    function Location(options) {
      // Set up options
      util.deepExtend(this.options = {}, defaultOptions);

      if (util.isPlainObject(options)) {
        util.deepExtend(this.options, options);
      }

      this.currentServiceIndex = -1; // the index (in options) of the service we're currently using
    }

    Location.prototype.getNextService = function() {
      var service;

      do {
        service = this.getServiceByIdx(++this.currentServiceIndex);
      } while (this.currentServiceIndex < this.options.services.length && !service);

      return service;
    };

    Location.prototype.getServiceByIdx = function(idx) {
      // This can either be the name of a default locationService, or a function.
      var serviceOption = this.options.services[idx];

      // If it's a string, use one of the location services.
      if (typeof serviceOption === 'function') {
        var dynamicOpts = serviceOption();
        if (dynamicOpts.name) {
          util.deepExtend(dynamicOpts, this.options.serviceDefinitions[dynamicOpts.name](dynamicOpts));
        }
        return dynamicOpts;
      }

      // If it's a string, use one of the location services.
      if (typeof serviceOption === 'string') {
        return this.options.serviceDefinitions[serviceOption]();
      }

      // If it's an object, assume {name: 'ipinfo', ...otherOptions}
      // Allows user to pass in API keys etc.
      if (util.isPlainObject(serviceOption)) {
        return this.options.serviceDefinitions[serviceOption.name](serviceOption);
      }

      return null;
    };

    // This runs the service located at index `currentServiceIndex`.
    // If the service fails, `runNextServiceOnError` will continue trying each service until all fail, or one completes successfully
    Location.prototype.locate = function(complete, error) {
      var service = this.getNextService();

      if (!service) {
        error(new Error('No services to run'));
        return;
      }

      this.callbackComplete = complete;
      this.callbackError = error;

      this.runService(service, this.runNextServiceOnError.bind(this));
    };

    // Potentially adds a callback to a url for jsonp.
    Location.prototype.setupUrl = function(service) {
      var serviceOpts = this.getCurrentServiceOpts();
      return service.url.replace(/\{(.*?)\}/g, function(_, param) {
        if (param === 'callback') {
          var tempName = 'callback' + Date.now();
          window[tempName] = function(res) {
            service.__JSONP_DATA = JSON.stringify(res);
          }
          return tempName;
        }
        if (param in serviceOpts.interpolateUrl) {
          return serviceOpts.interpolateUrl[param];
        }
      });
    };

    // requires a `service` object that defines at least a `url` and `callback`
    Location.prototype.runService = function(service, complete) {
      var self = this;

      // basic check to ensure it resembles a `service`
      if (!service || !service.url || !service.callback) {
        return;
      }

      // we call either `getScript` or `makeAsyncRequest` depending on the type of resource
      var requestFunction = service.isScript ? getScript : makeAsyncRequest;

      var url = this.setupUrl(service);

      // both functions have similar signatures so we can pass the same arguments to both
      requestFunction(url, function(xhr) {
        // if `!xhr`, then `getScript` function was used, so there is no response text
        var responseText = xhr ? xhr.responseText : '';

        // if the resource is a script, then this function is called after the script has been run.
        // if the script is JSONP, then a time defined function `callback_{Date.now}` has already
        // been called (as the JSONP callback). This callback sets the __JSONP_DATA property
        if (service.__JSONP_DATA) {
          responseText = service.__JSONP_DATA;
          delete service.__JSONP_DATA;
        }

        // call the service callback with the response text (so it can parse the response)
        self.runServiceCallback.call(self, complete, service, responseText);

      }, this.options.timeout, service.data, service.headers);

      // `service.data` and `service.headers` are optional (they only count if `!service.isScript` anyway)
    };

    // The service request has run (and possibly has a `responseText`) [no `responseText` if `isScript`]
    // We need to run its callback which determines if its successful or not
    // `complete` is called on success or failure
    Location.prototype.runServiceCallback = function(complete, service, responseText) {
      var self = this;
      // this is the function that is called if the service uses the async callback in its handler method
      var serviceResultHandler = function (asyncResult) {
        // if `result` is a valid value, then this function shouldn't really run
        // even if it is called by `service.callback`
        if (!result) {
          self.onServiceResult.call(self, complete, asyncResult)
        }
      };

      // the function `service.callback` will either extract a country code from `responseText` and return it (in `result`)
      // or (if it has to make additional requests) it will call a `done` callback with the country code when it is ready
      var result = service.callback(serviceResultHandler, responseText);

      if (result) {
        this.onServiceResult.call(this, complete, result);
      }
    };

    // This is called with the `result` from `service.callback` regardless of how it provided that result (sync or async).
    // `result` will be whatever is returned from `service.callback`. A service callback should provide an object with data
    Location.prototype.onServiceResult = function(complete, result) {
      // convert result to nodejs style async callback
      if (result instanceof Error || (result && result.error)) {
        complete.call(this, result, null);
      } else {
        complete.call(this, null, result);
      }
    };

    // if `err` is set, the next service handler is called
    // if `err` is null, the `onComplete` handler is called with `data`
    Location.prototype.runNextServiceOnError = function(err, data) {
      if (err) {
        this.logError(err);

        var nextService = this.getNextService();

        if (nextService) {
          this.runService(nextService, this.runNextServiceOnError.bind(this));
        } else {
          this.completeService.call(this, this.callbackError, new Error('All services failed'));
        }
      } else {
        this.completeService.call(this, this.callbackComplete, data);
      }
    };

    Location.prototype.getCurrentServiceOpts = function() {
      var val = this.options.services[this.currentServiceIndex];

      if (typeof val == 'string') {
        return {name: val};
      }

      if (typeof val == 'function') {
        return val();
      }

      if (util.isPlainObject(val)) {
        return val;
      }

      return {};
    };

    // calls the `onComplete` callback after resetting the `currentServiceIndex`
    Location.prototype.completeService = function(fn, data) {
      this.currentServiceIndex = -1;

      fn && fn(data);
    };

    Location.prototype.logError = function (err) {
      var idx = this.currentServiceIndex;
      var service = this.getServiceByIdx(idx);

      console.error('The service[' + idx + '] (' + service.url + ') responded with the following error', err);
    };

    function getScript(url, callback, timeout) {
      var timeoutIdx, s = document.createElement('script');

      s.type = 'text/' + (url.type || 'javascript');
      s.src = url.src || url;
      s.async = false;

      s.onreadystatechange = s.onload = function() {
        // this code handles two scenarios, whether called by onload or onreadystatechange
        var state = s.readyState;

        clearTimeout(timeoutIdx);

        if (!callback.done && (!state || /loaded|complete/.test(state))) {
          callback.done = true;
          callback();
          s.onreadystatechange = s.onload = null;
        }
      };

      document.body.appendChild(s);

      // You can't catch JSONP Errors, because it's handled by the script tag
      // one way is to use a timeout
      timeoutIdx = setTimeout(function () {
        callback.done = true;
        callback();
        s.onreadystatechange = s.onload = null;
      }, timeout);
    }

    function makeAsyncRequest(url, onComplete, timeout, postData, requestHeaders) {
      var xhr = new(window.XMLHttpRequest || window.ActiveXObject)('MSXML2.XMLHTTP.3.0');

      xhr.open(postData ? 'POST' : 'GET', url, 1);

      xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
      xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');

      if (Array.isArray(requestHeaders)) {
        for (var i = 0, l = requestHeaders.length; i < l; ++i) {
          var split = requestHeaders[i].split(':', 2)
          xhr.setRequestHeader(split[0].replace(/^\s+|\s+$/g, ''), split[1].replace(/^\s+|\s+$/g, ''));
        }
      }

      if (typeof onComplete == 'function') {
        xhr.onreadystatechange = function() {
          if (xhr.readyState > 3) {
            onComplete(xhr);
          }
        };
      }

      xhr.send(postData);
    }

    function toError(obj) {
      return new Error('Error [' + (obj.code || 'UNKNOWN') + ']: ' + obj.error);
    }

    return Location;
  }());

  cc.Law = (function() {

    var defaultOptions = {
      // Make this false if you want to disable all regional overrides for settings.
      // If true, options can differ by country, depending on their cookie law.
      // It does not affect hiding the popup for countries that do not have cookie law.
      regionalLaw: true,

      // countries that enforce some version of a cookie law
      hasLaw: ['AT', 'BE', 'BG', 'HR', 'CZ', 'CY', 'DK', 'EE', 'FI', 'FR', 'DE', 'EL', 'HU', 'IE', 'IT', 'LV', 'LT', 'LU', 'MT', 'NL', 'PL', 'PT', 'SK', 'SI', 'ES', 'SE', 'GB', 'UK'],

      // countries that say that all cookie consent choices must be revokable (a user must be able too change their mind)
      revokable: ['HR', 'CY', 'DK', 'EE', 'FR', 'DE', 'LV', 'LT', 'NL', 'PT', 'ES'],

      // countries that say that a person can only "consent" if the explicitly click on "I agree".
      // in these countries, consent cannot be implied via a timeout or by scrolling down the page
      explicitAction: ['HR', 'IT', 'ES'],
    };

    function Law(options) {
      this.initialise.apply(this, arguments);
    }

    Law.prototype.initialise = function(options) {
      // set options back to default options
      util.deepExtend(this.options = {}, defaultOptions);

      // merge in user options
      if (util.isPlainObject(options)) {
        util.deepExtend(this.options, options);
      }
    };

    Law.prototype.get = function(countryCode) {
      var opts = this.options;
      return {
        hasLaw: opts.hasLaw.indexOf(countryCode) >= 0,
        revokable: opts.revokable.indexOf(countryCode) >= 0,
        explicitAction: opts.explicitAction.indexOf(countryCode) >= 0,
      };
    };

    Law.prototype.applyLaw = function(options, countryCode) {
      var country = this.get(countryCode);

      if (!country.hasLaw) {
        // The country has no cookie law
        options.enabled = false;
      }

      if (this.options.regionalLaw) {
        if (country.revokable) {
          // We must provide an option to revoke consent at a later time
          options.revokable = true;
        }

        if (country.explicitAction) {
          // The user must explicitly click the consent button
          options.dismissOnScroll = false;
          options.dismissOnTimeout = false;
        }
      }
      return options;
    };

    return Law;
  }());

  // This function initialises the app by combining the use of the Popup, Locator and Law modules
  // You can string together these three modules yourself however you want, by writing a new function.
  cc.initialise = function(options, complete, error) {
    var law = new cc.Law(options.law);

    if (!complete) complete = function() {};
    if (!error) error = function() {};

    cc.getCountryCode(options, function(result) {
      // don't need the law or location options anymore
      delete options.law;
      delete options.location;

      if (result.code) {
        options = law.applyLaw(options, result.code);
      }

      complete(new cc.Popup(options));
    }, function(err) {
      // don't need the law or location options anymore
      delete options.law;
      delete options.location;

      error(err, new cc.Popup(options));
    });
  };

  // This function tries to find your current location. It either grabs it from a hardcoded option in
  // `options.law.countryCode`, or attempts to make a location service request. This function accepts
  // options (which can configure the `law` and `location` modules) and fires a callback with which
  // passes an object `{code: countryCode}` as the first argument (which can have undefined properties)
  cc.getCountryCode = function(options, complete, error) {
    if (options.law && options.law.countryCode) {
      complete({
        code: options.law.countryCode
      });
      return;
    }
    if (options.location) {
      var locator = new cc.Location(options.location);
      locator.locate(function(serviceResult) {
        complete(serviceResult || {});
      }, error);
      return;
    }
    complete({});
  };

  // export utils (no point in hiding them, so we may as well expose them)
  cc.utils = util;

  // prevent this code from being run twice
  cc.hasInitialised = true;

  window.cookieconsent = cc;

}(window.cookieconsent || {}));
;

(function ($) {
    "use strict";

    var settings = {
        gridBlock: '[data-news-grid]',
        gridLeft: '[data-news-grid-left]',
        gridRight: '[data-news-grid-right]'
    };



    function init() {

        var $gridBlock = $(this);
        var $gridLeft = $gridBlock.find(settings.gridLeft);
		var $gridRight = $gridBlock.find(settings.gridRight);

        calcHeights($gridLeft, $gridRight);
        
        $(window).on('throttled-resize', function () {
            calcHeights($gridLeft, $gridRight)
        });
    }

	function calcHeights($left, $right) {
		$left.height("auto");
		$right.height("auto");

		if ($(window).width() >= 1024) {
			if ($right.height() !== 0) {
				if ($right.height() > $left.height()) {
					$left.height($right.height());
				}
				else {
					$right.height($left.height());
				}
			}
		}
    }

	$(function () {
        $(settings.gridBlock).each(init);
    });

})(jQuery)
;
/*
	Initialises Owl Carousel for news category items

	Example usage:

  <ul class="owl-carousel" data-carousel="news-category">
    <li> example 1 </li>
    <li> example 2 </li>
    <li> example 3 </li>
    <li> example 4 </li>
  </ul>
*/

(function ($) {
    "use strict";

    var settings = {
        trigger: '[data-carousel="news-home-hero"]',
    };

    var owl;

    function resetFocus(index) {
  
        owl.find('.owl-item:not(.cloned) a').eq(index-1).focus();
        window.EFL.eventDispatcher.deregisterHandler("slide-moved", resetFocus);
    }

    function init(index, element) {

        owl = $(settings.trigger);


        owl.owlCarousel({
            loop: true,
            items: 1,
            autoplay: true,
            smartSpeed: 800,
            autoplayHoverPause:true,
            onInitialized: function (event) {

                owl.find('.owl-item:not(.cloned) a').on('focus', function (e) {
                    e.preventDefault();
                });

                owl.find('.owl-item:not(.cloned) a').first().one('focus', function (e2) {
                   
                    owl.trigger('stop.owl.autoplay');
                    owl.trigger('to.owl.carousel', 0);

                    owl.find('.owl-item:not(.cloned) a').on('keydown', function (e) {
                        
                        if (e.keyCode == '9') {
                            window.EFL.eventDispatcher.registerHandler("slide-moved", resetFocus);
                            /*Buttons to not work after tabbing.  Need to revisit if requirement*/
                            /*owl.trigger('next.owl.carousel');*/
                        }
                    });

                });

                owl.on('translated.owl.carousel', function (event) {

                    index = event.item.index;

                    var tempFix = index - (event.relatedTarget.clones().length / 2);
                    if (tempFix > 0) {
                        index = tempFix;
                    }
                    
                    window.EFL.eventDispatcher.dispatch("slide-moved", index);
                });

                owl.find('.owl-item:not(.cloned) a').last().blur(function () {

                    owl.find('.owl-item:not(.cloned) a').off('keydown');

                });  
            }
        });
    }

    $(function () {
        $(settings.trigger).each(init);
    });

})(jQuery);
;
(function($) {
    "use strict";

    var settings = {
        trigger: '[data-custom-checkbox]',
        navbuttons: '[data-form-nav-button]',
        formclass: '.Form__MainBody',
        formactivestep: '.FormStep:not(.hide)',
        forms: 'form.manual-validation-summary',
        errorClass: 'ValidationFail',
        errorList: []
    };

    function setCheckboxState(element, isChecked) {
        var parent = element.parent();

        if (isChecked) {
            element.attr('aria-checked', 'true');
            parent.addClass('checked');
        }
        else {
            parent.removeClass('checked');
            element.attr('aria-checked', 'false');
        }
    }

    function init(index, element) {
        element = $(element);

        setCheckboxState(element, element.is(':checked'));

        element.on('change', function () {
            setCheckboxState(element, element.is(':checked'));
        })
    }

    //A function for forms that use 'steps' and next/prev buttons
    //Once the next/prev button is pressed, it moves focus to the first input on the new screen.
    //Epi didn't do this by default and it caused page jumping and accessibility issues.
    function focusStep(index, element) {

        $(element).on('click', function () {
            
            var $btn = $(element);

            //The next/prev buttons alter the dom to show the different steps.
            //We have to wait until that's finished before we activate the first input.'
            setTimeout(function () {
      
                if ($(settings.forms).hasClass(settings.errorClass)) {
                    return;
                }
                var $formstep = $btn.parents(settings.formclass).find('.FormStep:not(.hide)');
                $formstep.find(':input:visible:enabled:first').focus();
            }, 100);
            
        });
    }

    function buildErrorList(index, form) {
        /*
            // any questions ask Laurence

            This function collects all the required fields in the form,
            captcha field is collected seperately as doesn't have the validationrequired class

            a list item is then built up with an error string,
            the item initially has aria-hidden set to true so it's not read out,
            it is also set to display none initially in the css,
            the item has an id set by modifying the field's label

            the built up item is appended to the validation summary errors unordered list
        */

        var summary = $(form).find('.validation-summary-errors ul');
        // build up list item
        $.each($(form).find('.ValidationRequired'), function (index, requiredField) {
            var label, message = '', markup = '';
            label = $(requiredField).find('label').text();
            message = label + ' is required.';
            markup = '<li id="' + buildSafeLabel(label) + '-message" aria-hidden="true">' + message + '</li>';
            summary.append(markup);
        });

        // have to get captcha field seperately as doesn't have the validationrequired field
        $.each($(form).find('.FormRecaptcha'), function (index, requiredField) {
            var message = '', markup = '';
            message = 'Invalid captcha value.';
            markup = '<li id="captcha-message" aria-hidden="true">' + message + '</li>';
            summary.append(markup);
        });

        // horrible work around to prevent Google's recaptcha required message being read out when you're not on the step with a recaptcha field yet
        $.each($(form), function () {
            if ($(this).find('.FormStep').length > 0) {
                window.setTimeout(function () {
                    $(this).find('.FormStep.hide .FormRecaptcha').hide().attr('aria-hidden', 'true');
                }, 500);

                $(this).find('.Form__NavigationBar__Action').bind('click', function (e) {
                    window.setTimeout(function () {
                        $(this).find('.FormStep:not(.hide) .FormRecaptcha').show().attr('aria-hidden', 'false');
                    }, 500);
                });
            }
        });

        

        // call function that handles error triggers
        onError(form);
    }

    function generateEventId() {
        var randLetter = String.fromCharCode(65 + Math.floor(Math.random() * 26));
        var uniqid = randLetter + Date.now();

        return uniqid;
    }

    // ESC-900 console errors occurred when special characters are used in the field label, preventing forms from being submitted in some cases.
    // Let's strip out special characters. Mmmmkkk.
    function buildSafeLabel(label) {
        return label.toLowerCase().replace(/[^a-zA-Z]/g, "").replace(" ", "-");
    }

    function showHideErrors(form, field) {
        /*
            // any questions ask Laurence

            This function takes the field that has either passed or failed validation,
            determines whether it passed or failed and either hides or shows the item in the summary,
            aria-hidden is also updated so it's not read when the item is hidden,
            but it read when the item is shown.

            The label of the field is manipulated and used to link it with the correct item in the validation summary
        */

        var form = $(form);
        var field = $(field);
        var label = field.find('label').text();
        var id = buildSafeLabel(label);

        // captcha doesn't have a label so look for class and alter id
        if (field.hasClass('FormRecaptcha')) {
            id = 'captcha';
        }

        // hide item is passed validation, else show the item
        if (field.hasClass('ValidationSuccess')) {
            form.find('#' + id + '-message').attr('aria-hidden', 'true').removeClass().hide();
        } else {
            form.find('#' + id + '-message').attr('aria-hidden', 'false').show();
        }

        /*
            if the user submits the form with the same errors as before
            the content of the summary doesn't change, so aria-live won't read it
            therefore we have to make a change somehow. Here we generate a random class
            and assign it to the first visible list item in the summary
        */
        if (form.find('li:visible').length > 0) {
            var eventId = 'error-event-' + generateEventId();
            form.find('li:visible:first').removeClass().addClass(eventId);
        }
    }

    function onError(form) {
        /*
            // any questions ask Laurence

            This function uses Mutation Observer
            https://developer.mozilla.org/en/docs/Web/API/MutationObserver

            It listens for select parts of the DOM being altered -
            there is an observer for the required fields and another for the captcha field

            When the classes on these fields change, it checks to see if either a validation fail
            or success class has been added and if so triggers the showHideErrors function
        */

        var config;

        // settings - looking for attribute changes and the classes to use
        config = {
            attributes: true,
            invalidClass: 'ValidationFail',
            recaptchaClass: 'FormRecaptcha',
            requiredClass: 'ValidationRequired',
            successClass: 'ValidationSuccess'
        };

        // listen for DOM changes
        var observer = new MutationObserver(function(mutations) {
            mutations.forEach(function (mutation, index) {
                // if there is a class change
                if (mutation.attributeName === "class") {
                    var attributeValue = $(mutation.target).prop(mutation.attributeName);
                    // if the class change is for a validation fail or validation success we need to update the validation summary items
                    if (attributeValue.indexOf(config.invalidClass) > -1 || attributeValue.indexOf(config.successClass) > -1) {
                        showHideErrors(form, $(mutation.target));
                    }
                }
            });
        });

        // observe the required fields for changes
        var requiredFields = $(form).find('.' + config.requiredClass);
        $.each(requiredFields, function (index, requiredField) {
            observer.observe(requiredField, config);
        });

        // observe the captcha field for changes
        var recaptchaFields = $(form).find('.' + config.recaptchaClass);
        $.each(recaptchaFields, function (index, recaptchaField) {
            observer.observe(recaptchaField, config);
        });
    }

    $(function () {
        $(settings.trigger).each(init);
        $(settings.navbuttons).each(focusStep);
        // build up manual form field error summary for screenreaders
        $(settings.forms).each(buildErrorList);

        // if age is under 13, reveal parental permission field, otherwise hide it
        if ($('#ContactDetails_DobYear').length > 0) {
            var dayField = $('#ContactDetails_DobDay');
            var monthField = $('#ContactDetails_DobMonth');
            var yearField = $('#ContactDetails_DobYear');
            var dateSelectors = $('#ContactDetails_DobDay, #ContactDetails_DobMonth, #ContactDetails_DobYear');

            $('#ContactDetails_DobDay, #ContactDetails_DobMonth, #ContactDetails_DobYear').on('change', function () {
                var date = new Date();
                var year = date.getFullYear();
                var month = date.getMonth();
                var day = date.getDate();
                var ageLimit = 13;
                var selectedDate = +new Date(yearField.val(), monthField.val() - 1, dayField.val());
                var allowedDate = date.setFullYear(year - ageLimit, month, day);
                var target = yearField.parent('.form-group').data('target');

                if (selectedDate >= allowedDate) {
                    $('#' + target).removeClass('transition-hide');
                } else {
                    $('#' + target).addClass('transition-hide');
                    //var checkbox = $('#' + target).find('[data-custom-checkbox]');
                    //setCheckboxState(checkbox, false);
                }
            });
        }

        // when supported club selected, reveal sign up to club newsletter checkbox
        if ($('#ContactDetails_ClubSupportedID')) {
            var supportedClub = $('#ContactDetails_ClubSupportedID');
            supportedClub.on('change', function () {
                var favClub = $(this);
                var target = $(this).parent('.form-group').data('target');

                if ($(this).val() > 0) {
                    $('#' + target).removeClass('transition-hide');
                } else {
                    $('#' + target).addClass('transition-hide');
                    //var checkbox = $('#' + target).find('[data-custom-checkbox]');
                    //setCheckboxState(checkbox, false);
                }
            });
        }

        // when supported club selected, reveal sign up to club newsletter checkbox
        if ($('#receive_info_from')) {
            var receiveFrom = $('#receive_info_from');
            var target = receiveFrom.data('target');

            $(receiveFrom).find('input[type=checkbox]').on('change', function () {
                if ($(receiveFrom).find('input[type=checkbox]:checked').length > 0) {
                    $('#' + target).removeClass('transition-hide');
                } else {
                    $('#' + target).addClass('transition-hide');
                    //var checkbox = $('#' + target).find('[data-custom-checkbox]');
                    //setCheckboxState(checkbox, false);
                }
            });
        }
        
    })
})(jQuery);;
(function ($) {
	'use strict';

	var settings = {
		trigger: '[data-file-upload]'
	};

	var init = function() {

		var $file_upload = $(this),
			$label = $file_upload.next(),
			$label_text = $label.find('span'),
			default_label = $label_text.html();

		var update = function(e) {
			var fileName = '';

			if (this.files) {
				fileName = e.target.value.split('\\').pop();
			}

			if (fileName) {
				$label_text.html(fileName);
			}
			else {
				$label_text.html(default_label);
			}
		};

		$file_upload.wrap($('<div class="file-upload-container" />'));
		$label.appendTo($file_upload.parent());

		$file_upload.addClass('styled');

		$file_upload.on('change', update);

		// focus/blur fix (Firefox doesn't recognise input[type=file]:focus)
		$file_upload.on('focus', function() {
			$(this).addClass('has-focus');
		});

		$file_upload.on('blur', function() {
			$(this).removeClass('has-focus');
		});

	};

	$(settings.trigger).each(init);

})(window.jQuery);;
(function () {
    'using strict';

    var settings = {
        trigger: '[data-widget="fa-widget"]',
    };

    function init() {

        var element = $(this);
        var clearstyles = setInterval(function () {
            if (element.find('table').length > 0) {
                clearStyles(element);
                updateLinks(element);
                clearInterval(clearstyles);
            }
        }, 1000);  
    }

    function clearStyles(element) {
        $(element).find("*").removeAttr("style");
        $(element).removeClass("hidden");
    }

    function updateLinks(element) {
        element.find('a').each(function () {
            $(this).attr('target', '_blank');
        });
    }

    $(function () {
        $(settings.trigger).each(init);
    });
     
})();;
window.EFL = window.EFL || {};
window.EFL.fixtures = window.EFL.fixtures || {};

window.EFL.fixtures.buildClubLinksiFollow = function (linkData, fixtureData, matchCenterText) {

    var homeClubLinks = getClubLinks(linkData, fixtureData.homeId);
    var awayClubLinks = getClubLinks(linkData, fixtureData.awayId);
	var firstLink = ''
	var lastLink = '';

    var hasLinks = typeof (homeClubLinks) !== 'undefined' || typeof (awayClubLinks) !== 'undefined';

    if (typeof (homeClubLinks) !== 'undefined' && homeClubLinks != null && typeof (homeClubLinks.URL) !== 'undefined') {
        if (homeClubLinks.IsOP) {
            if (fixtureData.isFixture) {
				
                //add MC link
				firstLink = '<a target="_blank" href="' + homeClubLinks.URL + '/r/g/' + fixtureData.matchId + '" class="">' + fixtureData.homeTeamName + '<span class="game-list__watch-cta">' + matchCenterText + '</span></a>';
            }

        } else {
            if (fixtureData.isFixture) {

                //add MC link
                if (typeof (homeClubLinks.HomeMC) !== 'undefined') {
					firstLink = '<a target="_blank" href="' + homeClubLinks.HomeMC + '" class="">' + fixtureData.homeTeamName + '<span class="game-list__watch-cta">' + matchCenterText + '</span></a>';
                }
                else {
					firstLink = '<a target="_blank" href="' + homeClubLinks.URL + '" class="">' + fixtureData.homeTeamName + '<span class="game-list__watch-cta">' + matchCenterText + '</span></a>';
                }
            }

        }
    }

    if (typeof (awayClubLinks) !== 'undefined' && awayClubLinks != null && typeof (awayClubLinks.URL) !== 'undefined') {
        if (awayClubLinks.IsOP) {
            if (fixtureData.isFixture) {

                //add MC link
				lastLink = '<a target="_blank" href="' + awayClubLinks.URL + '/r/g/' + fixtureData.matchId + '" class="">' + fixtureData.awayTeamName + '<span class="game-list__watch-cta">' + matchCenterText + '</span></a>';
            }

        } else {
			if (fixtureData.isFixture) {
                
                //add MC link
                if (typeof (awayClubLinks.AwayMC) !== 'undefined') {
					lastLink = '<a target="_blank" href="' + awayClubLinks.AwayMC + '" class="">' + fixtureData.awayTeamName + '<span class="game-list__watch-cta">' + matchCenterText + '</span></a>';
                }
                else {
					lastLink = '<a target="_blank" href="' + awayClubLinks.URL + '" class="">' + fixtureData.awayTeamName + '<span class="game-list__watch-cta">' + matchCenterText + '</span></a>';
                }
            }
        }
    }

    if (typeof (matchData) !== 'undefined' && matchData.data[i].Links.length == 0) {
        firstLink = $(firstLinks);
        firstLink.addClass('empty');
        firstLink = firstLinks.outerHTML;
    }

    if (typeof (matchData) !== 'undefined' && matchData.data[i].Links.length <= 1) {
        lastLink = $(lastLinks);
        lastLink.addClass('empty');
        lastLink = lastLinks.outerHTML;
    }

    return {
        hasLinks: hasLinks,
        firstLink: firstLink,
        lastLink: lastLink
    }

};

function getClubLinks(linkData, clubId) {
    var clubLinks = null;
    if (typeof (linkData) !== 'undefined' && typeof (clubId) !== 'undefined' && typeof (linkData.length) !== 'undefined') {
        var found = false;
        var i = 0;
        var teamId;
        while (!found && i < linkData.length) {
            if (typeof (linkData[i].OptaId) != 'undefined') {
                teamId = linkData[i].OptaId.replace('t', '');

                found = teamId == clubId;
            }

            if (found) {
                clubLinks = linkData[i];
            }
            i++;
        }


    }

    return clubLinks;
}

function getGeoData() {

    var regionResult = null;
    var countryResult = null;

    if (findDLIndex('league-blackout-excluded') && findDLIndex('video-region') && findDLIndex('video-country') && window.dataLayer[findDLIndex('league-blackout-excluded')]['league-blackout-excluded'] && window.dataLayer[findDLIndex('video-region')]['video-region'] && window.dataLayer[findDLIndex('video-country')]['video-country']) {
        var videoRegion = window.dataLayer[findDLIndex('video-region')]['video-region'];
        var videoCountry = window.dataLayer[findDLIndex('video-country')]['video-country'];
        var leagueBlackoutExcluded = window.dataLayer[findDLIndex('league-blackout-excluded')]['league-blackout-excluded'];
        if (leagueBlackoutExcluded === 'true') {
            videoRegion += 'P';
        }
        regionResult = window.EFL.Regions[videoRegion];
        countryResult = videoCountry;
    }

    return geoData = {
        "Region": regionResult,
        "Country": countryResult
    };
}



/* iFollow Fixtures Block */
(function () {
    'using strict';
    var settings = {
		trigger: '[data-widget="ifollow-block"]',
    };

    var userGeoData;

    function init() {
		var element,
			root,
			originalWidgetElement,
			teamId,
			matchData,
			optaWidget,
			matchCenterText,
			isUpdating = false,
			scopeId,

		element = $(this);

        var optawidgetid = element.find('opta-widget').attr('data-widget-id');

		scopeId = element.data('scopeId');

		matchCenterText = element.attr("data-match-center-text");

        element.hide();
        root = element;

        originalWidgetElement = element.find('opta-widget');

        if (originalWidgetElement.length > 0) {
            originalWidgetElement = originalWidgetElement[0].outerHTML;
        }

        /* Bind Events */
		window.EFL.MatchCentre.registerWidget(optawidgetid, element, onDrawn, onError);

		element.show();

		if (typeof (Opta) !== 'undefined') {
			//     Opta.start();
		} else {
			console.log('Opta unavailable');
		}

        /* Opta OnDrawn event callback - executes when opta renders widget */
		function onDrawn(element, optaItem) {
            var optaItemDataScopeId = optaItem.widget.attr['data-scope-id'];
            var elementDataScopeId = element[0].attributes['data-scope-id'].value;
            if (optaItemDataScopeId != elementDataScopeId) {
                return;
			}


			optaItem.widget.pause();

            element.addClass('custom');
            console.log("onDrawn opta-fixtures");
            optaWidget = optaItem;
            initialised = element.attr('data-initialised') === 'true';

            // if (!initialised) {
            element.attr('data-initialised', 'true');

            var teamId = optaItem.widget.attr_original.team;
            var allTbodies = element.find('tbody');
            var elements = element.find('tbody.Opta-fixture');
            var titles = element.find('tbody .Opta-title');
            var maxRows = typeof (element.data('maxresults')) != 'undefined' && !isNaN(parseInt(element.data('maxresults'))) && parseInt(element.data('maxresults')) > -1 ? parseInt(element.data('maxresults')) : elements.length;

			var fixturesKept = 0;

			var gameList = $('<ul class="game-list"></ul>');
			var optaDiv = root.children().first();

			optaDiv.append(gameList);

            for (var i = 0; i < allTbodies.length; i++) {

                var thisTbody = allTbodies[i];

                if (maxRows == -1 || fixturesKept < maxRows) {
                    if ($(thisTbody).hasClass('Opta-fixture')) {
                        var rowData = getOptaRowData(thisTbody, optaItem);
						constructView(fixturesKept, thisTbody, teamId, rowData, gameList);
                        fixturesKept++;
                    }
                } else {
                    $(thisTbody).remove();
                }
			}


            element.attr('data-initialised', 'true');


            root.css('height', '');
            element.addClass('widget-loaded');
            console.log('fixtures drawn');

			optaDiv.children().first().hide();
        };

        function setNoDataMessage(element, selector, text) {
            if (element.length > 0) {
                var message = element.find(selector);
                if (message.length > 0) {
                    message.empty().append(text);
                }
            }
        };

        //scrape row and get out all the relevant data
        function getOptaRowData(element, data) {

            var rowData = null;

            /*if (element.attr('data-period').toLowerCase() === 'prematch') {

            }*/

            if (typeof (data) != 'undefined' && typeof (data.widget) != 'undefined' && typeof (data.widget.matches) != 'undefined') {

                var className = element.className.match(/Opta-Match-[0-9]*/);

                if (className.length > 0) {
                    var matchIds = className[0].match(/[0-9]+/);

                    if (typeof (matchIds) != 'undefined' && matchIds.length > 0 && matchIds[0] != '') {

                        var matchId = matchIds[0];

                        var matches = data.widget.matches;
                        var subMatches;
                        for (var i = 0; i < matches.length; i++) {
                            subMatches = matches[i].matches;

                            if (typeof (subMatches) != 'undefined') {
                                for (var j = 0; j < subMatches.length; j++) {
                                    if (subMatches[j].id.toString() === matchId) {
                                        rowData = subMatches[j];
                                    }
                                }
                            }
                        }
                    }
                }
            }

            return rowData;
        }

        /* Opta OnError event callback - executes when opta finds a problem */
        function onError(element, optaItem) {
            console.log("onError opta-fixtures");
            optaWidget = optaItem;
            element.addClass('widget-loaded');
            root.css('height', '');

            //Replace No data message
            //Check for the override 
            if (EFL && EFL.optaNoDataText) {
                setNoDataMessage(element, ".Opta-Error .Opta-Cf p", EFL.optaNoDataText);
            }
            else {
                //Use the old original value if nothing set
                setNoDataMessage(element, ".Opta-Error .Opta-Cf p", "There are currently no fixtures to display. Please check back soon.");
            }
        }

		function constructView(index, element, teamId, optaData, container) {
         
			element = $(element);

			var div = $('<li class="game-list__game"></li>')

            //gathers data from the opta widget
            var data = gatherRowData(element, teamId, optaData);

            var time;
            var postponed = false;
			var tbc = false;
			var links;

            if (element.attr('data-period').toLowerCase() === 'postponed') {
                time = 'PP';
                postponed = true;
            } else if (data.time.indexOf("TBC") == -1) {
                time = data.time;
			}
			else {
				tbc = true;
			}

			div.append($('<div class="game-list__team game-list__team--home"><div class="game-list__badge">' + data.homeCrest + '</div><div class="game-list__team-name sr-only">' + data.homeTeamName + '</div></div>'));
			if (data.hasScore) {
				div.append($('<div class="game-list__info"><div class= "game-list__score">' + data.homeScore + '</div ><div class="game-list__versus">v</div><div class="game-list__score">' + data.awayScore + '</div></div>'));
			}
			else {
				div.append($('<div class="game-list__info"><div class="game-list__time">' + time + '</div></div >'));
			}
			div.append($('<div class="game-list__team game-list__team--away"><div class="game-list__badge">' + data.awayCrest + '</div><div class="game-list__team-name sr-only">' + data.awayTeamName + '</div></div>'));

            if (!postponed) {

				links = window.EFL.fixtures.buildClubLinksiFollow(window.EFL.fixturesClubUrls, data, matchCenterText);

				if (typeof (links) !== 'undefined' && typeof (links.firstLink) !== 'undefined') {
					if (links.hasLinks) {
						div.find('.game-list__team--home').append($('<div class="game-list__watch">' + links.firstLink + '</div>'));
					}
                }

				if (typeof (links) !== 'undefined' && typeof (links.lastLink) !== 'undefined') {
					if (links.hasLinks) {
						div.find('.game-list__team--away').append($('<div class="game-list__watch">' + links.lastLink + '</div>'));
					}
                }
			}

            // add name of game to each link so that screen readers have some context when reading it
			div.find('a').prepend('<span class="sr-only">' + data.homeTeamName + ' versus ' + data.awayTeamName + ', ' + data.date + ', </span>');

			container.append(div);
        }

        function gatherRowData(element, teamId, optaData) {

            var data = {};
            var homeElement = element.find('td.Opta-Home').first();

            var awayElement = element.find('td.Opta-Away').first();
            var teamIdRegex = new RegExp('Opta-Team-[0-9]+');
            var homeId = teamIdRegex.exec(homeElement[0].className);


            if (homeId != null) {
                homeId = homeId[0].replace('Opta-Team-', '');
            }

            var awayId = teamIdRegex.exec(awayElement[0].className);

            if (awayId != null) {
                awayId = awayId[0].replace('Opta-Team-', '');
            }

            var classes = homeElement.attr('class');
            var teamIds = classes.match(/[A-z]*-[A-z]*-[0-9]*/g);
            var homeTeamId;
            //  var teamId = optaWidget.widget.attr_original.team;
            if (teamIds.length > 0) {
                homeTeamId = teamIds[0].replace('Opta-Team-', '');
            }

            var link = $('<a href=""></a>');
            var venueElement = element.find('.Opta-Venue');
            var venue = venueElement.html() || '';
            venueElement.parent().remove();;


            var competition = '';
            if (typeof (optaData) !== 'undefined' && optaData !== null && typeof (optaData.competition) !== 'undefined' && typeof (optaData.competition.full) !== 'undefined') {
                competition = optaData.competition;
            }

            var time = element.find('.Opta-Outer.Opta-Time').detach().html();
            var dateLocal = new Date(parseInt(element.attr('data-date')));

            //modify for GMT/BST
            dateLocal.setHours(dateLocal.getHours() + 1);

            var date = new Date(dateLocal.getUTCFullYear(), dateLocal.getUTCMonth(), dateLocal.getUTCDate(), dateLocal.getUTCHours(), dateLocal.getUTCMinutes(), dateLocal.getUTCSeconds());

            // Laurence:EFLTECH-2725: check against opta returned data instead of time to cut out timezone issues
            // prematch - means before or during game, fulltime - means result
            // EFLRM-211: Caused issues when postponements appeared so taking a safer route to hide results - assume all are fixtures
            // unless it is definitely a result
            //var isFixture = element.attr('data-period').toLowerCase() === 'prematch';
            var isFixture = element.attr('data-period').toLowerCase() != 'fulltime';

            var homeTeamName;
            var awayTeamName;
            var time;

            // check if a game has a postponed status
            if (element.attr('data-period').toLowerCase() === 'postponed') {
                time = 'PP';
            }

            var homeCrest, awayCrest, homeScore, awayScore;
            var gameType;
            var matchId = element.attr('data-match');
            var spacer = $('<tbody class="spacer"><tr><td colspan="12"></td></tr></tbody>');
			var penalties = element.find('.Opta-penalties');

            if (isFixture) {
                element.addClass('fixture-configuration');
                if (typeof (teamId) === 'undefined' || teamId === '' || isClubId(homeTeamId, teamId)) {
                    gameType = 'H';
                    homeCrest = element.find('.Opta-Image-Team-' + homeTeamId).detach().html();
                    awayCrest = element.find('.Opta-Image-Team').detach().html();
					homeScore = element.find('.Opta-Score.Opta-Team-' + homeTeamId).detach().text();
					awayScore = element.find('.Opta-Score').detach().text();
                    homeTeamName = element.find('.Opta-TeamName.Opta-Home').detach().html();
                    awayTeamName = element.find('.Opta-TeamName.Opta-Away').detach().html();
                }
                else {
                    gameType = 'A';
                    awayCrest = element.find('.Opta-Image-Team-' + homeTeamId).detach().html();
                    homeCrest = element.find('.Opta-Image-Team').detach().html();
					awayScore = element.find('.Opta-Score.Opta-Team-' + homeTeamId).detach().text();
					homeScore = element.find('.Opta-Score').detach().text();
                    awayTeamName = element.find('.Opta-TeamName.Opta-Home').detach().html();
                    homeTeamName = element.find('.Opta-TeamName.Opta-Away').detach().html();
                }

            }
            else {
                element.addClass('results-configuration');

                if (typeof (teamId) === 'undefined' || teamId === '' || isClubId(homeTeamId, teamId)) {
                    gameType = 'H';
                    homeCrest = element.find('.Opta-Image-Team-' + homeTeamId).detach().html();
                    awayCrest = element.find('.Opta-Image-Team').detach().html();
                }
                else {
                    gameType = 'A';
                    awayCrest = element.find('.Opta-Image-Team-' + homeTeamId).detach().html();
                    homeCrest = element.find('.Opta-Image-Team').detach().html();
                }

                // Results page (fixture has a data-period of FullTime)
				homeScore = element.find('.Opta-Score.Opta-Team-' + homeTeamId).detach().text();
				awayScore = element.find('.Opta-Score').detach().text();
                homeTeamName = element.find('.Opta-TeamName.Opta-Home').detach().html();
                awayTeamName = element.find('.Opta-TeamName.Opta-Away').detach().html();

            }

            var item;

            // Get the BSI form the page
            var blackoutSheetInfo = window.EFL.blackoutSheetInfo;
            // If we have data
            if (blackoutSheetInfo != null) {
                // Set ITEM to the Object in BSI that matched the current match in the loop
                var thisMatch = $.grep(blackoutSheetInfo, function (obj) { return obj.OptaId === "g" + matchId; })[0];

                if (thisMatch) {
                    // Make sure we have the UserGeoData
                    if (userGeoData == null) {
                        userGeoData = getGeoData();
                    }
                    // If the data we need isn't null
                    if (userGeoData != null && userGeoData.Region != null && userGeoData.Country != null) {
                        // Get the Region which matches where we are
                        var region = $.grep(thisMatch.RegionAvailabilities, function (obj) { return obj.Region === userGeoData.Region })[0];

                        // If we have the region
                        if (region != null) {
                            // Check the match is AvailableOnIfollow for this region
                            if (region.AvailableOnIfollow != null && region.AvailableOnIfollow == true) {
                                data.availableOnIfollow = true;
                            }
                            // If we're in the UK - also check for TV Channel
                            if (userGeoData.Region == 0) {
                                // Get the TV channel that matches the country we are in
                                var tvChannel = $.grep(region.TVChannels, function (obj) { return obj.CountryCode === userGeoData.Country })[0];
                                if (tvChannel != null) {
                                    data.tvChannel = tvChannel;
                                }
                            }
                        }
                    }
                }
            }

            data.awayCrest = awayCrest;
            data.homeCrest = homeCrest;
			data.awayScore = awayScore;
			data.homeScore = homeScore;
            data.homeId = homeId;
            data.awayId = awayId;
            data.awayTeamName = typeof (awayTeamName) != 'undefined' && awayTeamName != 'undefined' ? awayTeamName : '';
            data.homeTeamName = homeTeamName || '';
            data.time = time;
            data.gameType = gameType || '';
            data.date = date.toDateString();
            data.competition = competition;
            data.venue = venue || '';
            data.matchId = matchId;
            data.spacer = spacer;
            data.penalties = penalties;
            data.isFixture = isFixture;
            data.customLinkText = root.data('customLinkText') || 'Register on iFollow';
			data.customHighlightsLinkText = root.data('customHighlightsLinkText') || 'Watch on iFollow';
			data.hasScore = !isNullOrWhitespace(homeScore);

            return data;
        }

        function isClubId(id, clubIds) {
            isClub = false;
            if (typeof (clubIds) !== 'undefined') {
                //if we have more than one id for the club we need to split them and check against each one
                if (clubIds.toString().indexOf(',') !== -1) {
                    var ids = clubIds.toString().split(',');
                    var i = 0;
                    while (!isClub && i < ids.length) {
                        isClub = id == ids[i];
                        i++;
                    }
                }
                else {
                    isClub = id == clubIds;
                }
            }
            return isClub;
        }

		function isNullOrWhitespace(input) {
			return !input || !input.trim();
		}
    }

    $(settings.trigger).each(init);

})();

function findDLIndex(key) {
    var index;
    for (var i = 0; i < window.dataLayer.length; ++i) {
        if (typeof window.dataLayer[i][key] !== 'undefined') {
            index = i;
            break;
        }
    }
    return index;
}
;
(function ($, Ps) {
    'use strict';

    var settings = {
        trigger: '[data-news-archive-filter]',
        openEvent: 'news-archive-filter-open',
        selectEvent: 'news-archive-filter-select',
        minResultsForSearch: 10,
        updatesTarget: 'data-updates-target',
        updateURL: 'data-url-for-data-request',
        broadcastJsonEvent: 'filters-data-update'
    };


    function init() {
        var filter = $(this);
        var id = filter.attr('id');
        var header = filter.find('.header');
        var overlay = filter.find('.filter-overlay');
        var filterInput = overlay.find('input');
        var results = overlay.find('li a');
        var numberOfResults = results.length;
        var selectedValueElement = filter.find('[data-selected-value]');
        var selectedValue = overlay.find('li').first().html();
        var isJSON = filter.attr('data-is-json') === 'true' || false;
        selectedValueElement.html(selectedValue);

        if (numberOfResults > settings.minResultsForSearch) {
            filter.addClass('more-than-ten-items');
        }

        //Perfect scrollbar
        Ps.initialize(filter.find('ul')[0], {
            scrollYMarginOffset: 5,
        });

        function onSelect(event, id, value, href) {
            selectedValueElement.html(value);
            close();
        }

        function openOverlay(e) {
            var element = $(this).parent();

            e.preventDefault();

            element.trigger(settings.openEvent);

            if (element.hasClass('active')) {
                element.removeClass('active');
            } else {
                element.addClass('active');
            }
        }

        function autocomplete() {
            var val = $(this).val();
            var results = overlay.find('li');
            results.removeClass('hidden');

            if (val !== '') {
                results.each(function (index, element) {
                    element = $(element);

                    if (element.html().toLowerCase().indexOf(val.toLowerCase()) === -1) {
                        element.addClass('hidden');
                    }
                });

                Ps.update(filter.find('ul')[0]);
            }
        }

        function selectResult(e) {

            var $context = $(this);
            var $parent = $context.closest(settings.trigger);
            var $collapse = $parent.find('[data-toggle=collapse]');
            var $collapse_target = $( $collapse.attr('data-target') );


            e.preventDefault();

            if ($collapse_target.length) {
                $collapse_target.collapse('hide');
            }

            $context.trigger(settings.selectEvent, [$parent.attr('id'), $context.html(), $context.attr('href')]);

            if (typeof $parent.attr(settings.updatesTarget) !== 'undefined') {
                var id = $parent.attr(settings.updatesTarget);

                updateFilters(id, $context.attr('href'));
            }
        }

        function close() {
            filter.removeClass('active');
        }

        function makeApiCall(url, target) {
            var call;

            if (typeof (call) !== 'undefined' && call.progress().readyState !== 4) {
                call.abort();
            }

            call = $.get(url, function (data) {

                if (isJSON) {
                    broadcastJSON(settings.broadcastJsonEvent, data);
                }
                else {
                    populateFilter(data, target);
                }

            }).fail(function () { populateFilter('this is an error message'); });

            var promise = call.promise();
            return promise;

        }

        function populateFilter(data, target) {

            $('#' + target + ' ul').after(data);
            $('#' + target + ' ul' + ':not(:last)').remove();
            $('#' + target + ' ul' + ':not(:last)').remove();

            rebindFilters(target);


            //EFL-1276 - this was used when eg the season list is filterd and would reset the selected value to All Seasons
            //this caused inconsistencies with the content actually filtered and the selected value.
            //var value = $('#' + target).find('ul li a[href="#"]').html();
            //$('#' + target).find('[data-selected-value]').html(value);
        }

        function broadcastJSON(eventName, data) {
            window.EFL.eventDispatcher.dispatch(eventName, data);
        }

        function updateFilters(id, href) {
            var url;

            $(id).each(function () {
                var object = [];
                object[0] = $(this).attr('id');
                object[1] = '';
                object[2] = 'true';

                url = $(this).attr(settings.updateURL) + '=' + href.replace(/#/g, '');
                makeApiCall(url, $(this).attr('id'));

                //this was supposed to update the url but it isn't and was causing other issues so have disabled it
                //if it needs revisited 
                //window.EFL.eventDispatcher.dispatch('update-url', object);

            });
            
        }

        function rebindFilters(id) {

            $('#' + id + ' .filter-overlay li a').on('click', selectResult);

            var numberOfResults = $('#' + id + ' .filter-overlay li').length;

            if (numberOfResults > settings.minResultsForSearch) {
                $('#' + id).addClass('more-than-ten-items');

            }
            else {
                $('#' + id).removeClass('more-than-ten-items');
            }

            //Perfect scrollbar
            Ps.initialize($('#' + id + ' .filter-overlay').find('ul')[0], {
                scrollYMarginOffset: 5,
            });
        }


        //wire events
        header.on('click', openOverlay);
        filter.on(settings.selectEvent, onSelect);
        filterInput.on('keyup', autocomplete);
        results.on('click', selectResult);
        $(window).on('resize', close);
        $(document.body).on('click touchstart', function (event) {
            if ($(event.target).parents('.filter-dropdown').length === 0) {
                close();
            }
        });
    }


    $(function () {
        $(settings.trigger).each(init);
    });

})(window.jQuery, window.Ps);;
(function ($) {
    'use strict';

    var settings = {
        trigger: '[data-news-archive-filters]',
        openEvent: 'news-archive-filter-open',
        selectEvent: 'news-archive-filter-select',
        filterClearTrigger: '.js-clear-filters'
    };

    var filters = {};

    function setFilters(loadValues) {

        $('[data-news-archive-filter]').each(function () {

            var id = $(this).attr('id');

            var href = loadValues[id];

            if (href === '') {
                href = '#';
            }

            var value = $('#' + id + ' a[data-linkmatch = "' + href + '"]').html();

            $(this).find('[data-selected-value]').html(value);
        });
    }

    function init() {
        var $filters = $(this);

        $filters.on(settings.openEvent, function (e, isActive) {
            $filters.find('[data-news-archive-filter]').removeClass('active');
        });

        $filters.on(settings.selectEvent, function (event, id, value) {
            filters[id] = value;
        });

        window.EFL.eventDispatcher.registerHandler('set-filters', setFilters);
    }

    $(function () {
        $(settings.trigger).each(init);
    })

})(window.jQuery);;
window.EFL = window.EFL || {};

window.EFL.NewsArchiveFilteres = (function ($, EFL) {
	"use strict";

	var settings = {
		trigger: '[data-news-archive-filters]',
		placeholderElement: '#defaults',
		baseUrl: '',
		targetContainer: '[data-article-grid-wrapper]',
		dataWrapper: '.article-filtered-container',
		layoutIndicator: 'data-container-layout',
		selectEvent: 'news-archive-filter-select',
		buttonTarget: '.js-view-more',
		pagesViewed: 'data-news-achive-additional',
		butonHTML: '<p class="button-container  col-xs-12"><a href="#" class="js-view-more btn btn-primary border-primary bg-primary">View more</a></p>',
		moreTarget: 'data-has-more',
		updateURL: 'data-url-for-data-request',
		viewMoreCount: 0,
		isEditorVideoSelection: $("#EditorVideoSelection").length > 0
	};
	var call, url;

	//make api call
	function makeCall(url, target, clearExisting, more, matchHeights, setFocus){
		//console.log('call made');
		if (typeof (call) !== 'undefined' && call.progress().readyState !== 4) {
			call.abort();
		}

		call = $.get(url, function (data) {
			settings.viewMoreCount += 1;
			populate(data, target, clearExisting, more, matchHeights, setFocus);
		}).fail(function () { populate("this is an error message") });

		var promise = call.promise();

		return promise;

	}

	//populate Article list
	function populate(data, target, clearExisting, more, matchHeights, setFocus) {

		var $newBlock = $(data);
		$(target).last().append($newBlock);

		if (more && EFL.Dice.useDiceForVideo) {
			EFL.DiceVideoAccess.loadThumbnailIndicatorsInElement($newBlock);
		}

		if (clearExisting){
			$(settings.dataWrapper + ':not(:last)').remove();
		}

		if (setFocus) {
			//if in selection widget. if this is needed in the video archive will need to adjust offset
			if (settings.isEditorVideoSelection) {
				$('html,body').animate({
					scrollTop: $newBlock.find('a').first().offset().top-20
				});
			}
			$newBlock.find('a').first().focus();
		}
		
		if (more) {
			moreButton();
		}

		if (matchHeights) {
			articleMatchHeights();
		}

		if (window.EFL.adition && window.EFL.adition.functions) {
			window.EFL.adition.functions.check_for_instances();
		}
	}

	function articleMatchHeights(){
		$('.match-height').matchHeight();
		$('.match-height-outer').matchHeight();
	}

	function splitQueryString(qString){
		//create object from query string
		var pairs = qString.slice(1).split('&'),
			result = {};

		pairs.forEach(function (pair) {
			pair = pair.split('=');
			result[pair[0]] = decodeURIComponent(pair[1] || '');
		});

		JSON.parse(JSON.stringify(result));

		return result;
	}

	function createQueryString(object){
		//create query string from object
		var string = "?", x = 0, property;

		for(property in object) {
			string += property + '=' + object[property];
			x++;

			if(x !== Object.keys(object).length) {
				string += '&';
			}
		}

		return string;
	}

	function changeFilterURL(key, value, resetPage){
		var urlObject, url;
		//get current filter from page
		url = $(settings.placeholderElement).val();
		urlObject = splitQueryString(url);
		urlObject[key] = value;

		if (resetPage == "true") {
			urlObject["page"] = "1";
			$(settings.placeholderElement).attr(settings.pagesViewed, '1');
		}

		//check layout required - update
		urlObject["layout"] = $('[' + settings.layoutIndicator + ']').attr(settings.layoutIndicator);
		url = createQueryString(urlObject);


		//copy new filter options to page
		$(settings.placeholderElement).val(url);


		return url;
	}

	function changeUrlEvent(object) {
		var url;
		changeFilterURL(object[0], object[1], object[2]);
		url = $(settings.placeholderElement).val();
		makeCall(settings.baseUrl + url, settings.targetContainer, true, true, true);
		moreButton();
	}

	//show/hide, bind and unbind show more button depending on page status.
	function moreButton() {

		if ($(settings.dataWrapper).last().attr(settings.moreTarget) === "True"){
			if ($(settings.buttonTarget).length > 0) {
				//$(settings.dataWrapper).last().after(butonHTML);
			}

			$(settings.buttonTarget).unbind('click');

			$(settings.buttonTarget).show();

			$(settings.buttonTarget).on('click', function (event) {
				event.preventDefault();

				var nextPage = parseInt($(settings.placeholderElement).attr(settings.pagesViewed));
				nextPage++;
				$(settings.placeholderElement).attr(settings.pagesViewed, nextPage);
				makeCall(settings.baseUrl + changeFilterURL('page', nextPage), settings.targetContainer, false, true, true, true);
			});
		}

		else{
			$(settings.buttonTarget).hide();
		}
	}

	function onPageLoad() {
		var loadValues = splitQueryString($(settings.placeholderElement).val());
		window.EFL.eventDispatcher.dispatch('set-filters', loadValues);
		var url = $(settings.placeholderElement).val();
		makeCall(settings.baseUrl + url, settings.targetContainer, true, true, true);

	}

	function clearContent() {
		var url;
		$('[data-news-archive-filter]').each(function () {
			var key = $(this).attr('id'), value = "";
			url = changeFilterURL(key, value, "true");
			makeCall(settings.baseUrl + url, settings.targetContainer, true, true, true);
			moreButton();
		});
	}

	/* For use in editor for selecting a video*/
	$('#videoSelectorSearch').on('click', function () {
		url = changeFilterURL('searchTerm', $('#searchTerm').val(), "true");
		makeCall(settings.baseUrl + url, settings.targetContainer, true, true, true);
		moreButton();
	});

	/* For use in editor for reseting the search*/
	$('#videoSelectorReset').on('click', function () {
		$("#searchTerm").val('');
		$("#videoSelectorSearch").trigger('click');
	});

	$('#searchTerm').keypress(function (e) {
		var key = e.which;
		if (key == 13)  // the enter key code
		{
			url = changeFilterURL('searchTerm', $('#searchTerm').val(), "true");
			makeCall(settings.baseUrl + url, settings.targetContainer, true, true, true);
			moreButton();
		}
	});

	function init() {
		settings.baseUrl = $(this).attr(settings.updateURL);

		$(this).on(settings.selectEvent, function (event, id, value, href) {
			makeCall(settings.baseUrl +changeFilterURL(id, href.replace(/#/g, ''), "true"), settings.targetContainer, true, true, true);
			moreButton();

		});
	   
		moreButton();
		onPageLoad();
		window.EFL.eventDispatcher.registerHandler("clear-content", clearContent);
		window.EFL.eventDispatcher.registerHandler("update-content", clearContent);
		window.EFL.eventDispatcher.registerHandler("update-url", changeUrlEvent);

	}

	$(function () {
		$(settings.trigger).each(init);
	})

})(window.jQuery, window.EFL);;

(function($) {
  "use strict";

  var settings = {
      trigger: '[data-news-archive-layout-switch]',
      changeTo: 'news-archive-layout',
      layoutIndicator: 'data-container-layout',
      articleContainer: '.article-grid-container'
  };



  function init() {

    $(this).on('click', function(){

        var element = $(settings.articleContainer);

        if (element.children().length == 1)
        {
            return;
        }
    	$(settings.trigger).each(function(){
    		$(this).removeClass('active');
    	});

    	$(this).addClass('active');

    	stripClasses(element);
    	element.addClass('layout-' + $(this).data(settings.changeTo));
    	$('[' + settings.layoutIndicator +']').attr(settings.layoutIndicator, $(this).data(settings.changeTo));
    	
    })

  }

  function stripClasses(element){
  	  element.removeClass(function (index, css) {
		  return (css.match (/(^|\s)layout-\S+/g) || []).join(' ');
	  });
  }


  $(function() {
    $(settings.trigger).each(init);
  })

})(jQuery)
;


(function ($) {
    "use strict";

    var settings = {
        trigger: '[data-news-pullout]',
        overlapWrapper: '.news-article-meta'
    };

    function init(element) {

        window.EFL.eventDispatcher.registerHandler('addthis-loaded', calcOverlap);

    }

    function calcOverlap() {

        //Put this in as a timeout to allow the SVGs from addthis to render.
        
        setTimeout(function () {
            var $elements = $(settings.trigger);
            
            if ($elements.length > 0) {
                var $pullout = $elements.first();

                var $overlap = $(settings.overlapWrapper);

                if ($overlap.length > 0) {
                    var pulloutTop = $pullout.offset().top;
                    var overlapBottom = $(settings.overlapWrapper).first().offset().top + $(settings.overlapWrapper).first().height();
                   
                    var currentBreakpoint = findBootstrapEnvironment();

                    if ((currentBreakpoint === "lg") && (pulloutTop <= overlapBottom)) {
          
                        var amount = (overlapBottom - pulloutTop) + 40;
                        $elements.css({ 'position': 'relative', 'top': amount + 'px' });

                    } else if (currentBreakpoint !== "lg") {
                        $elements.removeAttr('style');
                    }
                } 
            }
        }, 100);
    }

    $(function () {
        init();
        $(window).on('throttled-resize', calcOverlap);
    })
})(jQuery);
;
/*
	Initialises Owl Carousel for news category items

	Example usage:

  <ul class="owl-carousel" data-carousel="news-category">
    <li> example 1 </li>
    <li> example 2 </li>
    <li> example 3 </li>
    <li> example 4 </li>
  </ul>
*/

(function ($) {
    "use strict";

    var settings = {
        trigger: '[data-carousel="simple-loop"]',
    };

    function resetFocus(owl, index) {

        owl.find('.owl-item:not(.cloned) a').eq(index - 1).focus();
        window.EFL.eventDispatcher.deregisterHandler("slide-moved", function () {
            resetFocus(owl);
        });
    }

    function init(index, element) {

        var owl = $(this);

        owl.owlCarousel({
            loop: false,
            margin: 10,
            autoWidth: true,
            nav: false,
            onInitialized: function (event) {

                owl.find('.owl-item:not(.cloned) a').on('focus', function (e) {
                    e.preventDefault();
                });

                owl.find('.owl-item:not(.cloned) a').first().one('focus', function (e2) {

                    owl.trigger('stop.owl.autoplay');
                    owl.trigger('to.owl.carousel', 0);

                    owl.find('.owl-item:not(.cloned) a').on('keydown', function (e) {

                        if (e.keyCode == '9') {
                            window.EFL.eventDispatcher.registerHandler("slide-moved", function () {
                                resetFocus(owl);
                            });
                            /*Buttons to not work after tabbing.  Need to revisit if requirement*/
                            /*owl.trigger('next.owl.carousel');*/
                        }
                    });

                });

                owl.on('translated.owl.carousel', function (event) {

                    index = event.item.index;

                    var tempFix = index - (event.relatedTarget.clones().length / 2);
                    if (tempFix > 0) {
                        index = tempFix;
                    }

                    window.EFL.eventDispatcher.dispatch("slide-moved", index);
                });

                owl.find('.owl-item:not(.cloned) a').last().blur(function () {

                    owl.find('.owl-item:not(.cloned) a').off('keydown');

                });
            },
            onDrag: function () {
                owl.find('.transition').addClass('transition-off').removeClass('transition');
            },
            onDragged: function () {
                owl.find('.transition-off').addClass('transition').removeClass('transition-off');
            }

        });

        var $customControls = $('<div class="custom-controls">');
        var $customPrev = $('<a href="#"><span class="icon-Small-Back-Arrow" aria-hidden="true"></span><span class="sr-only">Previous</span></a>');
        var $customNext = $('<a href="#"><span class="icon-Forward-Arrow" aria-hidden="true"></span><span class="sr-only">Next</span></a>');

        $customControls.append($customPrev).append($customNext);

        owl.parent().find('.header-row').append($customControls);

        // Custom Navigation Events
        $customNext.on('click', settings.next, function (e) {
            e.preventDefault();
            owl.trigger('next.owl.carousel');
        });

        $customPrev.on('click', settings.prev, function (e) {
            e.preventDefault();
            owl.trigger('prev.owl.carousel');
        });
    }

    $(function () {
        $(settings.trigger).each(init);
    })
})(jQuery);
;

(function ($) {
    "use strict";

    var settings = {
        careerBlock: '[data-career-block]',
        careerRow: '[data-career-row]' 
    };

    function init() {

        var $careerBlock = $(this);
        var $careerCount = $careerBlock.find(settings.careerRow);

        if ($careerCount.length > 5) {
            $careerCount.slice(5).hide();

            var $moreButton = $('<div class="container-fluid"><a class="btn btn-primary" href="#">View more</a></div>');

            $moreButton.find('a').on('click', function (e) {

                e.preventDefault();

                if ($(this).attr('data-expanded')) {
                    $careerCount.slice(5).hide();
                    $(this).removeAttr('data-expanded');
                    $(this).text("View more");
                } else {
                    $careerCount.show();
                    $(this).attr('data-expanded', true);
                    $(this).text("View less");
                }

            });

            $careerBlock.append($moreButton);
        }

        function disableClick(e) {
            e.preventDefault();
            return false;
        }

        function toggleLinks() {

            var currentBreakpoint = findBootstrapEnvironment();

            $.each($careerCount, function () {

                var $currentTrigger = $(this).find('a');

                if (currentBreakpoint === "xs") {
                    $currentTrigger.attr("tabindex", "").attr('aria-disabled', "false");
                    $currentTrigger.off('click', disableClick);
                } else {
                    $currentTrigger.attr("tabindex", "-1").attr('aria-disabled', "true");
                    $currentTrigger.on('click', disableClick);
                }
            });
        }

        toggleLinks();

        $(window).on('throttled-resize', toggleLinks);

    }

    $(function () {
        $(settings.careerBlock).each(init);
    });

})(jQuery)
;
(function() {
  'using strict';

  var settings = {
    trigger: '[data-widget="player-stats"]',
  };

  //season player stats menu - position scroll in correct position on mobile
  function setNavMenuScroll(element) {
    var widgetNav = element.find('.Opta-Nav');
    var widgetActiveTab = element.find('.Opta-Tabs .Opta-On').first();
    var tabOffset = widgetActiveTab.position().left;
    var tabHalfWidth = widgetActiveTab.width() / 2;
    var halfScreenWidth = $(window).width() / 2;
    widgetNav.scrollLeft(tabOffset + tabHalfWidth - halfScreenWidth);
  }

  function init() {
      var element = $(this);
      var isInitialised = false;


      window.EFL.MatchCentre.registerWidget('player-stats', element, onWidgetRender, onWidgetError);

      function onWidgetRender(element) {
          // EFLTECH-2792: Laurence - Sometimes even though a player hasn't played a single minute, their player stats are showing but all stats are 0
          // this finds the minutes played value and if it's 0, then it hides the widget
          var minutesPlayedLabels = $(element).find('.Opta-Stats-Section-General .Opta-Stat .Opta-Label');
          $.each(minutesPlayedLabels, function () {
              if ($(this).html().toLowerCase() === 'minutes played') {
                  var minutesPlayedValue = $(this).parent('.Opta-Stat').find('.Opta-Value').html();
                  if (minutesPlayedValue === '0') {
                      onWidgetError(element);
                  }
              }
          });

          setNavMenuScroll(element);
          element.find('.Opta-Tabs').first().find('li').off('click').on('click', function () {
              setNavMenuScroll(element);
          });

          if (!isInitialised) {
              var tab = $('.Opta-Tabs li.Opta-On a');
              var title = $(tab).text();

              if (typeof (title) !== 'undefined') {
                  window.EFL.analyticsController.pageView(window.location.pathname + 'player-stats/' + title.toLowerCase(), 'match-stats ' + title.toLowerCase());
              }

              element.find('.Opta-Tabs li').off('click', trackTab).on('click', trackTab);
          }


          isInitialised = true;
      }

      function onWidgetError(element) {
          element.hide();
      }

  }

  function trackTab(event) {
      try {
          var element = $(this);

          var title = $(element).text();

          if (typeof (title) !== 'undefined') {
              window.EFL.analyticsController.pageView(window.location.pathname + 'player-stats/' + title.toLowerCase(), 'match-stats ' + title.toLowerCase());
          }
      }
      catch (e) {

      }
  }


 
    $(settings.trigger).each(init);

})();
;
(function () {
    'using strict';

    var settings = {
        trigger: '[data-component="fixture-filter"]',
        openEvent: "efl-filter-open",
        selectEvent: "efl-filter-select",
        filterClearTrigger: '.js-clear-filters'
    };


    //shared code
    var filters = {};

    function init() {
        var element = $(this);
        var filter = createFilterObject(element);
        var defaultValue = element.data('default');
        var scopeId = element.data('scopeId') || '';
        /*set a default value if one exists, else pick top item */
        if (typeof (defaultValue) !== 'undefined' && defaultValue !== '') {
            filter.selectedValue = element.find('[data-value="' + defaultValue + '"]').parent();
            filters[filter.id] = defaultValue;
        }
        else {

            filters[filter.id] = $(filter.selectedValue).attr('data-value');

        }

        filter.selectedValueElement.html( filter.selectedValue.find('a[href="#"]').html() );


        if (filter.numberOfResults > settings.minResultsForSearch) {
            element.addClass('more-than-ten-items');
        }

        //Perfect scrollbar
        Ps.initialize(element.find('ul')[0], {
            scrollYMarginOffset: 5,
        });

        function createFilterObject(element) {
            var filter = {};

            filter.element = $(this);
            filter.id = element.attr('id');
            filter.header = element.find('.header');
            filter.overlay = element.find('.filter-overlay');
            filter.results = filter.overlay.find('li');
            filter.numberOfResults = filter.results.length;
            filter.selectedValueElement = element.find('[data-selected-value]');
            filter.selectedValue = filter.overlay.find('li').first().html();

            return filter;
        }

        function onSelect(event, id, label, value, href) {
            filter.selectedValueElement.html(label);
            filters[id] = value;
            close();
            
            window.EFL.eventDispatcher.dispatch('on-filters-change', filters, scopeId);
        }

        function openOverlay(event) {
            window.EFL.eventDispatcher.dispatch('on-filters-open', null, scopeId);
            event.preventDefault();
            var element = $(this).parent();



            if (element.hasClass('active')) {
                element.removeClass('active');
            } else {
                element.addClass('active');
            }
        }

        function selectResult(event) {
            event.preventDefault();

            var parentID = $(this).closest(settings.trigger).attr('id');
            var result = $(this).find('a');
            result.trigger(settings.selectEvent, [parentID, result.html(),result.attr('data-value'), result.attr('href')]);
            parentID = '#' + parentID;

        }

        function populateFilter(data, target) {

            $('#' + target + ' ul').after(data);
            $('#' + target + ' ul' + ':not(:last)').remove();
            $('#' + target + ' ul' + ':not(:last)').remove();

            rebindFilters(target);

            var value = $('#' + target).find('ul li a[href="#"]').html();
            $('#' + target).find('[data-selected-value]').html(value);

        }

        function close() {
            element.removeClass('active');
        }

        function updateFilters(id, href) {
            var url;

            $(id).each(function () {
                var object = [];
                object[0] = $(this).attr('id');
                object[1] = '';
                object[2] = "true";

                url = $(this).attr(settings.updateURL) + '=' + href.replace(/#/g, '');
                makeApiCall(url, $(this).attr('id'));

                window.EFL.eventDispatcher.dispatch("update-url", object, scopeId);


            });
        }

        function rebindFilters(id) {
            $('#' + id + ' .filter-overlay li').on('click', selectResult);
            var numberOfResults = $('#' + id + ' .filter-overlay li').length;
            if (numberOfResults > settings.minResultsForSearch) {
                $('#' + id).addClass('more-than-ten-items');

            }
            else {
                $('#' + id).removeClass('more-than-ten-items');
            }

            //Perfect scrollbar
            Ps.initialize($('#' + id + ' .filter-overlay').find('ul')[0], {
                scrollYMarginOffset: 5,
            });
        }


        function closeOnBodyClick(event) {
            if ($(event.target).parents('.filter-dropdown').length === 0) {
                close();
            }
        }

        //handler for specific requests for the current filters
        window.EFL.eventDispatcher.registerHandler('request-filters', function (callback) {
            //return filters back to requestee
            callback(filters);
        }, scopeId);

        //wire events
        filter.header.off().on('click', openOverlay);
        element.off().on(settings.selectEvent, onSelect);
        filter.results.off().on('click', selectResult);
        $(window).off('resize', close).on('resize', close);
        $(document.body).off('click', closeOnBodyClick).on('click', closeOnBodyClick);
        window.EFL.eventDispatcher.registerHandler('on-filters-open', close, scopeId);
        window.EFL.eventDispatcher.registerHandler('on-filters-change', close, scopeId);
        
        
    }





    $(function () {

        $(settings.trigger).each(init);
    });


})();;
'using strict';
window.EFL = window.EFL || {};
window.EFL.FixtureFilter = (function () {
    'using strict';

    var settings = {
        trigger: '[data-component="read-filters-from-attributes"]',
    };


    function init() {

        var element = $(this);
        var callbackFunction;
        var scopeId = element.data('scopeId') || '';
       
        window.EFL.eventDispatcher.registerHandler('request-filters', function (callback) {
            callbackFunction = callback;
            getAttr();
        }, scopeId);

        function getAttr() {
  
            element.each(function () {
                scopeId = $(this).data('scopeId') || '';

                var filters = {};

                $.each(this.attributes, function () {
                    // this.attributes is not a plain object, but an array
                    // of attribute nodes, which contain both the name and value
                    if (this.specified) {

                        if (typeof (this.name) !== 'undefined' && this.name !== 'data-component') {
                            filters[this.name.replace('data-', '')] = this.value;
                        }
                    }
                });

                callbackFunction(filters, scopeId);
            });

        }

       

    }

    $(function () {
        $(settings.trigger).each(init);
    });
})();;
(function() {
    'using strict';

    var settings = {
        trigger: '[data-widget="club-comparison"]'
    };

    //season player stats menu - position scroll in correct position on mobile
    function setNavMenuScroll(element) {
        var widgetNav = element.find('.Opta-Nav');
        var widgetActiveTab = element.find('.Opta-Tabs .Opta-On').first();
        var tabOffset = widgetActiveTab.position().left;
        var tabHalfWidth = widgetActiveTab.width() / 2;
        var halfScreenWidth = $(window).width() / 2;
        widgetNav.scrollLeft(tabOffset + tabHalfWidth - halfScreenWidth);
    }

    function init() {
        var element = $(this);
        var matchPreviewSectionInitialised = false;
        var homeCrest, homeName, awayCrest, awayName, homeTab, awayTab;

        if (element.find('[data-widget-id="club-comparison"]').length > 0) {

            window.EFL.MatchCentre.registerWidget('club-comparison', element, function(element) {
                setNavMenuScroll(element);
                element.find('.Opta-Tabs').first().find('li').off('click').on('click', function() {
                    setNavMenuScroll(element);
                });
                console.log('club-comparison drawn');

                //Replace No data message
                //Check for the override
                if (EFL && EFL.optaNoDataText) {
                    setNoDataMessage(element, ".Opta.Opta-Normal.Opta-Empty .Opta-Cf p", EFL.optaNoDataText);
                }

            });
        }
    };

    function setNoDataMessage(element, selector, text) {
        if (element.length > 0) {
            var message = element.find(selector);
            if (message.length > 0) {
                message.empty().append(text);
            }
        }
    };

    $(function() {
        $(settings.trigger).each(init);
    });

})();
;
(function () {
    'using strict';

    var settings = {
        trigger: '[data-widget="club-form"]',
        changeEvent: 'on-club-switch'
    };

    function init() {
        var element = $(this);
        var root = element;
        var matchPreviewSectionInitialised = false;
        var homeCrest, homeName, awayCrest, awayName, homeTab, awayTab;
        var club = element.data('clubId');
        var isHome = element.data('isHome') === 'True';
        var plainHeading = root.parent().find('.plain-heading').first();
             

        element.on(settings.changeEvent, function (event, showHome) {
            if (showHome) {
                element.removeClass('show-away');
                element.addClass('show-home');
            } else {
                element.removeClass('show-home');
                element.addClass('show-away');
            }
        });

        if (element.find('[data-widget-id="club-form-preview"]').length > 0) {
            window.EFL.MatchCentre.registerWidget('club-form-preview', element, onClubFormDrawn, onClubFormError);
        }

        var el = element.find('[data-widget-id^="club-form-standings-home"]');
        if (el.length > 0) {
            window.EFL.MatchCentre.registerWidget(el.attr('data-widget-id'), element, onStandingsHomeDrawn, onStandingsHomeError);
            el.attr('load', 'true');
            el.show();
            if (typeof (Opta) !== 'undefined') {
                Opta.start();
            } else {
                console.log('Opta unavailable');
            }
        }

       

        $('body').on('shown.bs.tab', function (event) {
            element.find('.Opta-MatchHeader>tbody>tr').matchHeight();
        });


        function onClubFormDrawn(element) {

            var previousGameTabs = element.find('.Opta-RecordNav');
            //we need to add a class to the side of the club to colour the highlight correctly
            if (isHome) {
                previousGameTabs.first().addClass('club-highlight');
            }
            else {
                previousGameTabs.last().addClass('club-highlight');
            }

            if (!matchPreviewSectionInitialised) {
                //Add Mobile Tab Crest menu
                homeCrest = element.find('.match-preview [data-match]>.Opta-MatchHeader .Opta-Home img').first().clone();
               // homeName = element.find('.match-preview [data-match]>.Opta-MatchHeader .Opta-Home.Opta-TeamName').first().html();
                awayCrest = element.find('.match-preview [data-match]>.Opta-MatchHeader .Opta-Away img').first().clone();
               // awayName = element.find('.match-preview [data-match]>.Opta-MatchHeader .Opta-Away.Opta-TeamName').first().html();

                homeTab = element.find('.mobile-club-tabs [data-home]');
                awayTab = element.find('.mobile-club-tabs [data-away]');

                homeTab.find('[data-crest]').append(homeCrest);
               // homeTab.find('[data-club-name]').append(homeName);

                awayTab.find('[data-crest]').append(awayCrest);
               // awayTab.find('[data-club-name]').append(awayName);

                homeTab.on('click', function (event) {
                    homeTab.trigger(settings.changeEvent, true);
                    return false;
                });

                homeTab.on('keyup', function (event) {
                    if (event.which == 13 || event.keyCode == 13) {
                        homeTab.trigger(settings.changeEvent, true);
                        return false;
                    }
                });

                awayTab.on('click', function (event) {
                    awayTab.trigger(settings.changeEvent, false);
                    return false;
                });

                awayTab.on('keyup', function (event) {
                    if (event.which == 13 || event.keyCode == 13) {
                        awayTab.trigger(settings.changeEvent, false);
                        return false;
                    }
                });

               
                root.addClass('widget-loaded');

                plainHeading.addClass('loaded');

            }

            var comps = $(".Opta-Matchdata dl dd, .Opta-Header td h4 span");
            comps.each(function (i, o) {
                var el = $(o);
                var competition = getCompetitionImage(el.text());
                if (competition != '') {
                    el.html(competition);
                }
            });

            matchPreviewSectionInitialised = true;

            element.find('.Opta-Team-' + club + '.Opta-highlight').addClass('club');

            console.log('club-form-preview drawn');

            //Replace No data message
            //Check for the override
            if (EFL && EFL.optaNoDataText) {
                setNoDataMessage(element, ".Opta.Opta-Normal.Opta-Empty .Opta-Cf p", EFL.optaNoDataText);
            }
        }

        function setNoDataMessage(element, selector, text) {
            if (element.length > 0) {
                var message = element.find(selector);
                if (message.length > 0) {
                    message.empty().append(text);
                }
            }
        };

        function onClubFormError(element) {
            element.hide();
            plainHeading.removeClass('loaded');
            root.addClass('widget-loaded');
        }

        function onStandingsHomeDrawn() {
            element.find('.standings-heading').show();
            element.find('.Opta-Team-' + club + '.Opta-highlight').addClass('club');
            root.addClass('widget-loaded');
        }

        function onStandingsHomeError(element) {
            element.find('.standings-home').hide();
             root.addClass('widget-loaded');
        }


        function onStandingsAwayDrawn() {
        }

        function onStandingsAwayError(element) {
            element.find('.standings-away').hide();
        }

        function getCompetitionImage(competition) {
            var competitionName = competition.replace(/'/g, '');
            var competitionlogo = $.grep(window.EFL.competitionLogos, function (obj) { return obj.ImgAlt === competitionName; })[0];
            if (competitionlogo) {
                if (competitionlogo.ImgSrc === '') {
                    return competitionlogo.Title;
                }
                
                return '<img src="' + competitionlogo.ImgSrc + '" alt ="' + competitionlogo.Title + ' logo" class="competition-logo" title="' + competitionlogo.Title + '" />';
            } else {
                return competitionName;
            }
        }
       
    }

    $(function () {
        $(settings.trigger).each(init);
    });

})();
;
window.EFL = window.EFL || {};
window.EFL.fixtures = window.EFL.fixtures || {};

/*Build the custom links that are attached to the side of fixtures*/
window.EFL.fixtures.buildLinks = function (matchData, data, packagesPageUrl) {
    var firstLinks, lastLinks, hasLinks = false;

    if (typeof (data) !== 'undefined' && typeof (matchData) !== 'undefined') {
        var found = false;
        var i = 0;
        while (!found && i < matchData.data.length) {
            found = matchData.data[i].GameId.indexOf(data.matchId) !== -1;
            if (found) {
                firstLinks = '';
                lastLinks = '';
                var startcol2 = 2;


                //Always link to packages page if there is a url
                if (packagesPageUrl) {

                    // If Match Level URL override exists - set PackagesPageURL to it.
                    if (data.RegisterOnIfollowURL) {
                        packagesPageUrl = data.RegisterOnIfollowURL;
                    }
 
                    //The kickoff has only been added to the manual fixtures so if its undefined we know its an opta fixture.
                    if (data.kickoff != undefined) {

                        //get the kickoff time.
                        var matchDate = new Date(data.kickoff);

                        //Get todays date and set the time to midnightish.
                        var endOfToday = new Date();
                        endOfToday.setHours(23);
                        endOfToday.setMinutes(59);
                        endOfToday.setSeconds(59);
                        endOfToday.setMilliseconds(999);

                        //Only display the Live on iFollow link if its the day of the match or less.
                        if (matchDate.getTime() > endOfToday.getTime()) {
                            hasLinks = true;
                            //Check for ROI Override
                            if (typeof matchData.data[i].ROIOverride !== 'undefined' && matchData.data[i].ROIOverride) {
                                data.customLinkText = matchData.data[i].ROIOverride;
                            }

                            //Check if we've to hide the ROI link
                            if (typeof matchData.data[i].HideROIOverride !== 'undefined' && !matchData.data[i].HideROIOverride) {
                                firstLinks += '<span class="link"><span  aria-hidden="true" class="icon icon-Video"></span><a target="_self" href="' + packagesPageUrl + '" class=""><span>' + data.customLinkText + '</span></a></span>';

                                startcol2 = 1;
                            } 
                        }
                    }
                    else {
                        //Opta fixture.
                        hasLinks = true;
                        if (typeof matchData.data[i].HideROIOverride !== 'undefined' && !matchData.data[i].HideROIOverride) {
                            firstLinks += '<span class="link"><span  aria-hidden="true" class="icon icon-Video"></span><a target="_self" href="' + packagesPageUrl + '" class=""><span>' + data.customLinkText + '</span></a></span>';
                        } 

                        startcol2 = 1;
                    }
                }

                for (var j = 0; j < matchData.data[i].Links.length; j++) {

                    var linktarget = matchData.data[i].Links[j].CssClass.indexOf('icon-Ticket') > -1 ? "_blank" : "_self";

                    //highlights link text can be overriden by admins and non-efl teams, so set the label here and ignore what was set in FixtureLinksController
                    if (matchData.data[i].Links[j].Url.indexOf("#highlights") > 0) {
                        matchData.data[i].Links[j].Label = data.customHighlightsLinkText;
                    }

                    if (j < startcol2) {
                        hasLinks = true;

                        if (matchData.data[i].Links[j].Url === "#") {
                            firstLinks += '<span class="link"><span aria-hidden="true" class="' + matchData.data[i].Links[j].CssClass + '"></span><span>' + matchData.data[i].Links[j].Label + '</span></span>';
                        } else {
                            if (!matchData.data[i].Links[j].HideLMC) {
                                firstLinks += '<span class="link"><span aria-hidden="true" class="' + matchData.data[i].Links[j].CssClass + '"></span><a target="' + linktarget + '" href="' + matchData.data[i].Links[j].Url + '" class=""><span>' + matchData.data[i].Links[j].Label + '</span></a></span>';
                            } 
                        }


                    }
                    else {
                        if (matchData.data[i].Links[j].Url === "#") {
                            lastLinks += '<span class="link"><span aria-hidden="true" class="' + matchData.data[i].Links[j].CssClass + '"></span><span>' + matchData.data[i].Links[j].Label + '</span></span>';
                        } else {
                            if (!matchData.data[i].Links[j].HideLMC) {
                                lastLinks += '<span class="link"><span aria-hidden="true" class="' + matchData.data[i].Links[j].CssClass + '"></span><a target="' + linktarget + '" href="' + matchData.data[i].Links[j].Url + '" class=""><span>' + matchData.data[i].Links[j].Label + '</span></a></span>';
                            }
                            
                        }

                    }
                }


                if ((firstLinks.match(/link/g) || []).length === 1) {
                        firstLinks += '<span class="link blank-link"></span>';                
                } 

                if ((lastLinks.match(/link/g) || []).length === 1) {
                    lastLinks += '<span class="link blank-link"></span>';
                }


                if (firstLinks) {
                    firstLinks = '<span class="links">' + firstLinks + '</span>';
                }

                if (lastLinks) {
                    lastLinks = '<span class="links">' + lastLinks + '</span>';
                }

                if (typeof (matchData) !== 'undefined' && matchData.data[i].Links.length == 0) {
                    firstLinks = $(firstLinks);
                    firstLinks.addClass('empty');
                    firstLinks = firstLinks.outerHTML;
                }

                if (typeof (matchData) !== 'undefined' && matchData.data[i].Links.length <= 1) {
                    lastLinks = $(lastLinks);
                    lastLinks.addClass('empty');
                    lastLinks = lastLinks.outerHTML;
                }
            }
            i++;
        }



    }

    return {
        hasLinks: hasLinks,
        firstLinks: firstLinks,
        lastLinks: lastLinks
    }

};

/*I think this is currently only used for EFL.com EFLFixtures block - linkData is based off inline JSON rendered in view of Club blocks */
window.EFL.fixtures.buildClubLinks = function (linkData, fixtureData) {

    var homeClubLinks = getClubLinks(linkData, fixtureData.homeId);
    var awayClubLinks = getClubLinks(linkData, fixtureData.awayId);
    var firstLinks = '', lastLinks = '';
    var firstEmpty, lastEmpty

    var hasLinks = typeof (homeClubLinks) !== 'undefined' || typeof (awayClubLinks) !== 'undefined';

    if (typeof (homeClubLinks) !== 'undefined' && homeClubLinks != null && typeof (homeClubLinks.URL) !== 'undefined') {
        if (homeClubLinks.IsOP) {
            if (fixtureData.isFixture) {

                //add tickets link
                if (!(typeof (homeClubLinks.DisableTickets) !== 'undefined' && homeClubLinks.DisableTickets)) {
					//if we have a ticket url set in the club block then use that
					if (typeof (homeClubLinks.TicketURL) !== 'undefined') {
						firstLinks += '<span class="link"><span aria-hidden="true" class="icon icon-Ticket"></span><a target="_blank" href="' + homeClubLinks.TicketURL + '" class=""><span>Home Tickets</span></a></span>';
					}
					//otherwise default to the old system
					else {
						firstLinks += '<span class="link"><span aria-hidden="true" class="icon icon-Ticket"></span><a target="_blank" href="' + homeClubLinks.URL + '/r/tickets/" class=""><span>Home Tickets</span></a></span>';
					}
				}
				
                //add MC link
                firstLinks += '<span class="link"><span aria-hidden="true" class="icon icon-Goals"></span><a target="_blank" href="' + homeClubLinks.URL + '/r/g/' + fixtureData.matchId + '" class=""><span>Home Match Centre</span></a></span>';
            }

        } else {
            if (fixtureData.isFixture) {

				//add tickets link
				if (!(typeof (homeClubLinks.DisableTickets) !== 'undefined' && homeClubLinks.DisableTickets)) {
					if (typeof (homeClubLinks.TicketURL) !== 'undefined') {
						firstLinks += '<span class="link"><span aria-hidden="true" class="icon icon-Ticket"></span><a target="_blank" href="' + homeClubLinks.TicketURL + '" class=""><span>Home Tickets</span></a></span>';
					}
					else {
						firstLinks += '<span class="link"><span aria-hidden="true" class="icon icon-Ticket"></span><a target="_blank" href="' + homeClubLinks.URL + '" class=""><span>Home Tickets</span></a></span>';
					}
				}

                //add MC link
                if (typeof (homeClubLinks.HomeMC) !== 'undefined') {
                    firstLinks += '<span class="link"><span aria-hidden="true" class="icon icon-Goals"></span><a target="_blank" href="' + homeClubLinks.HomeMC + '" class=""><span>Home Match Centre</span></a></span>';
                }
                else {
                    firstLinks += '<span class="link"><span aria-hidden="true" class="icon icon-Goals"></span><a target="_blank" href="' + homeClubLinks.URL + '" class=""><span>Home Match Centre</span></a></span>';
                    //firstLinks += '<span class="link blank-link"></span>';
                }
            }

        }
    }

    if (typeof (awayClubLinks) !== 'undefined' && awayClubLinks != null && typeof (awayClubLinks.URL) !== 'undefined') {
        if (awayClubLinks.IsOP) {
            if (fixtureData.isFixture) {

                //add tickets link
				if (!(typeof (awayClubLinks.DisableTickets) !== 'undefined' && awayClubLinks.DisableTickets)) {
					//if we have a ticket url set in the club block then use that
					if (typeof (awayClubLinks.TicketURL) !== 'undefined') {
						lastLinks += '<span class="link"><span aria-hidden="true" class="icon icon-Ticket"></span><a target="_blank" href="' + awayClubLinks.TicketURL + '" class=""><span>Away Tickets</span></a></span>';
					}
					//otherwise default to the old system
					else {
						lastLinks += '<span class="link"><span aria-hidden="true" class="icon icon-Ticket"></span><a target="_blank" href="' + awayClubLinks.URL + '/r/tickets/" class=""><span>Away Tickets</span></a></span>';
					}
				}

                //add MC link
                lastLinks += '<span class="link"><span aria-hidden="true" class="icon icon-Goals"></span><a target="_blank" href="' + awayClubLinks.URL + '/r/g/' + fixtureData.matchId + '" class=""><span>Away Match Centre</span></a></span>';
            }

        } else {
			if (fixtureData.isFixture) {

                //add tickets link
				if (!(typeof (awayClubLinks.DisableTickets) !== 'undefined' && awayClubLinks.DisableTickets)) {
					if (typeof (awayClubLinks.TicketURL) !== 'undefined') {
						lastLinks += '<span class="link"><span aria-hidden="true" class="icon icon-Ticket"></span><a target="_blank" href="' + awayClubLinks.TicketURL + '" class=""><span>Away Tickets</span></a></span>'
					}
					else {
						lastLinks += '<span class="link"><span aria-hidden="true" class="icon icon-Ticket"></span><a target="_blank" href="' + awayClubLinks.URL + '" class=""><span>Away Tickets</span></a></span>'
					}
				}
                
                //add MC link
                if (typeof (awayClubLinks.AwayMC) !== 'undefined') {
                    lastLinks += '<span class="link"><span aria-hidden="true" class="icon icon-Goals"></span><a target="_blank" href="' + awayClubLinks.AwayMC + '" class=""><span>Away Match Centre</span></a></span>';
                }
                else {
                    lastLinks += '<span class="link"><span aria-hidden="true" class="icon icon-Goals"></span><a target="_blank" href="' + awayClubLinks.URL + '" class=""><span>Away Match Centre</span></a></span>';
                    //lastLinks += '<span class="link blank-link"></span>';
                }
            }
        }
    }

    if (firstLinks) {
        firstLinks = '<span class="links">' + firstLinks + '</span>'
    }

    if (lastLinks) {
        lastLinks = '<span class="links">' + lastLinks + '</span>'
    }

    if (typeof (matchData) !== 'undefined' && matchData.data[i].Links.length == 0) {
        firstLinks = $(firstLinks);
        firstLinks.addClass('empty');
        firstLinks = firstLinks.outerHTML;
    }

    if (typeof (matchData) !== 'undefined' && matchData.data[i].Links.length <= 1) {
        lastLinks = $(lastLinks);
        lastLinks.addClass('empty');
        lastLinks = lastLinks.outerHTML;
    }

    return {
        hasLinks: hasLinks,
        firstLinks: firstLinks,
        lastLinks: lastLinks
    }

};

function getClubLinks(linkData, clubId) {
    var clubLinks = null;
    if (typeof (linkData) !== 'undefined' && typeof (clubId) !== 'undefined' && typeof (linkData.length) !== 'undefined') {
        var found = false;
        var i = 0;
        var teamId;
        while (!found && i < linkData.length) {
            if (typeof (linkData[i].OptaId) != 'undefined') {
                teamId = linkData[i].OptaId.replace('t', '');

                found = teamId == clubId;
            }

            if (found) {
                clubLinks = linkData[i];
            }
            i++;
        }


    }

    return clubLinks;
}

function getGeoData() {

    var regionResult = null;
    var countryResult = null;

    if (findDLIndex('league-blackout-excluded') && findDLIndex('video-region') && findDLIndex('video-country') && window.dataLayer[findDLIndex('league-blackout-excluded')]['league-blackout-excluded'] && window.dataLayer[findDLIndex('video-region')]['video-region'] && window.dataLayer[findDLIndex('video-country')]['video-country']) {
        var videoRegion = window.dataLayer[findDLIndex('video-region')]['video-region'];
        var videoCountry = window.dataLayer[findDLIndex('video-country')]['video-country'];
        var leagueBlackoutExcluded = window.dataLayer[findDLIndex('league-blackout-excluded')]['league-blackout-excluded'];
        if (leagueBlackoutExcluded === 'true') {
            videoRegion += 'P';
        }
        regionResult = window.EFL.Regions[videoRegion];
        countryResult = videoCountry;
    }

    return geoData = {
        "Region": regionResult,
        "Country": countryResult
    };
}



/* OPTA FIXTURES */
(function () {
    'using strict';
    var settings = {
        trigger: '[data-widget="fixtures"]',
    };

    var userGeoData;

    //template for rebuilding widget
    var widgetHtml = $('<opta-widget sport="football" data-widget-id="fixtures" widget="fixtures" competition="10" season="2016" team="19" template="normal" live="false" show_venue="true" match_status="all" grouping="month" show_grouping="true" default_nav="1" start_on_current="true" switch_current="0" sub_grouping="date" show_subgrouping="true" order_by="date_ascending" show_crests="true" date_format="dddd D MMMM YYYY" month_date_format="MMMM" competition_naming="full" team_naming="full" pre_match="false" show_live="false" show_logo="false" show_title="false" breakpoints=""></opta-widget>');


    function init() {
        var element, root, originalWidgetElement, initialised, matchId, competitionId, dateFrom, dateTo, dateOrder, teamId, venueType, matchData, optaWidget, packagesPageUrl, isUpdating = false, isManual, scopeId, loadDefaults, isEfl;
        element = $(this);
        var optawidgetid = element.find('opta-widget').attr('data-widget-id');

        scopeId = element.data('scopeId');


        if (typeof (window.EFL) !== 'undefined' && typeof (window.EFL.fixturesClubUrls) !== 'undefined' && window.EFL.fixturesClubUrls.length > 0) {
            isEfl = true;
        }

        var isFixtureReleaseWidget = false;
        isManual = element.data('isManual') || false;
        dateFrom = element.attr("data-date-from");
        dateTo = element.attr("data-date-to");
        dateOrder = element.attr("data-date-order");
        packagesPageUrl = element.attr("data-packages-url");
        loadDefaults = element.attr("data-load-defaults") || false;
        hideTvChannels = element.data("hide-channels") || false;

        element.hide();
        root = element;

        originalWidgetElement = element.find('opta-widget');

        if (originalWidgetElement.length > 0) {
            originalWidgetElement = originalWidgetElement[0].outerHTML;
        }

        /* Bind Events */
        window.EFL.eventDispatcher.registerHandler('on-filters-change', onFilterChange, scopeId);
        window.EFL.MatchCentre.registerWidget(optawidgetid, element, onDrawn, onError);

        // 419
        if ($(element).parents('[data-widget="fixtures-release"]').length) {
            var parent = $(element).closest('[data-widget="fixtures-release"]');
            isFixtureReleaseWidget = true;
            $('[data-select-clubEFR]').on('change', function (e) {
                var teamId = $(this).val();
                var compId = $(this).find(":selected").attr("data-competition");
                var stanzaLinkPrefix = $(parent).attr("data-stanza-url") != null ? $(parent).attr("data-stanza-url") : null;
                var stanzaClubName = $(this).find(":selected").attr("data-stanza") != null ? $(this).find(":selected").attr("data-stanza") : null;
                var date = $(element).attr("data-date-from");
                var season = $(element).attr("data-season");

                var data = {
                    filters: {
                        competition: compId,
                        team: teamId,
                        season: season,
                        date: date
                    }
                }
                onFixtureUpdate(data);

                //We'll hide the button if the calendar club id is empty or null
                if (stanzaClubName) {
                    $(".calendar-btn-container").removeClass("hidden");
                }
                else {
                    $(".calendar-btn-container").addClass("hidden");
                }

                $("#calendar-btn").attr("href", stanzaLinkPrefix + stanzaClubName);
                
            });
        }

        if (loadDefaults) {
            element.show();
            if (typeof (Opta) !== 'undefined') {
                //     Opta.start();
            } else {
                console.log('Opta unavailable');
            }
        }
        else {

            /* On ready request the current filters*/
            $(function () {
                var event = isManual ? 'request-manual-filters' : 'request-filters';
                window.EFL.eventDispatcher.dispatch(event, onRequestFilters, scopeId);
            });

        }

        /*Return method for recieving the results of a request to get the current filters*/
        function onRequestFilters(filters, filterId) {

            if (filterId !== scopeId) {
                return;
            }

            if (!isUpdating) {
                console.log("on-request-filters opta-fixtures");

                if (typeof (filters) === 'undefined' || isEmpty(filters)) {
                    return;
                }

                if (typeof (filters.competition) != 'undefined' && typeof (filters.season) != 'undefined') {
                    //STRANGE BEHAVIOUR WITH OPTA - multiple competitions require seasons to be duplicated -> e.g. competition 10,7,2  requires a season value of 2016,2016,2016
                    filters.season = createSeason(filters.competition, filters.season);
                }

                //matchData exists after the widget has loaded, if its not available we need to load the image
                if (typeof (matchData) == 'undefined') {

                    onFilterChange(filters);
                }
                else {

                    loadWidget(element, element.find('opta-widget'), filters.competition, dateFrom, dateTo, dateOrder, filters.team, filters.season);
                }
            }
        };

        /* event listener to detect filter changes and update the widget */
        function onFilterChange(filters) {


            if (!isUpdating) {
                isUpdating = true;
                console.log("onFilterChange opta-fixtures");

                url = '/api/fixturelinks/?team=' + (filters.team || '') + '&competition=' + (filters.competition || '') + '&season=' + (filters.season || '') + '&useteam=' + (filters.useteam || '') + '&excludeopta=' + (filters.excludeopta ? 'true' : 'false');

                var xhr = $.get(url, function (data) {

                    onFixtureUpdate({
                        filters: filters,
                        data: data
                    });
                });
            }
        }

        /* Opta OnDrawn event callback - executes when opta renders widget */
        function onDrawn(element, optaItem) {
            var optaItemDataScopeId = optaItem.widget.attr['data-scope-id'];
            var elementDataScopeId = element[0].attributes['data-scope-id'].value;
            if (optaItemDataScopeId != elementDataScopeId) {
                return;
            }

            element.addClass('custom');
            console.log("onDrawn opta-fixtures");
            optaWidget = optaItem;
            initialised = element.attr('data-initialised') === 'true';

            // if (!initialised) {
            element.attr('data-initialised', 'true');

            var teamId = optaItem.widget.attr_original.team;
            var allTbodies = element.find('tbody');
            var elements = element.find('tbody.Opta-fixture');
            var titles = element.find('tbody .Opta-title');
            var maxRows = typeof (element.data('maxresults')) != 'undefined' && !isNaN(parseInt(element.data('maxresults'))) && parseInt(element.data('maxresults')) > -1 ? parseInt(element.data('maxresults')) : elements.length;

            var fixturesKept = 0;

            for (var i = 0; i < allTbodies.length; i++) {

                var thisTbody = allTbodies[i];

                if (maxRows == -1 || fixturesKept < maxRows) {
                    if ($(thisTbody).hasClass('Opta-fixture')) {
                        var rowData = getOptaRowData(thisTbody, optaItem);
                        constructView(fixturesKept, thisTbody, teamId, rowData);
                        fixturesKept++;
                    }
                } else {
                    $(thisTbody).remove();
                }
            }

            // when using multi-team fixtures like on efl.com remove all unwanted rows
            if (typeof (teamId) === 'undefined' || teamId == '') {

                element.find('tbody:not(.Opta-fixture)').each(function (index, tbody) {

                    var $tbody = $(tbody),
                        isMonth = false,
                        opta_title_text,
                        isRound = false,
                        opta_round_text;

                    opta_title_text = $tbody.find('.Opta-title h3 span')[0];

                    if (opta_title_text && opta_title_text.innerHTML) {
                        isMonth = opta_title_text.innerHTML.match(/(^January$|^February$|^March$|^April$|^May$|^June$|^July$|^August$|^September$|^October$|^November$|^December$)/) ? true : false;
                    }

                    opta_round_text = $tbody.find('.Opta-title h4 span')[0];
                    // Opta sends both the ROUND and DATE/DAY Data in the same format
                    // Need to make sure the data sent doesn't contains any DATE data
                    // IF not - we assume that the Tbody row we're on contains ROUND data
                    if (opta_round_text && opta_round_text.innerHTML) {
                        isRound = opta_round_text.innerHTML.match(/(?:January|February|March|April|May|June|July|August|September|October|November|December)/) ? false : true;
                        // If the data row IS a round, let's add a class to the element so we can better target the tbody element
                        if (isRound) {
                            $tbody.addClass("Opta-round-title");
                        }
                    }
                    if (!isMonth && !isRound) {
                        $tbody.remove();
                    }
                });
           }

            element.attr('data-initialised', 'true');


            root.css('height', '');
            element.addClass('widget-loaded');
            console.log('fixtures drawn');

            //Replace No data message
            //Check for the override
            if (EFL && EFL.optaNoDataText) {
                setNoDataMessage(element, ".Opta.Opta-Normal.Opta-Empty .Opta-Cf p", EFL.optaNoDataText);
            }
            else {
                //Use the old original value if nothing set
                setNoDataMessage(element, ".Opta.Opta-Normal.Opta-Empty .Opta-Cf p", "There are currently no fixtures to display. Please check back soon.");
            }


        };

        function setNoDataMessage(element, selector, text) {
            if (element.length > 0) {
                var message = element.find(selector);
                if (message.length > 0) {
                    message.empty().append(text);
                }
            }
        };

        //scrape row and get out all the relevant data
        function getOptaRowData(element, data) {

            var rowData = null;

            /*if (element.attr('data-period').toLowerCase() === 'prematch') {

            }*/

            if (typeof (data) != 'undefined' && typeof (data.widget) != 'undefined' && typeof (data.widget.matches) != 'undefined') {

                var className = element.className.match(/Opta-Match-[0-9]*/);

                if (className.length > 0) {
                    var matchIds = className[0].match(/[0-9]+/);

                    if (typeof (matchIds) != 'undefined' && matchIds.length > 0 && matchIds[0] != '') {

                        var matchId = matchIds[0];

                        var matches = data.widget.matches;
                        var subMatches;
                        for (var i = 0; i < matches.length; i++) {
                            subMatches = matches[i].matches;

                            if (typeof (subMatches) != 'undefined') {
                                for (var j = 0; j < subMatches.length; j++) {
                                    if (subMatches[j].id.toString() === matchId) {
                                        rowData = subMatches[j];
                                    }
                                }
                            }
                        }
                    }
                }
            }

            return rowData;
        }

        /* Opta OnError event callback - executes when opta finds a problem */
        function onError(element, optaItem) {
            console.log("onError opta-fixtures");
            optaWidget = optaItem;
            element.addClass('widget-loaded');
            root.css('height', '');

            //Replace No data message
            //Check for the override 
            if (EFL && EFL.optaNoDataText) {
                setNoDataMessage(element, ".Opta-Error .Opta-Cf p", EFL.optaNoDataText);
            }
            else {
                //Use the old original value if nothing set
                setNoDataMessage(element, ".Opta-Error .Opta-Cf p", "There are currently no fixtures to display. Please check back soon.");
            }
        }

        //Used to update the fixtures table with our own data
        function onFixtureUpdate(data) {

            console.log("on-fixture-update opta-fixtures");

            var original = $(originalWidgetElement);
            var competition = data.filters.competition;// || optaWidget.widget.attr_original.competition;// original.attr('competition');
            var team = data.filters.team;// || optaWidget.widget.attr_original.team;
            var season = data.filters.season;// || optaWidget.widget.attr_original.season;

            if (data.filters.date && data.filters.date.length > 0 && data.filters.date.indexOf(",") > 9) {
                dateFrom = data.filters.date.split(',')[0];
                dateTo = data.filters.date.split(',')[1];
            }

            //STRANGE BEHAVIOUR WITH OPTA - multiple competitions require seasons to be duplicated -> e.g. competition 10,7,2  requires a season value of 2016,2016,2016
            season = createSeason(competition, season);

            matchData = data;

            if (typeof (competition) !== 'undefined' && competition !== '') {
                if (typeof (optaWidget) === "undefined") {

                    loadWidget(element, element.find('opta-widget'), competition, dateFrom, dateTo, dateOrder, team, season);
                }
                else {
                    reloadWidget(element, optaWidget, original, competition, dateFrom, dateTo, dateOrder, team, season)
                }
            } else {
                // when no competition is pulled down
                // clear widget markup
                element.empty();
                // and mimic no fixtures message
                loadWidget(element, element.find('opta-widget'), competition, dateFrom, dateTo, dateOrder, team, season);
                //Use the old original value if nothing set
                if (EFL && EFL.optaNoDataText) {
                    element.append('<div class="Opta"><p>' + EFL.optaNoDataText + '</p></div>');
                }
                else {
                    element.append('<div class="Opta"><p>There are currently no fixtures to display. Please check back soon.</p></div>');
                }

                
            }



        };

        /*Only way to change the state of the widget is to reconstruct it with new attribute options*/
        function reloadWidget(element, optaItem, widgetHtml, competitionId, pdateFrom, pdateTo, pdateOrder, teamId, season) {
            console.log("reloadWidget opta-fixtures");
            var height = root.height();
            root.css('height', height);
            optaItem.widget.destroy(true);
            element.empty();

            var selectedFilter = $('[data-filter-name="competition"]').find('[data-selected="true"]');
            var showSubGrouping = selectedFilter.attr("data-showSubgroup");

            var newWidget = widgetHtml.clone();
            newWidget.attr('competition', competitionId);
            newWidget.attr('date_from', pdateFrom);
            newWidget.attr('date_to', pdateTo);
            newWidget.attr('team', teamId);
            newWidget.attr('order_by', pdateOrder);
            newWidget.attr('season', season);
            if (typeof (showSubGrouping) !== 'undefined' && showSubGrouping === "true") {
                newWidget.attr('show_subgrouping', showSubGrouping);
                newWidget.attr('sub_grouping', "round");
            }
            else {
                newWidget.attr('show_subgrouping', "false");
            }
            newWidget.attr('load', 'true');
            element.append(newWidget);
            element.show();

            if (typeof (Opta) !== 'undefined') {
                Opta.start();
            } else {
                console.log('Opta unavailable');
            }

            isUpdating = false;
            element.attr('data-initialised', 'false');
        }

        /* is run before opta widget is loaded to change configuration, once load becomes true only reload will work */
        function loadWidget(element, widgetHtml, competitionId, pdateFrom, pdateTo, pdateOrder, teamId, season) {
            console.log("loadWidget opta-fixtures");
            widgetHtml.attr('competition', competitionId);
            widgetHtml.attr('date_from', pdateFrom || '');
            widgetHtml.attr('date_to', pdateTo || '');
            widgetHtml.attr('order_by', pdateOrder);
            widgetHtml.attr('team', teamId);
            widgetHtml.attr('season', season);
            element.show();

            widgetHtml.attr('load', 'true');



            if (typeof (Opta) !== 'undefined') {
                Opta.start();
            } else {
                console.log('Opta unavailable');
            }

            isUpdating = false;
            element.attr('data-initialised', 'false');
        }

        //STRANGE BEHAVIOUR WITH OPTA - multiple competitions require seasons to be duplicated -> e.g. competition 10,7,2  requires a season value of 2016,2016,2016
        function createSeason(competition, season) {
            var competitionCount = 0, collatedSeason = season;

            competitionCount = competition.toString().split(',').length;

            if (typeof (competition !== 'undefined') && typeof (season !== 'undefined') && season.toString().indexOf(',') === -1 && competitionCount > 1) {
                collatedSeason = '';

                for (var i = 0; i < competitionCount; i++) {
                    collatedSeason += season + ',';
                }
                collatedSeason = collatedSeason.substring(0, collatedSeason.length - 1);
            }

            //If single competition but we are holding onto multi-seasons
            if (competitionCount <= 1 && season.toString().split(',').length > 1) {
                collatedSeason = season.toString().split(',')[0];
            }

            return collatedSeason;
        }

        /* Clears opta widget rows and constructs new markup from the data */
        function constructView(index, element, teamId, optaData) {
            //console.log("constructView opta-fixtures");
         
            element = $(element);

            //gathers data from the opta widget
            var data = gatherRowData(element, teamId, optaData);
            var competition = getCompetitionImage(data.competition);

            var tvChannel = "";
            var iFollow = "";

            if (data.tvChannel != null) {
                if (getTvChannelImage(data.tvChannel) != null) {
                    tvChannel = '<span>' + getTvChannelImage(data.tvChannel) + '</span>'
                }
            }

            if (data.availableOnIfollow != null) {
                iFollow = '<span>' + getIfollowImage() + '</span>'
            }

            var time;
            var postponed = false;
			var tbc = false;

            if (element.attr('data-period').toLowerCase() === 'postponed') {
                time = 'PP';
                postponed = true;
            } else if (data.time.indexOf("TBC") == -1) {
                time = data.time + ' (UK)';
			}
			else {
				tbc = true;
			}

            var links;
            element.find('td').remove();
            element.find('tr').first().append($('<td class="crest"><span class="home-crest">' + data.homeCrest + '</span><span class="away-crest">' + data.awayCrest + '</span></td>'));
            element.find('tr').first().append($('<td class="match-info"><span><span class="home-team">' + data.homeTeamName + '</span><br class="home-team"><span class="away-team">' + data.awayTeamName + '</span></br><span class="venue">' + data.venue + '</span></span></td>'));
            element.find('tr').first().append($('<td class="game-type"><span class="align-wrapper"><span class="sr-only">Score ' + data.homeTeamName + ' ' + data.homeScore + ' - ' + data.awayTeamName + ' ' + data.awayScore + '</span><span aria-hidden="true" class="score">' + data.homeScore + '</span><span  aria-hidden="true" class="score">' + data.awayScore + '</span><span class="side">' + data.gameType + '</span></span></td>'));

			if (tbc) {
				element.find('tr').first().append($('<td class="team-name"><span><span class="match-date">TBC</span></br><span class="competition">' + competition + '</span>' + tvChannel + iFollow + '</td>'));
			}
			else {
				element.find('tr').first().append($('<td class="team-name"><span><span class="match-date">' + data.date + ' <span class="time">' + time + '</span></span></br><span class="competition">' + competition + '</span>' + tvChannel + iFollow + '</td>'));
			}

            if (data.penalties.length > 0) {
                element.addClass('additional-fixture-info');
                element.find('tr').last().addClass('penalties-section').append('<td>' + data.penalties.html() + '</td>');
            }

            //EFLRM-139 : don't draw fixture links for a postponed game
            if (!postponed) {

                if (!isFixtureReleaseWidget) {
                    //check if there are any inline team links such as for efl.com
                    if (isEfl) {
                        links = window.EFL.fixtures.buildClubLinks(window.EFL.fixturesClubUrls, data);
                    }
                    else {
                        links = window.EFL.fixtures.buildLinks(matchData, data, packagesPageUrl);
                    }
                }

                //creates the fixture links by comparing the match id against a json list of matchid-links
                var linksList;
                if (typeof (links) !== 'undefined' && typeof (links.firstLinks) !== 'undefined') {
                    linksList = $('<td class="links-first' + (links.hasLinks ? ' filled' : '') + '">' + links.firstLinks + '</td>');

                    if (linksList.find('.empty').length > 0) {
                        linksList.addClass('empty');
                    }

                    links.firstLinks && element.find('tr').first().append(linksList);
                }

                if (typeof (links) !== 'undefined' && typeof (links.lastLinks) !== 'undefined') {
                    linksList = $('<td class="links-last">' + links.lastLinks + '</td>');

                    if (linksList.find('.empty').length > 0) {
                        linksList.addClass('empty');
                    }

                    links.lastLinks && element.find('tr').first().append(linksList);
                }
            }
            //If its a single clubs fixtures
            if (typeof (teamId) !== 'undefined') {
                //EFLRM-139 : added a check to make sure we're not removing an actual fixture.
                //League game was postponed, and a new cup game appeared on the same day, but cup game was removed by this
                if (!element.prev().hasClass('Opta-fixture')) {
                    // ESC-600 : Let's check if the competition filter has the showSubgroup attribute.
                    // If it doesn't - we can remove the row from displaying.
                    var selectedFilter = $('[data-filter-name="competition"]').find('[data-selected="true"]');
                    var showSubGrouping = selectedFilter.attr("data-showSubgroup");
					if (!(showSubGrouping == 'true')) {
                        element.prev().remove();
                    }
                    // If the does - let's add a class to the element so we can better target the tbody element
                    else {
                        element.prev().addClass("Opta-round-title");
                    }
                }
            }

            root.find('table').attr('cellpadding', 0);
            root.find('table').attr('cellspacing', 0);
                setGameType(element, data.spacer);

                // add name of game to each link so that screen readers have some context when reading it
                element.find('a').prepend('<span class="sr-only">' + data.homeTeamName + ' versus ' + data.awayTeamName + ', ' + data.date + ', </span>');
        }

        /*Scrapes the opta widget to get the relevant data to build in the new structure*/
        //TODO: Refactor Below to maybe get the same data through the Opta object
        function gatherRowData(element, teamId, optaData) {

            var data = {};
            var homeElement = element.find('td.Opta-Home').first();

            var awayElement = element.find('td.Opta-Away').first();
            var teamIdRegex = new RegExp('Opta-Team-[0-9]+');
            var homeId = teamIdRegex.exec(homeElement[0].className);


            if (homeId != null) {
                homeId = homeId[0].replace('Opta-Team-', '');
            }

            var awayId = teamIdRegex.exec(awayElement[0].className);

            if (awayId != null) {
                awayId = awayId[0].replace('Opta-Team-', '');
            }

            var classes = homeElement.attr('class');
            var teamIds = classes.match(/[A-z]*-[A-z]*-[0-9]*/g);
            var homeTeamId;
            //  var teamId = optaWidget.widget.attr_original.team;
            if (teamIds.length > 0) {
                homeTeamId = teamIds[0].replace('Opta-Team-', '');
            }

            var link = $('<a href=""></a>');
            var venueElement = element.find('.Opta-Venue');
            var venue = venueElement.html() || '';
            venueElement.parent().remove();;


            var competition = '';
            if (typeof (optaData) !== 'undefined' && optaData !== null && typeof (optaData.competition) !== 'undefined' && typeof (optaData.competition.full) !== 'undefined') {
                competition = optaData.competition;
            }

            //Ed:EFLTECH-2572: use UK date/time for all (widget settings are configured to Europe/London already).
            var time = element.find('.Opta-Outer.Opta-Time').detach().html();
            var dateLocal = new Date(parseInt(element.attr('data-date')));

            //modify for GMT/BST
            dateLocal.setHours(dateLocal.getHours() + 1);

            var date = new Date(dateLocal.getUTCFullYear(), dateLocal.getUTCMonth(), dateLocal.getUTCDate(), dateLocal.getUTCHours(), dateLocal.getUTCMinutes(), dateLocal.getUTCSeconds());

            // Laurence:EFLTECH-2725: check against opta returned data instead of time to cut out timezone issues
            // prematch - means before or during game, fulltime - means result
            // EFLRM-211: Caused issues when postponements appeared so taking a safer route to hide results - assume all are fixtures
            // unless it is definitely a result
            //var isFixture = element.attr('data-period').toLowerCase() === 'prematch';
            var isFixture = element.attr('data-period').toLowerCase() != 'fulltime';

            var homeTeamName;
            var awayTeamName;
            var time;

            // check if a game has a postponed status
            if (element.attr('data-period').toLowerCase() === 'postponed') {
                time = 'PP';
            }

            var homeCrest, awayCrest, homeScore, awayScore;
            var gameType;
            var matchId = element.attr('data-match');
            var spacer = $('<tbody class="spacer"><tr><td colspan="12"></td></tr></tbody>');
            var penalties = element.find('.Opta-penalties');

            if (isFixture) {
                element.addClass('fixture-configuration');
                if (typeof (teamId) === 'undefined' || teamId === '' || isClubId(homeTeamId, teamId) || isFixtureReleaseWidget) {
                    gameType = 'H';
                    homeCrest = element.find('.Opta-Image-Team-' + homeTeamId).detach().html();
                    awayCrest = element.find('.Opta-Image-Team').detach().html();
                    homeScore = element.find('.Opta-Score.Opta-Team-' + homeTeamId).detach().html();
                    awayScore = element.find('.Opta-Score').detach().html();
                    homeTeamName = element.find('.Opta-TeamName.Opta-Home').detach().html();
                    awayTeamName = element.find('.Opta-TeamName.Opta-Away').detach().html();
                }
                else {
                    gameType = 'A';
                    awayCrest = element.find('.Opta-Image-Team-' + homeTeamId).detach().html();
                    homeCrest = element.find('.Opta-Image-Team').detach().html();
                    awayScore = element.find('.Opta-Score.Opta-Team-' + homeTeamId).detach().html();
                    homeScore = element.find('.Opta-Score').detach().html();
                    awayTeamName = element.find('.Opta-TeamName.Opta-Home').detach().html();
                    homeTeamName = element.find('.Opta-TeamName.Opta-Away').detach().html();
                }

            }
            else {
                element.addClass('results-configuration');

                // a fixture shows under results if on day of the fixture, even when it's not been played yet
                // this adds a results-view class to the widget and then CSS hides any non 'fulltime' rows
                var widget = element.parents('.widget-fixtures').first();
                /*if (!widget.hasClass('results-view')) {
                   widget.addClass('results-view');
                }*/

                if (typeof (teamId) === 'undefined' || teamId === '' || isClubId(homeTeamId, teamId)) {
                    gameType = 'H';
                    homeCrest = element.find('.Opta-Image-Team-' + homeTeamId).detach().html();
                    awayCrest = element.find('.Opta-Image-Team').detach().html();
                }
                else {
                    gameType = 'A';
                    awayCrest = element.find('.Opta-Image-Team-' + homeTeamId).detach().html();
                    homeCrest = element.find('.Opta-Image-Team').detach().html();
                }

                // Results page (fixture has a data-period of FullTime)
                homeScore = element.find('.Opta-Score.Opta-Team-' + homeTeamId).detach().html();
                awayScore = element.find('.Opta-Score').detach().html();
                homeTeamName = element.find('.Opta-TeamName.Opta-Home').detach().html();
                awayTeamName = element.find('.Opta-TeamName.Opta-Away').detach().html();

            }

            var item;

            if (!isFixtureReleaseWidget) {
                if (!isEfl) {
                    var FixtureData = matchData.data;
                    item = $.grep(FixtureData, function (obj) { return obj.GameId === "g" + matchId; })[0];
                }
                else if (isEfl && !hideTvChannels) {
                    // Get the BSI form the page
                    var blackoutSheetInfo = window.EFL.blackoutSheetInfo;
                    // If we have data
                    if (blackoutSheetInfo != null) {
                        // Set ITEM to the Object in BSI that matched the current match in the loop
                        var thisMatch = $.grep(blackoutSheetInfo, function (obj) { return obj.OptaId === "g" + matchId; })[0];

                        if (thisMatch) {
                            // Make sure we have the UserGeoData
                            if (userGeoData == null) {
                                userGeoData = getGeoData();
                            }
                            // If the data we need isn't null
                            if (userGeoData != null && userGeoData.Region != null && userGeoData.Country != null) {
                                // Get the Region which matches where we are
                                var region = $.grep(thisMatch.RegionAvailabilities, function (obj) { return obj.Region === userGeoData.Region })[0];

                                // If we have the region
                                if (region != null) {
                                    // Check the match is AvailableOnIfollow for this region
                                    if (region.AvailableOnIfollow != null && region.AvailableOnIfollow == true) {
                                        data.availableOnIfollow = true;
                                    }
                                    // If we're in the UK - also check for TV Channel
                                    if (userGeoData.Region == 0) {
                                        // Get the TV channel that matches the country we are in
                                        var tvChannel = $.grep(region.TVChannels, function (obj) { return obj.CountryCode === userGeoData.Country })[0];
                                        if (tvChannel != null) {
                                            data.tvChannel = tvChannel;
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }

            data.awayCrest = awayCrest;
            data.homeCrest = homeCrest;
            data.awayScore = $(awayScore).text();
            data.homeScore = $(homeScore).text();
            data.homeId = homeId;
            data.awayId = awayId;
            data.awayTeamName = typeof (awayTeamName) != 'undefined' && awayTeamName != 'undefined' ? awayTeamName : '';
            data.homeTeamName = homeTeamName || '';
            data.time = time;
            data.gameType = gameType || '';
            data.date = date.toDateString();
            data.competition = competition;
            data.venue = venue || '';
            data.matchId = matchId;
            data.spacer = spacer;
            data.penalties = penalties;
            data.isFixture = isFixture;
            if (!isEfl  && typeof item !== 'undefined' && item.ROIOverride) {
                data.customLinkText = item.ROIOverride;
            }
            else {
                data.customLinkText = root.data('customLinkText') || 'Register on iFollow';
            }
            if (!isEfl && typeof item !== 'undefined' && item.RegisterOnIfollowURL != null) {
                data.RegisterOnIfollowURL = item.RegisterOnIfollowURL;
            }
            data.customHighlightsLinkText = root.data('customHighlightsLinkText') || 'Watch on iFollow';

            if (!isFixture && isNaN(parseInt($(homeScore).text()))) {
                //data.gameType = element.data('period').toString().substring(0, 1);
                element.addClass('fixture-configuration');
                element.removeClass('results-configuration');
            }

            return data;
        }


        function getTvChannelImage(tvChannel) {
            if (tvChannel.Logo != null) {
                return "<img src='" + tvChannel.Logo + "' class='tvchannel-logo' alt='Available on " + tvChannel.Name + "' title='" + tvChannel.Name + "'/>"
            }
        }

        function getIfollowImage() {
            return "<img src='/static/images/icons/efl-ifollow-extra-small.png' class='tvchannel-logo' alt='Available on iFollow' />"
        }

        function getCompetitionImage(competition) {
            var competitionId = competition.id;
            var competitionName = competition.full.replace(/'/g, '');
            var competitionlogo = $.grep(window.EFL.competitionLogos, function (obj) { return obj.OptaId === competitionId; })[0];
            if (competitionlogo) {
                if (competitionlogo.ImgSrc === '') {
                    return competitionlogo.Title;
                }
                
                return '<img src="' + competitionlogo.ImgSrc + '" alt ="' + competitionlogo.Title + ' logo" class="competition-logo" title="' + competitionlogo.Title + '" />';
            } else {
                return competitionName;
            }
        }

        function isClubId(id, clubIds) {
            isClub = false;
            if (typeof (clubIds) !== 'undefined') {
                //if we have more than one id for the club we need to split them and check against each one
                if (clubIds.toString().indexOf(',') !== -1) {
                    var ids = clubIds.toString().split(',');
                    var i = 0;
                    while (!isClub && i < ids.length) {
                        isClub = id == ids[i];
                        i++;
                    }
                }
                else {
                    isClub = id == clubIds;
                }
            }
            return isClub;
        }

        /*Set fixture row as Home or Away or as a title */
        function setGameType(element, spacer) {
            if (element.hasClass('.Opta-fixture')) {
                if (homeTeamId == teamId.toString()) {
                    element.addClass("home-game");
                    element.prev().addClass("home-game");
                    spacer.addClass("home-game");
                } else {
                    element.addClass("away-game");
                    element.prev().addClass("away-game");
                    spacer.addClass("away-game");
                }
            }
            else {
                element.addClass("title-body");
            }
        }

        function isEmpty(obj) {
            for (var prop in obj) {
                if (obj.hasOwnProperty(prop))
                    return false;
            }

            return JSON.stringify(obj) === JSON.stringify({});
        }
    }

    $(settings.trigger).each(init);

})();

/* MANUAL FIXTURES */
/* Manual match centre version of Fixtures widget, data source is not Opta*/
/* TODO: Consolidate with above script if there is time, perhaps extract common functions into their own utility class */
(function () {
    'using strict';
    var settings = {
        trigger: '[data-widget="manual-fixtures"]',
    };

    function init() {
        var element = $(this);
        var root = element;
        var isUpdating = false
        var packagesPageUrl = element.attr("data-packages-url");
        var scopeId = element.data('scopeId') || '';

        /*Bind Events*/
        window.EFL.eventDispatcher.registerHandler('on-filters-change', onFilterChange, scopeId);

        /* On ready request the current filters*/
        $(function () {
            window.EFL.eventDispatcher.dispatch('request-filters', onRequestFilters, scopeId);
        });

        function onRequestFilters(filters) {
            onFilterChange(filters);
        }

        /* callback to update the widget when the filters change */
        function onFilterChange(filters) {

            filters.season = root.attr("data-season");
            filters.useteam = root.attr("data-useteam");
            filters.excludeopta = root.attr("data-excludeopta");

            if (!isUpdating) {
                isUpdating = true;

                filters = filters || {};

                url = '/api/fixturelinks/?team=' + (filters.team || '') + '&competition=' + (filters.competition || '') + '&season=' + (filters.season || '') + '&useteam=' + (filters.useteam || '') + '&excludeopta=' + (filters.excludeopta ? 'true' : 'false');

                var xhr = $.get(url, function (data) {
                    onFixtureUpdate({
                       filters: filters,
                        data: data
                    });
                });
            }
        }

        //This will pass through the custom link data as well as the filters
        function onFixtureUpdate(data) {
            console.log("on-fixture-update-manual manual-fixtures");
            var competition = data.filters.competition;// || optaWidget.widget.attr_original.competition;// original.attr('competition');
            var team = data.filters.team;// || optaWidget.widget.attr_original.team;
            var season = data.filters.season;// || optaWidget.widget.attr_original.season;
            var gameIds = [];
            var KickOffMonths = [];
            //STRANGE BEHAVIOUR WITH OPTA - multiple competitions require seasons to be duplicated -> e.g. competition 10,7,2  requires a season value of 2016,2016,2016
            season = createSeason(competition, season);

            matchData = data;

            $.each(data.data, function (index, element) {
                gameIds.push(parseInt(element.GameId));
                KickOffMonths.push(element.KickOffMonth);
            });
      
            element.find('.manual-fixture-month-heading').each(function (index, row) {
                var $this = $(this);
                var date = $this.data('date');
                if (jQuery.inArray(date, KickOffMonths) == -1) {
                    $this.addClass("hidden");
                }
                else {
                    $this.removeClass("hidden");
                }
            });

            element.find('tbody').each(function (index, row) {
                var id, links, firstLinks, lastLinks, data;
                row = $(row);
                id = row.data('match');
                if (jQuery.inArray(id, gameIds) == -1) {
                    row.addClass("hidden");
                    return;
                }
                else {
                    row.removeClass("hidden");
                }
                data = {};
                data.matchId = id;
                data.kickoff = row.data('kickoff');
                data.date = row.data('date');
                data.homeTeamName = row.data('homeTeam');
                data.awayTeamName = row.data('awayTeam');
                data.matchStatus = row.data('match-status');

                data.customLinkText = root.data('customLinkText') || 'Register on iFollow';
                data.customHighlightsLinkText = root.data('customHighlightsLinkText') || 'Watch on iFollow';

                firstLinks = row.find('.links-first');
                lastLinks = row.find('.links-last');

                if (data.matchStatus != "postponed" && data.matchStatus != "cancelled") {
                    links = window.EFL.fixtures.buildLinks(matchData, data, packagesPageUrl);
                    firstLinks.empty().append(links.firstLinks || '');
                    lastLinks.empty().append(links.lastLinks || '');
                }
                if (row.find('.links-first').find('.empty').length > 0) {
                    firstLinks.addClass('empty');
                }
                else {
                    firstLinks.removeClass('empty');
                }

                if (row.find('.links-last').find('.empty').length > 0) {
                    lastLinks.addClass('empty');
                }
                else {
                    lastLinks.removeClass('empty');
                }

                if (!row.find('.links-last').html()) {
                    lastLinks.addClass('empty');
                }
                else {
                    lastLinks.removeClass('empty');
                }

                // add name of game to each link so that screen readers have some context when reading it
                row.find('a').prepend('<span class="sr-only">' + data.homeTeamName + ' versus ' + data.awayTeamName + ', ' + data.date + ', </span>');
            });

            if (!matchData.data.length > 0) {
                element.find(".Opta-Empty").removeClass("hidden");
            }
            else {
                element.find(".Opta-Empty").addClass("hidden");
            }

            isUpdating = false;
        };

        //STRANGE BEHAVIOUR WITH OPTA - multiple competitions require seasons to be duplicated -> e.g. competition 10,7,2  requires a season value of 2016,2016,2016
        function createSeason(competition, season) {
            var competitionCount = 0, collatedSeason = season;

            competitionCount = competition.toString().split(',').length;

            if (typeof (competition !== 'undefined') && typeof (season !== 'undefined') && season.toString().indexOf(',') === - 1 && competitionCount > 1) {
                collatedSeason = '';

                for (var i = 0; i < competitionCount; i++) {
                    collatedSeason += season + ',';
                }
                collatedSeason = collatedSeason.substring(0, collatedSeason.length - 1);
            }

            //If single competition but we are holding onto multi-seasons
            if (competitionCount <= 1 && season.toString().split(',').length > 1) {
                collatedSeason = season.toString().split(',')[0];
            }

            return collatedSeason;
        }
    }

    // Call init on each found widget
    $(settings.trigger).each(init);

})();


function findDLIndex(key) {
    var index;
    for (var i = 0; i < window.dataLayer.length; ++i) {
        if (typeof window.dataLayer[i][key] !== 'undefined') {
            index = i;
            break;
        }
    }
    return index;
}
;
window.EFL = window.EFL || {};
window.EFL.fixtures = window.EFL.fixtures || {};

/*Build the custom links that are attached to the side of fixtures*/
window.EFL.fixtures.buildEFLLinks = function (matchData, data, packagesPageUrl) {
    var firstLinks, lastLinks, hasLinks = false;
    
    if (typeof (data) !== 'undefined' && typeof (matchData) !== 'undefined' && matchData != null) {

        var found = false;
        var i = 0;
        while (!found && i < matchData.data.length) {
            found = matchData.data[i].GameId.indexOf(data.matchId) !== -1;

            if (found) {
                firstLinks = '';
                lastLinks = '';
                var startcol2 = 2;
                //Always link to packages page if there is a url
                if (packagesPageUrl) {
                    hasLinks = true;
                    firstLinks += '<span class="link"><span aria-hidden="true" class="icon icon-Video"></span><a target="_blank" href="' + packagesPageUrl + '" class=""><span>Watch on iFollow</span></a></span>';
                    startcol2 = 1;
                }
                for (var j = 0; j < matchData.data[i].Links.length; j++) {

                    if (j < startcol2) {
                        hasLinks = true;
                        firstLinks += '<span class="link"><span aria-hidden="true" class="' + matchData.data[i].Links[j].CssClass + '"></span><a target="_blank" href="' + matchData.data[i].Links[j].Url + '" class=""><span>' + matchData.data[i].Links[j].Label + '</span></a></span>';
                    }
                    else {
                        lastLinks += '<span class="link"><span  aria-hidden="true" class="' + matchData.data[i].Links[j].CssClass + '"></span><a target="_blank" href="' + matchData.data[i].Links[j].Url + '" class=""><span>' + matchData.data[i].Links[j].Label + '</span></a></span>';
                    }
                }

                if (packagesPageUrl) {
                    if (matchData.data[i].Links % 2 === 1) {
                        lastLinks += '<span class="link"><a href="#"></a></span>';
                    }
                } else {
                    if (matchData.data[i].Links % 2 === 0) {
                        lastLinks += '<span class="link"><a href="#"></a></span>';
                    }
                }

                if (firstLinks) {
                    firstLinks = '<span class="links">' + firstLinks + '</span>'
                }

                if (lastLinks) {
                    lastLinks = '<span class="links">' + lastLinks + '</span>'
                }
            }
            i++;
        }

      

    }

    return {
        hasLinks: hasLinks,
        firstLinks: firstLinks,
        lastLinks: lastLinks
    }

};

/* OPTA FIXTURES */
(function () {
    'using strict';
    var settings = {
        trigger: '[data-widget="fixtures-efl"]',
    };

    //template for rebuilding widget
    var widgetHtml = $('<opta-widget sport="football" data-widget-id="fixtures" widget="fixtures" competition="10" season="2016" team="19" template="normal" live="false" show_venue="true" match_status="all" grouping="month" show_grouping="true" default_nav="1" start_on_current="true" switch_current="0" sub_grouping="competition|date" show_subgrouping="true" order_by="date_ascending" show_crests="true" date_format="dddd D MMMM YYYY" month_date_format="MMMM" competition_naming="full" team_naming="full" pre_match="false" show_live="false" show_logo="false" show_title="false" breakpoints=""></opta-widget>');

    function init() {

        var element, root, originalWidgetElement, initialised, matchId, competitionId, dateFrom, dateTo, dateOrder, teamId, venueType, optaWidget, packagesPageUrl, isUpdating = false, isManual, scopeId, originalSeason;
        var hasfilter;
        initialised = false;

        element = $(this);
        var optawidgetid = element.find('opta-widget').attr('data-widget-id');

        scopeId = element.data('scopeId');
        console.log(scopeId);

        isManual = element.data('isManual') || false;
        dateFrom = element.attr("data-date-from");
        dateTo = element.attr("data-date-to");
        dateOrder = element.attr("data-date-order");
        packagesPageUrl = element.attr("data-packages-url");
        originalSeason = element.attr("data-season");
        hasfilter = element.attr("data-hasfilter")=="True" || false;
        competitionId = element.attr("data-competition");

        element.hide();
        root = element;

        originalWidgetElement = element.find('opta-widget');

        if (originalWidgetElement.length > 0) {
            originalWidgetElement = originalWidgetElement[0].outerHTML;
        }

        /* Bind Events */
        window.EFL.eventDispatcher.registerHandler('on-filters-change', onFilterChange, scopeId);
        window.EFL.MatchCentre.registerWidget(optawidgetid, element, onDrawn, onError);

        /* On ready request the current filters*/
        $(function () {
            var event = isManual ? 'request-manual-filters' : 'request-filters';
            window.EFL.eventDispatcher.dispatch(event, onRequestFilters, scopeId);
            if(!hasfilter) {
                var season = createSeason(competitionId, originalSeason);
                loadWidget(element, element.find('opta-widget'), competitionId, dateFrom, dateTo, dateOrder, "", season);
            }

        });

        
        /*Return method for recieving the results of a request to get the current filters*/
        function onRequestFilters(filters) {
            if (!isUpdating) {
                console.log("on-request-filters opta-fixtures");
                
                if (typeof (filters) === 'undefined' || isEmpty(filters)) {
                    return;
                }
                
                if (typeof (filters.competition) != 'undefined') {
                    //STRANGE BEHAVIOUR WITH OPTA - multiple competitions require seasons to be duplicated -> e.g. competition 10,7,2  requires a season value of 2016,2016,2016
                    filters.season = createSeason(filters.competition, originalSeason);
                }

                loadWidget(element, element.find('opta-widget'), filters.competition, dateFrom, dateTo, dateOrder, filters.team, filters.season);
                
            }
        };

        /* event listener to detect filter changes and update the widget */
        function onFilterChange(filters) {

            if (!isUpdating) {
                isUpdating = true;
                console.log("onFilterChange opta-fixtures");
                onFixtureUpdate({
                        filters: filters
                });
                //filters = filters || {};
                //url = '/api/fixturelinks/?team=' + (filters.team || '') + '&competition=' + (filters.competition || '') + '&season=' + (filters.season || '');
                //var xhr = $.get(url, function (data) {
                    
                //});
            }
        }

        /* Opta OnDrawn event callback - executes when opta renders widget */
        function onDrawn(element, optaItem) {
            console.log("onDrawn opta-fixtures");
            optaWidget = optaItem;
            initialised = element.attr('data-initialised') === 'true';

            var elements = element.find('tbody.Opta-fixture');
            var titles = element.find('tbody .Opta-title');
            var maxRows = typeof (element.data('maxresults')) != 'undefined' && !isNaN(parseInt(element.data('maxresults'))) ? parseInt(element.data('maxresults')) : elements.length;

            for (var i = 0; i < elements.length; i+=2) {
                // if maxRows is -1 then do not remove
                if (i < maxRows || maxRows == -1) {
                    constructView(i, elements[i]);
                }
                else {
                    $(elements[i]).prev().remove();
                    elements[i].remove();
                }
            }

            element.attr('data-initialised', 'true');
            initialised = true;

            root.css('height', '');
            console.log('fixtures drawn');

            //Replace No data message
            //Check for the override
            if (EFL && EFL.optaNoDataText) {
                setNoDataMessage(element, ".Opta.Opta-Normal.Opta-Empty .Opta-Cf p", EFL.optaNoDataText);
            }
        };

        function setNoDataMessage(element, selector, text) {
            if (element.length > 0) {
                var message = element.find(selector);
                if (message.length > 0) {
                    message.empty().append(text);
                }
            }
        };

        /* Opta OnError event callback - executes when opta finds a problem */
        function onError(element, optaItem) {
            console.log("onError opta-fixtures");
            optaWidget = optaItem;

            root.css('height', '');

            //Replace No data message
            //Check for the override 
            if (EFL && EFL.optaNoDataText) {
                setNoDataMessage(element, ".Opta-Error .Opta-Cf p", EFL.optaNoDataText);
            }
        }

        //Used to update the fixtures table with our own data
        function onFixtureUpdate(data) {
            console.log("on-fixture-update opta-fixtures");

            var original = $(originalWidgetElement);
            var competition = data.filters.competition;// || optaWidget.widget.attr_original.competition;// original.attr('competition');
            var season = originalSeason;// || optaWidget.widget.attr_original.season;
            var team = "";
            //STRANGE BEHAVIOUR WITH OPTA - multiple competitions require seasons to be duplicated -> e.g. competition 10,7,2  requires a season value of 2016,2016,2016
            season = createSeason(competition, season);

            if (typeof (optaWidget) === "undefined") {
                loadWidget(element, element.find('opta-widget'), competition, dateFrom, dateTo, dateOrder, team, season);
            }
            else {
                reloadWidget(element, optaWidget, original, competition, dateFrom, dateTo, dateOrder, team, season)
            }

        };

        /*Only way to change the state of the widget is to reconstruct it with new attribute options*/
        function reloadWidget(element, optaItem, widgetHtml, competitionId, pdateFrom, pdateTo, pdateOrder, teamId, season) {
            console.log("reloadWidget opta-fixtures");
            var height = root.height();
            root.css('height', height);
            optaItem.widget.destroy(true);
            element.empty();

            var newWidget = widgetHtml.clone();
            newWidget.attr('competition', competitionId);
            newWidget.attr('date_from', pdateFrom);
            newWidget.attr('date_to', pdateTo);
            newWidget.attr('team', teamId);
            newWidget.attr('order_by', pdateOrder);
            newWidget.attr('season', season);
            newWidget.attr('load', 'true');
            element.append(newWidget);
            element.show();

            if (typeof (Opta) !== 'undefined') {
                Opta.start();
            } else {
                console.log('Opta unavailable');
            }

            isUpdating = false;
        }

        /* is run before opta widget is loaded to change configuration, once load becomes true only reload will work */
        function loadWidget(element, widgetHtml, competitionId, pdateFrom, pdateTo, pdateOrder, teamId, season) {
            console.log("loadWidget opta-fixtures");
            widgetHtml.attr('competition', competitionId);
            widgetHtml.attr('date_from', pdateFrom || '');
            widgetHtml.attr('date_to', pdateTo || '');
            widgetHtml.attr('order_by', pdateOrder);
            widgetHtml.attr('team', teamId);
            widgetHtml.attr('season', season);
            element.show();
            widgetHtml.attr('load', 'true');

            if (typeof (Opta) !== 'undefined') {
                Opta.start();
            } else {
                console.log('Opta unavailable');
            }

            isUpdating = false;
        }

        //STRANGE BEHAVIOUR WITH OPTA - multiple competitions require seasons to be duplicated -> e.g. competition 10,7,2  requires a season value of 2016,2016,2016
        function createSeason(competition, season) {
            var competitionCount = 0, collatedSeason = season;

            competitionCount = competition.toString().split(',').length;

            if (typeof (competition !== 'undefined') && typeof (season !== 'undefined') && season.toString().indexOf(',') === -1 && competitionCount > 1) {
                collatedSeason = '';

                for (var i = 0; i < competitionCount; i++) {
                    collatedSeason += season + ',';
                }
                collatedSeason = collatedSeason.substring(0, collatedSeason.length - 1);
            }

            //If single competition but we are holding onto multi-seasons
            if (competitionCount <= 1 && season.toString().split(',').length > 1) {
                collatedSeason = season.toString().split(',')[0];
            }

            return collatedSeason;
        }

        /* Clears opta widget rows and constructs new markup from the data */
        function constructView(index, element) {
            console.log("constructView opta-fixtures");
            element = $(element);

            //gathers data from the opta widget
            var data = gatherRowData(element);
            if (data == null) return;
            var links;
                element.find('td').remove();
                element.find('tr').first().append($('<td class="crest"><span>' + data.awayCrest + '</span></td>'));
                element.find('tr').first().append($('<td class="match-info"><span><span class="home-team">' + data.homeTeamName + '</span><br class="home-team"><span class="away-team">' + data.awayTeamName + '</span></br><span class="venue">' + data.venue + '</span></span></td>'));
                element.find('tr').first().append($('<td class="game-type"><span class="align-wrapper"><span class="score">' + data.homeScore + '</span><span class="score">' + data.awayScore + '</span><span class="side">' + data.gameType + '</span></span></td>'));
                element.find('tr').first().append($('<td class="team-name"><span><span class="match-date">' + data.date + ' <span class="time">' + data.time + ' (UK)</span></span></br>' + data.competition + '</span></td>'));

                if (data.penalties.length > 0) {
                    element.addClass('additional-fixture-info');
                    element.find('tr').last().addClass('penalties-section').append('<td>' + data.penalties.html() + '</td>');
                }
            //creates the fixture links by comparing the match id against a json list of matchid-links
            links = window.EFL.fixtures.buildEFLLinks(null, data, packagesPageUrl);

            links.firstLinks && element.find('tr').first().append($('<td class="links-first' + (links.hasLinks ? ' filled' : '') + '">' + links.firstLinks + '</td>'));
            links.lastLinks && element.find('tr').first().append($('<td class="links-last">' + links.lastLinks + '</td>'));
            
            element.prev().remove();
            root.find('table').attr('cellpadding', 0);
            root.find('table').attr('cellspacing', 0);
            setGameType(element, data.spacer)
        }

        /*Scrapes the opta widget to get the relevant data to build in the new structure*/
        //TODO: Refactor Below to maybe get the same data through the Opta object
        function gatherRowData(element) {
            
            var data = {};
            var homeElement = element.find('td.Opta-Home').first();
            if (homeElement.length == 0) return null;
            var classes = homeElement.attr('class');
            
            var teamIds = classes.match(/[A-z]*-[A-z]*-[0-9]*/g);
            var homeTeamId;
            //  var teamId = optaWidget.widget.attr_original.team;
            if (teamIds.length > 0) {
                homeTeamId = teamIds[0].replace('Opta-Team-', '');
            }

            var link = $('<a href=""></a>');
            var venueElement = element.find('.Opta-Venue');
            var venue = venueElement.html();
            venueElement.parent().remove();;
            var competition = element.prev().find('span').first().html();
            var date = new Date(parseInt(element.attr('data-date')));
            // Laurence:EFLTECH-2725: check against opta returned data instead of time to cut out timezone issues
            // prematch - means before or during game, fulltime - means result
            // EFLRM-211: Caused issues when postponements appeared so taking a safer route to hide results - assume all are fixtures
            // unless it is definitely a result
            //var isFixture = element.attr('data-period').toLowerCase() === 'prematch';
            var isFixture = element.attr('data-period').toLowerCase() != 'fulltime';

            var homeTeamName;
            var awayTeamName;
            var time = element.find('.Opta-Outer.Opta-Time').detach().html();
            var homeCrest, awayCrest, homeScore, awayScore;
            var gameType;
            var matchId = element.attr('data-match');
            var spacer = $('<tbody class="spacer"><tr><td colspan="12"></td></tr></tbody>');
            var penalties = element.find('.Opta-penalties');

            if (isFixture) {
                element.addClass('fixture-configuration');
                gameType = 'H';
                homeCrest = element.find('.Opta-Image-Team-' + homeTeamId).detach().html();
                awayCrest = element.find('.Opta-Image-Team').detach().html();
                homeScore = element.find('.Opta-Score.Opta-Team-' + homeTeamId).detach().html();
                awayScore = element.find('.Opta-Score').detach().html();
                homeTeamName = element.find('.Opta-TeamName.Opta-Home').detach().html();
                awayTeamName = element.find('.Opta-TeamName.Opta-Away').detach().html();
                
            }
            else {
                element.addClass('results-configuration');
                homeCrest = element.find('.Opta-Image-Team-' + homeTeamId).detach().html();
                awayCrest = element.find('.Opta-Image-Team').detach().html();
                
                //REsults page
                homeScore = element.find('.Opta-Score.Opta-Team-' + homeTeamId).detach().html();
                awayScore = element.find('.Opta-Score').detach().html();
                homeTeamName = element.find('.Opta-TeamName.Opta-Home').detach().html();
                awayTeamName = element.find('.Opta-TeamName.Opta-Away').detach().html();
            }
           
            data.awayCrest = awayCrest;
            data.homeCrest = homeCrest;
            data.awayScore = $(awayScore).text();
            data.homeScore = $(homeScore).text();
            data.awayTeamName = awayTeamName;
            data.homeTeamName = homeTeamName;
            data.time = time;
            data.gameType = gameType  || '';
            data.date = date.toDateString();
            data.competition = competition;
            data.venue = venue;
            data.matchId = matchId;
            data.spacer = spacer;
            data.penalties = penalties;

            if (!isFixture && isNaN(parseInt($(homeScore).text()))) {
                //data.gameType = element.data('period').toString().substring(0, 1);
                element.addClass('fixture-configuration');
                element.removeClass('results-configuration');
            }

            return data;
        }

        function isClubId(id, clubIds) {
            isClub = false;

            //if we have more than one id for the club we need to split them and check against each one
            if (clubIds.toString().indexOf(',') !== -1) {
                var ids = clubIds.toString().split(',');
                var i =0;
                while (!isClub && i < ids.length) {
                    isClub = id == ids[i];
                    i++;
                }
            }
            else {
                isClub = id == clubIds;
            }

            return isClub;
        }

        /*Set fixture row as Home or Away or as a title */
        function setGameType(element, spacer) {
            if (element.hasClass('.Opta-fixture')) {
                if (homeTeamId == teamId.toString()) {
                    element.addClass("home-game");
                    element.prev().addClass("home-game");
                    spacer.addClass("home-game");
                } else {
                    element.addClass("away-game");
                    element.prev().addClass("away-game");
                    spacer.addClass("away-game");
                }
            }
            else {
                element.addClass("title-body");
            }
        }

        function isEmpty(obj) {
            for (var prop in obj) {
                if (obj.hasOwnProperty(prop))
                    return false;
            }

            return JSON.stringify(obj) === JSON.stringify({});
        }
    }

    $(settings.trigger).each(init);

})();
;
/* OPTA FIXTURES */
(function () {
    'using strict';
    var settings = {
        trigger: '[data-widget="latest-scores"]',
    };

    //template for rebuilding widget
    var widgetHtml = $('<opta-widget load="false" data-widget-id="latest-scores" sport="football" widget="fixtures" template="normal" live="true" competition="10" season="2017" match="" team="" team_filter="" days_ahead="" days_before="" venue="" show_venue="false" group="" matchday="" round="" match_status="all" grouping="date" show_grouping="true" limit="" navigation="" default_nav="1" start_on_current="true" switch_current="0" sub_grouping="date" show_subgrouping="false" order_by="date_ascending" show_crests="true" date_format="dddd D MMMM YYYY" month_date_format="MMMM" competition_naming="full" team_naming="full" team_link="" match_link="" pre_match="false" show_live="false" show_logo="true" title="" show_title="true" breakpoints="">');

    function init() {
        var finishedOnPenalties = false;
        var fixtures;
        var element, root, originalWidgetElement, initialised, matchId, competitionId, dateFrom, dateTo, dateOrder, teamId, venueType, optaWidget, packagesPageUrl, isUpdating = false, isManual, scopeId, originalSeason;
        var hasfilter;
        initialised = false;

        element = $(this);
        var optawidgetid = element.find('opta-widget').attr('data-widget-id');

        scopeId = element.data('scopeId');

        dateFrom = element.attr("data-date-from");
        dateTo = element.attr("data-date-to");
        season = element.attr("data-season");
        hasfilter = element.attr("data-hasfilter") == "True" || false;
        competitionId = element.attr("data-competition");
        element.hide();
        root = element;

        originalWidgetElement = element.find('opta-widget');

        if (originalWidgetElement.length > 0) {
            originalWidgetElement = originalWidgetElement[0].outerHTML;
        }

        /* Bind Events */
        window.EFL.eventDispatcher.registerHandler('on-filters-change', onFilterChange, scopeId);

        if (element.find('[data-widget-id="latest-scores"]').length > 0) {
            window.EFL.MatchCentre.registerWidget(optawidgetid, element, onDrawn, onError);
        }

        /* On ready request the current filters*/
        $(function () {
            var event = isManual ? 'request-manual-filters' : 'request-filters';
            window.EFL.eventDispatcher.dispatch(event, onRequestFilters, scopeId);
            if (!hasfilter) {
                loadWidget(element, element.find('opta-widget'), competitionId, dateFrom, dateTo, dateOrder, "", season);
            }

        });

        /*Return method for recieving the results of a request to get the current filters*/
        function onRequestFilters(filters) {
            if (!isUpdating && initialised) {
                console.log("on-request-filters latest-scores");

                if (typeof (filters) === 'undefined' || isEmpty(filters)) {
                    return;
                }

                loadWidget(element, element.find('opta-widget'), filters.competition, dateFrom, dateTo, dateOrder, filters.team, filters.season);

            }
        };

        /* event listener to detect filter changes and update the widget */
        function onFilterChange(filters) {
            if (!isUpdating && initialised) {
                isUpdating = true;
                console.log("onFilterChange latest-scores");
                onFixtureUpdate({
                    filters: filters,
                });
            }
        }

        /* Opta OnDrawn event callback - executes when opta renders widget */
        function onDrawn(element, optaItem) {
            console.log("onDrawn latest-scores");

            element.find('.pre-load-content').removeClass('hidden');
            element.attr('team', '37');

            fixtures = element.find('tbody.Opta-fixture');

            fixtures.each(function (index, element) {
                element = $(element);

                if (element.find('.Opta-penalties').length > 0) {
                    element.addClass('js-penalties')
                }
            });


            optaWidget = optaItem;
            initialised = element.attr('data-initialised') === 'true';

            var elements = element.find('tbody.Opta-fixture');
            var titles = element.find('tbody .Opta-title');

            // Get competition
            var competitionlogo = getCompetitionImage(optaWidget.widget.attr.competition);
            var preLoadContent = $(".widget-live-scores p.pre-load-content");

            if (competitionlogo.length > 0) {
                preLoadContent.empty().append(competitionlogo);
                preLoadContent.find("img").addClass("competition-logo");
            } else { // text, in case title overridden
                var competitionhtml = $(".competition").html();
                preLoadContent.empty().append(competitionhtml);
            }

            element.attr('data-initialised', 'true');
            initialised = true;

            root.css('height', '');
            console.log('latest-scores drawn');

            //Replace No data message
            //Check for the override
            if (EFL && EFL.optaNoDataText) {
                setNoDataMessage(element, ".Opta.Opta-Normal.Opta-Empty .Opta-Cf p", EFL.optaNoDataText);
            }

        };

        function setNoDataMessage(element, selector, text) {
            if (element.length > 0) {
                var message = element.find(selector);
                if (message.length > 0) {
                    message.empty().append(text);
                }
            }
        };

        /* Opta OnError event callback - executes when opta finds a problem */
        function onError(element, optaItem) {
            console.log("onError latest-scores");
            optaWidget = optaItem;

            root.css('height', '');
        }

        //Used to update the fixtures table with our own data
        function onFixtureUpdate(data) {
            console.log("on-fixture-update latest-scores");

            var original = $(originalWidgetElement);
            var competition = data.filters.competition;// || optaWidget.widget.attr_original.competition;// original.attr('competition');

            var team = "";
            if (typeof (optaWidget) === "undefined") {
                loadWidget(element, element.find('opta-widget'), competition, dateFrom, dateTo, dateOrder, team, season);
            }
            else {
                reloadWidget(element, optaWidget, original, competition, dateFrom, dateTo, dateOrder, team, season)
            }

        };

        /*Only way to change the state of the widget is to reconstruct it with new attribute options*/
        function reloadWidget(element, optaItem, widgetHtml, competitionId, pdateFrom, pdateTo, pdateOrder, teamId, season) {
            console.log("reloadWidget latest-scores");
            var height = root.height();
            root.css('height', height);
            optaItem.widget.destroy(true);
            element.empty();

            var newWidget = widgetHtml.clone();

            var selectedFilter = $('[data-filter-name="competition"]').find('[data-selected="true"]');
            var showSubGrouping = selectedFilter.attr("data-showSubgroup");
           
            newWidget.attr('competition', competitionId);
            newWidget.attr('date_from', pdateFrom);
            newWidget.attr('date_to', pdateFrom);
            newWidget.attr('team', teamId);
            newWidget.attr('order_by', pdateOrder);
            newWidget.attr('season', season);
            newWidget.attr('load', 'true');
            if (showSubGrouping) {
                newWidget.attr('show_subgrouping', showSubGrouping);
            }
            element.append('<p class="fake-h3 hidden pre-load-content">' + competitionId + '</p>');
            element.append(newWidget);
            element.show();

            if (typeof (Opta) !== 'undefined') {
                Opta.start();
            } else {
                console.log('Opta unavailable');
            }

            isUpdating = false;
        }

        /* is run before opta widget is loaded to change configuration, once load becomes true only reload will work */
        function loadWidget(element, widgetHtml, competitionId, pdateFrom, pdateTo, pdateOrder, teamId, season) {
            widgetHtml.attr('competition', competitionId);
            widgetHtml.attr('date_from', pdateFrom || '');
            widgetHtml.attr('date_to', pdateFrom || '');
            widgetHtml.attr('order_by', pdateOrder);
            widgetHtml.attr('team', teamId);
            widgetHtml.attr('season', season);
            element.show();
            widgetHtml.attr('load', 'false');

            if (typeof (Opta) !== 'undefined') {
                Opta.start();
            } else {
                console.log('Opta unavailable');
            }

            isUpdating = false;
        }

        

        function getCompetitionImage(competition) {
            var competitionId = competition;
            var competitionlogo = $.grep(window.EFL.competitionLogos, function (obj) { return obj.OptaId === competitionId; })[0];
            if (competitionlogo) {
                if (competitionlogo.ImgSrc === '') {
                    return competitionlogo.Title;
                }
                
                return '<img src="' + competitionlogo.ImgSrc + '" alt ="' + competitionlogo.Title + ' logo" class="competition-logo" title="' + competitionlogo.Title + '" />';
            } else {
                return competition;
            }
        }

        function isEmpty(obj) {
            for (var prop in obj) {
                if (obj.hasOwnProperty(prop))
                    return false;
            }

            return JSON.stringify(obj) === JSON.stringify({});
        }
    }

    $(settings.trigger).each(init);

})();
;
window.EFL = window.EFL || {};
window.EFL.fixtures = window.EFL.fixtures || {};
window.EFL.liveScoreDetails = {};

/* OPTA FIXTURES */
(function () {
    'using strict';
    var settings = {
        trigger: '[data-widget="latest-scores-efl"]',
    };

    //template for rebuilding widget
    var widgetHtml = $('<opta-widget sport="football" widget="fixtures" template="normal" live="true" competition="" season="" show_venue="false" match_status="all" grouping="date" show_grouping="true" default_nav="1" start_on_current="true" switch_current="0" sub_grouping="date" show_subgrouping="false" order_by="date_ascending" show_crests="true" date_format="dddd D MMMM YYYY" month_date_format="MMMM" competition_naming="full" team_naming="full" pre_match="false" show_live="false" show_logo="true" show_title="true"></opta-widget>');
    var competitionLogo;

    function init() {
        
        var element, root, originalWidgetElement, initialised, matchId, competitionId, dateFrom, dateTo, dateOrder, teamId, venueType, matchData, optaWidget, packagesPageUrl, isUpdating = false, isManual, scopeId, loadDefaults;
        element = $(this);
        var optawidgetid = element.find('opta-widget').attr('data-widget-id');

        scopeId = element.data('scopeId');

        isManual = element.data('isManual') || false;
        dateFrom = element.attr("data-date-from");
        dateTo = element.attr("data-date-to");
        window.EFL.liveScoreDetails.dates = [dateFrom, dateTo];
        window.EFL.liveScoreDetails.season = element.attr('data-season');

        dateOrder = element.attr("data-date-order");
        packagesPageUrl = element.attr("data-packages-url");
        loadDefaults = element.attr("data-load-defaults") || false;

        element.hide();
        root = element;

        originalWidgetElement = element.find('opta-widget');

        if (originalWidgetElement.length > 0) {
            originalWidgetElement = originalWidgetElement[0].outerHTML;
        }

        /* Bind Events */
        window.EFL.eventDispatcher.registerHandler('on-filters-change', onFilterChange, scopeId);
        window.EFL.MatchCentre.registerWidget(optawidgetid, element, onDrawn, onError);


        if (loadDefaults) {
            element.show();
            if (typeof (Opta) !== 'undefined') {
                //     Opta.start();
            } else {
                console.log('Opta unavailable');
            }
        }
        else {

            /* On ready request the current filters*/
            $(function () {
                var event = isManual ? 'request-manual-filters' : 'request-filters';
                window.EFL.eventDispatcher.dispatch(event, onRequestFilters, scopeId);
            });

        }

        /*Return method for recieving the results of a request to get the current filters*/
        function onRequestFilters(filters, filterId) {
            if (filterId !== scopeId) {
                return;
            }

            if (!isUpdating) {
                console.log("on-request-filters opta-fixtures");

                if (typeof (filters) === 'undefined' || isEmpty(filters)) {
                    return;
                }

                //matchData exists after the widget has loaded, if its not available we need to load the image
                if (typeof (matchData) == 'undefined') {

                    onFilterChange(filters);
                }
                else {

                    loadWidget(element, element.find('opta-widget'), filters.competition, dateFrom, dateTo, dateOrder, filters.team, window.EFL.liveScoreDetails.season);
                }
            }
        };

        /* event listener to detect filter changes and update the widget */
        function onFilterChange(filters) {
            // get season from markup, filters aren't used for season
            console.log(filters);
            competitionLogo = getCompetitionImage(filters.competition);

            if (!isUpdating) {
                isUpdating = true;
                console.log("onFilterChange opta-fixtures");

                url = '/api/fixturelinks/?competition=' + (filters.competition || '') + '&season=' + filters.season + '&excludeopta=' + (filters.excludeopta ? 'true' : 'false');

                var xhr = $.get(url, function (data) {

                    onFixtureUpdate({
                        filters: filters,
                        data: data
                    });
                });
            }
        }

        /* Opta OnDrawn event callback - executes when opta renders widget */
        function onDrawn(element, optaItem) {
            var optaItemDataScopeId = optaItem.widget.attr['data-scope-id'];
            var elementDataScopeId = element[0].attributes['data-scope-id'].value;
            if (optaItemDataScopeId != elementDataScopeId) {
                return;
            }

            element.addClass('custom');
            console.log("onDrawn opta-fixtures");
            optaWidget = optaItem;
            initialised = element.attr('data-initialised') === 'true';

            // if (!initialised) {
            element.attr('data-initialised', 'true');

            var title = element.find('.Opta-title h3 span');
            var logoContainer = $('<span class="competition" />');
            logoContainer.append(competitionLogo);
            title.before(logoContainer);
            var elements = element.find('tbody.Opta-fixture');

            element.attr('data-initialised', 'true');

            root.css('height', '');
            element.addClass('widget-loaded');
            console.log('fixtures drawn');

            //Replace No data message
            //Check for the override
            if (EFL && EFL.optaNoDataText) {
                setNoDataMessage(element, ".Opta.Opta-Normal.Opta-Empty .Opta-Cf p", EFL.optaNoDataText);
            }
            else {
                //Use the old original value if nothing set
                setNoDataMessage(element, ".Opta.Opta-Normal.Opta-Empty .Opta-Cf p", "There are currently no fixtures to display. Please check back soon.");
            }

        };

        function setNoDataMessage(element, selector, text) {
            if (element.length > 0) {
                var message = element.find(selector);
                if (message.length > 0) {
                    message.empty().append(text);
                }
            }
        };

        /* Opta OnError event callback - executes when opta finds a problem */
        function onError(element, optaItem) {
            console.log("onError opta-fixtures");
            optaWidget = optaItem;
            element.addClass('widget-loaded');
            root.css('height', '');
        }

        //Used to update the fixtures table with our own data
        function onFixtureUpdate(data) {
            console.log("on-fixture-update opta-fixtures");

            var original = $(originalWidgetElement);
            var competition = data.filters.competition;// || optaWidget.widget.attr_original.competition;// original.attr('competition');
            var team = data.filters.team;// || optaWidget.widget.attr_original.team;
            competitionLogo = getCompetitionImage(competition);

            // get season from markup, no filters so it won't change
            var season = window.EFL.liveScoreDetails.season || data.filters.season;// || optaWidget.widget.attr_original.season;

            // just get today's date from markup, not using filters so it won't change 
            dateFrom = window.EFL.liveScoreDetails.dates[0];
            dateTo = window.EFL.liveScoreDetails.dates[1];

            matchData = data;

            season = createSeason(competition, season);

            if (typeof (competition) !== 'undefined' && competition !== '') {
                if (typeof (optaWidget) === "undefined") {

                    loadWidget(element, element.find('opta-widget'), competition, dateFrom, dateTo, dateOrder, team, season);
                }
                else {
                    reloadWidget(element, optaWidget, original, competition, dateFrom, dateTo, dateOrder, team, season)
                }
            } else {
                // when no competition is pulled down
                // clear widget markup
                element.empty();
                // and mimic no fixtures message
                loadWidget(element, element.find('opta-widget'), competition, dateFrom, dateTo, dateOrder, team, season);
                //Use the old original value if nothing set
                if (EFL && EFL.optaNoDataText) {
                    element.append('<div class="Opta"><p>' + EFL.optaNoDataText + '</p></div>');
                }
                else {
                    element.append('<div class="Opta"><p>There are currently no fixtures to display. Please check back soon.</p></div>');
                }
            }
        };

        /*Only way to change the state of the widget is to reconstruct it with new attribute options*/
        function reloadWidget(element, optaItem, widgetHtml, competitionId, pdateFrom, pdateTo, pdateOrder, teamId, season) {
            console.log("reloadWidget opta-fixtures");
            var height = root.height();
            root.css('height', height);
            optaItem.widget.destroy(true);
            element.empty();

            var newWidget = widgetHtml.clone();
            newWidget.attr('competition', competitionId);
            newWidget.attr('date_from', pdateFrom);
            newWidget.attr('date_to', pdateTo);
            newWidget.attr('team', teamId);
            newWidget.attr('order_by', pdateOrder);
            newWidget.attr('season', season);
            newWidget.attr('load', 'true');
            element.append(newWidget);
            element.show();

            if (typeof (Opta) !== 'undefined') {
                Opta.start();
            } else {
                console.log('Opta unavailable');
            }

            isUpdating = false;
            element.attr('data-initialised', 'false');
        }

        /* is run before opta widget is loaded to change configuration, once load becomes true only reload will work */
        function loadWidget(element, widgetHtml, competitionId, pdateFrom, pdateTo, pdateOrder, teamId, season) {
            console.log("loadWidget opta-fixtures");
            widgetHtml.attr('competition', competitionId);
            widgetHtml.attr('date_from', pdateFrom || '');
            widgetHtml.attr('date_to', pdateTo || '');
            widgetHtml.attr('order_by', pdateOrder);
            widgetHtml.attr('team', teamId);
            widgetHtml.attr('season', season);
            element.show();

            widgetHtml.attr('load', 'true');

            if (typeof (Opta) !== 'undefined') {
                Opta.start();
            } else {
                console.log('Opta unavailable');
            }

            isUpdating = false;
            element.attr('data-initialised', 'false');
        }

        //STRANGE BEHAVIOUR WITH OPTA - multiple competitions require seasons to be duplicated -> e.g. competition 10,7,2  requires a season value of 2016,2016,2016
        function createSeason(competition, season) {
  
            var competitionCount = 0, collatedSeason = season;

            competitionCount = competition.toString().split(',').length;

            if (typeof (competition !== 'undefined') && typeof (season !== 'undefined') && season.toString().indexOf(',') === -1 && competitionCount > 1) {
                collatedSeason = '';

                for (var i = 0; i < competitionCount; i++) {
                    collatedSeason += season + ',';
                }
                collatedSeason = collatedSeason.substring(0, collatedSeason.length - 1);
            }

            //If single competition but we are holding onto multi-seasons
            if (competitionCount <= 1 && season.toString().split(',').length > 1) {
                collatedSeason = season.toString().split(',')[0];
            }

            return collatedSeason;
        }

        function getCompetitionImage(competition) {
            var competitionId = competition;
            var competitionlogo = $.grep(window.EFL.competitionLogos, function (obj) { return obj.OptaId == competitionId; })[0];
            if (competitionlogo) {
                if (competitionlogo.ImgSrc === '') {
                    return competitionlogo.Title;
                }
                return '<img src="' + competitionlogo.ImgSrc + '" alt ="' + competitionlogo.Title + ' logo" class="competition-logo" title="' + competitionlogo.Title + '" />';
            } else {
                return competition;
            }
        }

        function isEmpty(obj) {
            for (var prop in obj) {
                if (obj.hasOwnProperty(prop))
                    return false;
            }

            return JSON.stringify(obj) === JSON.stringify({});
        }
    }

    $(settings.trigger).each(init);

})();

;
(function () {
    'using strict';

    var settings = {
        trigger: '[data-widget="latest-table"]'
    };

    function init() {
        
        var optaWidget, element, root, originalWidgetElement;
        element = $(this);

        if (element.find('[data-widget-id="latest-table"]').length > 0) {

            window.EFL.MatchCentre.registerWidget('latest-table', element, function (element) {
                element.find('.pre-load-content').removeClass('hidden');
                element.addClass('widget-loaded');
                console.log('latest-table drawn');
                var club = element.data('club-id');
                element.find('.Opta-Team-' + club + '.Opta-highlight').addClass('club');

            }, function () {
                element.addClass('widget-loaded');

                //Replace No data message
                //Check for the override
                if (EFL && EFL.optaNoDataText) {
                    setNoDataMessage(element, ".Opta.Opta-Normal.Opta-Empty .Opta-Cf p", EFL.optaNoDataText);
                }

            });
        }
    }

    function setNoDataMessage(element, selector, text) {
        if (element.length > 0) {
            var message = element.find(selector);
            if (message.length > 0) {
                message.empty().append(text);
            }
        }
    };

    $(function () {
        $(settings.trigger).each(init);
    });

})();;
(function () {

    var settings = {
        trigger: '[data-widget="league-table"]',
        no_data_message: 'There is no League Table to display based on the filters selected. Please try again.'
    };

    function init() {
        var optaWidget, element, root, originalWidgetElement;
        element = $(this);
        var scopeId = element.data('scopeId') || '';

        var optawidgetid = element.find('opta-widget').attr('data-widget-id');

        root = element;

        originalWidgetElement = element.find('opta-widget');

        if (originalWidgetElement.length > 0) {
            originalWidgetElement = originalWidgetElement[0].outerHTML;
        }

        function onDrawn(element, optaItem) {
            optaWidget = optaItem;
            root.css('height', '');

            // Modify the 'No data found' Opta message
            if (optaItem.error && optaItem.error.error && optaItem.error.error === 'FEED_LOAD') {
                var $feed_lock_message = element.find('.Opta-Error p');

                if ($feed_lock_message.length) {
                    //Replace No data message
                    //Check for the override
                    if (EFL && EFL.optaNoDataText) {
                        $feed_lock_message[0].innerHTML = EFL.optaNoDataText;
                    }
                    else {
                        //Use the old original value if nothing set
                        $feed_lock_message[0].innerHTML = settings.no_data_message;
                    }

                }
            }
        }

        function onError(element, optaItem) {
            optaWidget = optaItem;
            root.css('height', '');

            //Replace No data message
            //Check for the override
            if (EFL && EFL.optaNoDataText) {
                setNoDataMessage(element, ".Opta-Error .Opta-Cf p", EFL.optaNoDataText);
            }
            else {
                //Use the old original value if nothing set
                setNoDataMessage(element, ".Opta-Error .Opta-Cf p", settings.no_data_message);
            }

        }

        function setNoDataMessage(element, selector, text) {
            if (element.length > 0) {
                var message = element.find(selector);
                if (message.length > 0) {
                    message.empty().append(text);
                }
            }
        };

        function onFixtureUpdate(data) {
            var original = $(originalWidgetElement);
            var competition = data.filters.competition || original.attr("competition");
            var season = data.filters.season || original.attr("season");
            var team = data.filters.team || original.attr("team");
            matchData = data;

            reloadWidget(element, optaWidget, original, competition, season, team);
        }

        function getDividers(competitionId) {
            var filterData, dividers;

            filterData = window["filters_" + scopeId.replace(/-/g, '')];

            $.each(filterData, function () {
                if (this.Id === 'competition') {
                    dividers = this.Dividers[competitionId];
                }
            });

            return dividers;
        }

        function getLiveFlag(competitionId) {
            var filterData, liveTables;

            filterData = window["filters_" + scopeId.replace(/-/g, '')];

            $.each(filterData, function () {
                if (this.Id === 'competition') {
                    liveTables = this.LeagueTableLive[competitionId];
                }
            });

            return liveTables;
        }

        function reloadWidget(element, optaItem, widgetHtml, competitionId, season, team) {
            var height = root.height();
            var standings = element.find('.standings');
            root.css('min-height', height);
            //optaItem.widget.destroy(true);
            standings.empty();

            var newWidget = widgetHtml.clone();
            newWidget.attr('competition', competitionId);
            newWidget.attr('season', season);
            newWidget.attr('dividers', getDividers(competitionId));
            newWidget.attr('live', getLiveFlag(competitionId).toLowerCase());
            if (team != null) {
                newWidget.attr('team', team);
            }
            newWidget.attr('load', 'true');
            standings.append(newWidget);
            element.show();

            if (typeof (Opta) !== 'undefined') {
                Opta.start();
            } else {
                console.log('Opta unavailable');
            }
        }

        function loadWidget(element, widgetHtml, competitionId, season, team) {
            widgetHtml.attr('load', 'true');
            widgetHtml.attr('competition', competitionId);
            widgetHtml.attr('season', season);

            widgetHtml.attr('dividers', getDividers(competitionId));
            widgetHtml.attr('live', getLiveFlag(competitionId).toLowerCase());
            if (team != null) {
                widgetHtml.attr('team', team);
            }
            element.show();

            if (typeof (Opta) !== 'undefined') {
                Opta.start();
            } else {
                console.log('Opta unavailable');
            }
        }

        function onFilterChange(filters) {
            filters = filters || {};
            window.EFL.eventDispatcher.dispatch('on-fixture-update', {
                filters: filters,
                data: null
            }, scopeId);

        }
        function onRequestFilters(filters) {

            loadWidget(element, element.find('opta-widget'), filters.competition, filters.season, filters.team);
        }

        window.EFL.eventDispatcher.registerHandler('on-filters-change', onFilterChange, scopeId);
        window.EFL.eventDispatcher.registerHandler('on-fixture-update', onFixtureUpdate, scopeId);
        window.EFL.MatchCentre.registerWidget(optawidgetid, element, onDrawn, onError);

        $(function () {
            window.EFL.eventDispatcher.dispatch('request-filters', onRequestFilters, scopeId);
        });

    }

    $(function () {
        $(settings.trigger).each(init);
    });

})();;
(function() {
    'using strict';


    var settings = {
        trigger: '[data-widget="lineup"]',
        changeEvent: 'on-club-switch'
    };

    function init() {
        var element = $(this);
        var hasInitialised = false;
        var homeCrest, homeName, awayCrest, awayName, homeTab, awayTab, ogs;

        element.on(settings.changeEvent, function(event, showHome) {
            if (showHome) {
                element.removeClass('show-away');
                element.addClass('show-home');
            } else {
                element.removeClass('show-home');
                element.addClass('show-away');
            }
        });

        if (element.find('[data-widget-id="lineup"]').length > 0) {
            window.EFL.MatchCentre.registerWidget('lineup', element, function() {
                if (!hasInitialised) {
                    //Add Mobile Tab Crest menu
                    homeCrest = element.find('.Opta-Crest.Opta-Home img').first().clone();
                    homeName = element.find('.Opta-Home .Opta-TeamFormation').first().html();
                    awayCrest = element.find('.Opta-Crest.Opta-Away img').first().clone();
                    awayName = element.find('.Opta-Away .Opta-TeamFormation').first().html();

                    homeTab = element.find('.mobile-club-tabs [data-home]');
                    awayTab = element.find('.mobile-club-tabs [data-away]');

                    homeTab.find('[data-crest]').append(homeCrest);
                    homeTab.find('[data-club-name]').append(homeName);

                    awayTab.find('[data-crest]').append(awayCrest);
                    awayTab.find('[data-club-name]').append(awayName);

                    //inject (OG) after own goals
                    ogs = element.find('.Opta-Icon.Opta-IconOwn').each(function () {
                        if ($(this).next().text().indexOf('(OG)') < 0) {
                            $(this).next().prepend('(OG) ');
                        }
                    });

                    homeTab.on('click', function(event) {
                        homeTab.trigger(settings.changeEvent, true);
                    });

                    homeTab.on('keyup', function(event) {
                        if (event.which == 13 || event.keyCode == 13) {
                            homeTab.trigger(settings.changeEvent, true);
                            return false;
                        }
                    });

                    awayTab.on('click', function(event) {
                        awayTab.trigger(settings.changeEvent, false);
                    });

                    awayTab.on('keyup', function(event) {
                        if (event.which == 13 || event.keyCode == 13) {
                            awayTab.trigger(settings.changeEvent, false);
                            return false;
                        }
                    });


                }

                hasInitialised = true;
                element.addClass('widget-loaded');
                console.log('lineup drawn');

                //Replace No data message
                //Check for the override
                if (EFL && EFL.optaNoDataText) {
                    setNoDataMessage(element, ".Opta.Opta-Normal.Opta-Empty .Opta-Cf p", EFL.optaNoDataText);
                }

            }, function () {
                element.addClass('widget-loaded');
            });
        }

        function setNoDataMessage(element, selector, text) {
            if (element.length > 0) {
                var message = element.find(selector);
                if (message.length > 0) {
                    message.empty().append(text);
                }
            }
        };
    }

    $(function() {
        $(settings.trigger).each(init);
    });


})();
;

(function () {
    'using strict';

    var settings = {
        trigger: '[data-widget="lineup-block"]',
        changeEvent: 'on-club-switch'
    }


    function init() {
        var element = $(this);

        var homeTab = element.find('.home');
        var awayTab = element.find('.away');

        element.on(settings.changeEvent, function (event, showHome) {
            if (showHome) {
                element.removeClass('show-away');
                element.addClass('show-home');
            } else {
                element.removeClass('show-home');
                element.addClass('show-away');
            }
        });

        homeTab.on('click', function (event) {
            homeTab.trigger(settings.changeEvent, true);
        });

        homeTab.on('keyup', function (event) {
            if (event.which == 13 || event.keyCode == 13) {
                homeTab.trigger(settings.changeEvent, true);
                return false;
            }
        });

        awayTab.on('click', function (event) {
            awayTab.trigger(settings.changeEvent, false);
        });

        awayTab.on('keyup', function (event) {
            if (event.which == 13 || event.keyCode == 13) {
                awayTab.trigger(settings.changeEvent, false);
                return false;
            }
        });

        //Watch for manual header information
        window.EFL.eventDispatcher.registerHandler('club-commentary-update', function (data) {
            if (data.Items != null) {
                // Iterate through the data
                element.find(".decorations").empty();
                for (var i = 0; i < data.Items.length; i++) {
                    // Find existing item
                    var dataitem = data.Items[i];
                    if (typeof (dataitem) !== 'undefined' && typeof (dataitem.EventType) !== 'undefined') {
                        switch (dataitem.EventType) {
                            case 'substitution':
                                if (dataitem.SubOff) {
                                    var plyroff = element.find('[data-lineupid="' + dataitem.SubOff + '"]');
                                    if (plyroff.length > 0) {
                                        plyroff.find(".decorations").append('<span title="Substitution" class="Opta-Icon Opta-IconOff"></span><span class="Opta-Event-Text"><span class="Opta-Event-Time">' + dataitem.MatchTime + '<abbr title="Minute" class="">\'</abbr></span></span>');
                                    }
                                }
                                if (dataitem.SubOn) {
                                    var plyroff = element.find('[data-lineupid="' + dataitem.SubOn + '"]');
                                    if (plyroff.length > 0) {
                                        plyroff.find(".decorations").append('<span title="Substitution" class="Opta-Icon Opta-IconOn"></span><span class="Opta-Event-Text"><span class="Opta-Event-Time">' + dataitem.MatchTime + '<abbr title="Minute" class="">\'</abbr></span></span>');
                                    }
                                }
                                break;
                            case 'yellow card':
                                if (dataitem.Player) {
                                    var plyr = element.find('[data-lineupid="' + dataitem.Player + '"]');
                                    if (plyr.length > 0) {
                                        plyr.find(".decorations").append('<span title="Yellow card" class="Opta-Icon Opta-IconYellow"></span><span class="Opta-Event-Text"><span class="Opta-Event-Time">' + dataitem.MatchTime + '<abbr title="Minute" class="">\'</abbr></span></span>');
                                    }
                                }
                                break;
                            case 'red card':
                                if (dataitem.Player) {
                                    var plyr = element.find('[data-lineupid="' + dataitem.Player + '"]');
                                    if (plyr.length > 0) {
                                        plyr.find(".decorations").append('<span title="Red card" class="Opta-Icon Opta-IconRed"></span><span class="Opta-Event-Text"><span class="Opta-Event-Time">' + dataitem.MatchTime + '<abbr title="Minute" class="">\'</abbr></span></span>');
                                    }
                                }
                                break;
                            case 'goal':
                                if (dataitem.Player) {
                                    var plyr = element.find('[data-lineupid="' + dataitem.Player + '"]');
                                    if (plyr.length > 0) {
                                        var goalClass, goalTitle, goalText = '';
                                        // work out if the goal was an own goal
                                        if (dataitem.EventType === 'goal' && (dataitem.IsOpponent && dataitem.Player.indexOf('us_') > -1)
                                            || !dataitem.IsOpponent && dataitem.Player.indexOf('thm_') > -1) {
                                            goalClass = 'Opta-IconOwn';
                                            goalTitle = 'Own goal';
                                            goalText = '<span class="og-text"> (OG) </span>';
                                        } else {
                                            goalClass = 'Opta-IconGoal';
                                            goalTitle = 'Goal';
                                        }
                                        plyr.find(".decorations").append('<span title="' + goalTitle + '" class="Opta-Icon ' + goalClass + '"></span>' + goalText + '<span class="Opta-Event-Text"><span class="Opta-Event-Time">' + dataitem.MatchTime + '<abbr title="Minute" class="">\'</abbr></span></span>');
                                    }
                                }
                        }

                    }
                }
            }
        });
    }


    $(function () {
        $(settings.trigger).each(init);
    });
})();
   ;
/*

*/

(function ($) {
    "use strict";

    var settings = {
        trigger: '[data-widget="commentary"]',
        changeEvent: "switch-change"
    };

    function init(index, element) {
        var element = $(this); // DOM element
        var widget;
        var timeout;
        var keyMomentsCount = 0;

        window.EFL.eventDispatcher.registerHandler("on-kickoff-countdown-tick", monitorTime);

        window.EFL.MatchCentre.registerWidget('commentary', element, function (widget) {
            monitorTime();

            //mark special rows in the commentary section so we can filter them - live commentary widget
            var items = widget.find('.Opta-Events .Opta-Event.Opta-Icon');
            keyMomentsCount = items.length;
            items.each(function (index, element) {
                element = $(element);
                element.parent().addClass('special-event');

                if (element.hasClass('Opta-IconYellow')) {
                    element.parent().addClass('yellow-card');
                }

                if (element.hasClass('Opta-IconRed') || element.hasClass('Opta-IconDouble')) {
                    element.parent().addClass('red-card');
                }

                if (element.hasClass('Opta-IconOwn')) {
                    element.parent().addClass('own-goal');
                }
            });

            if (keyMomentsCount == 0) {
                element.addClass('no-special-events');
            }
            else {
                element.removeClass('no-special-events');
            }

            var dateLocal = new Date();
            var now = new Date(dateLocal.getUTCFullYear(), dateLocal.getUTCMonth(), dateLocal.getUTCDate(), dateLocal.getUTCHours() + isBSTinEffect(dateLocal), dateLocal.getUTCMinutes(), dateLocal.getUTCSeconds());

            //temp move to better place - live commentary widget
            element.find('.updated-time').html(formatAMPM(now) + ' (UK)');

            element.addClass('widget-loaded');

            console.log('commentary drawn');
        }, function () {
            element.addClass("show-coming-soon");
            element.addClass('widget-loaded');
        });


        //allow clicking entire control for toggle
        element.on(settings.changeEvent, function (event, filter) {
            if (typeof (widget) === 'undefined') {
                widget = element.find('.Opta');
            }

            if (filter) {
                widget.addClass('filtered');
                //filtered-complete set after animation to ensure that refresh in data doesn't cause a blip
                clearTimeout(timeout);
                timeout = setTimeout(function () {
                    widget.addClass('filtered-complete');
                    window.EFL.eventDispatcher.dispatch('update-adition-scroll-lock');
                }, 500);
            } else {
                clearTimeout(timeout);
                widget.removeClass('filtered-complete');
                timeout = setTimeout(function () {
                    widget.removeClass('filtered');
                    window.EFL.eventDispatcher.dispatch('update-adition-scroll-lock');
                }, 500);
            }

        });

        function monitorTime(time) {
            if (typeof (time) != "undefined") {
                var timeDifferentMs = time.kickoff - new Date();
                var timeDifferenceSeconds = timeDifferentMs / 1000;
                var timeDifferenceMins = timeDifferenceSeconds / 60;
                var timeDifference = Math.floor(timeDifferenceMins);
                if (timeDifference > 30) {
                    element.addClass("show-coming-soon");
                }
                else if (element.hasClass("show-coming-soon")) {
                    element.removeClass("show-coming-soon");
                }
            }
        }
    }



    function formatAMPM(date) {
        var hours = date.getHours();
        var minutes = date.getMinutes();
        var ampm = hours >= 12 ? 'pm' : 'am';
        hours = hours % 12;
        hours = hours ? hours : 12; // the hour '0' should be '12'
        minutes = minutes < 10 ? '0' + minutes : minutes;
        var strTime = hours + ':' + minutes + ' ' + ampm;
        return strTime;
    }


    $(function () {
        $(settings.trigger).each(init);
    });
})(jQuery);
;
(function () {
    'using strict';

    var settings = {
        trigger: '[data-widget="match-header"]',
        scrollTriggerPoint: 450
    };

    var liveBar = $('.live-bar');
    var liveBarMobile = $('.live-bar-mobile');
    var lockTriggerElement = $('.scroll-lock-trigger');


    function init() {
        var body = $('body');
        var element = $(this);
        var ms = parseInt(element.find('#ko').val());
        var kickoff = new Date(parseInt(element.find('#ko').val()));

        var optawidgetid = element.attr('widget-id');
        var timer = element.find('.timer');
        var pregame;
        var scopeId = element.data('scopeId');

        var miniHeaderHeight = 50;
        var miniHeaderHeightMobile = 40;

        var currentComponentHeight = 0;

        var kickoffTimer = new window.EFL.Timer(timer, kickoff,
            {
                scopeId: scopeId,
                onTick: onTimeTick,
                onComplete: onTimerComplete
            });

        //update kickoff time
        element.find('.js-kickoff').html(ensureDoubleDigits(kickoff.getUTCHours() + isBSTinEffect(kickoff)) + ":" + ensureDoubleDigits(kickoff.getUTCMinutes()) + ' (UK)');
        element.find('.js-kickoffsbst').html(ensureDoubleDigits(kickoff.getUTCHours()) + ":" + ensureDoubleDigits(kickoff.getUTCMinutes()) + ' (UK)');

        if (pregame = dateComparison(kickoff, new Date()) === 1 ) {
            element.removeClass('post-game');
            element.addClass('pre-game');
            element.find('.attendance-wrapper .sr-only, .referee-wrapper .sr-only').attr('aria-hidden', 'true');
        }
        else {
            element.removeClass('pre-game');
            element.addClass('post-game');
            element.find('.attendance-wrapper .sr-only, .referee-wrapper .sr-only').attr('aria-hidden', 'false');
        }

        if ($('.widget-match-header-mini').length != 0) {
            //mini hero activation
            $(window).scroll(function () {
                var width = window.innerWidth
                || document.documentElement.clientWidth
                || document.body.clientWidth;

                if (width >= 768) {
                    settings.scrollTriggerPoint = lockTriggerElement.offset().top - miniHeaderHeight;
                }
                else {
                    settings.scrollTriggerPoint = lockTriggerElement.offset().top - miniHeaderHeightMobile;
                }

                if ($(this).scrollTop() >= settings.scrollTriggerPoint) {
                    body.addClass("sticky-hero");
                } else {
                    body.removeClass("sticky-hero");
                }
            });
        }

        if (element.find('[data-widget-id="match-header-' + optawidgetid + '"]').length > 0) {

            //register callback
            window.EFL.MatchCentre.registerWidget('match-header-' + optawidgetid, element, function (element) {

                var gameTime = element.find('.Opta-Outer.Opta-Time').text();
                var timeSponsor = $('.countdown-widget .image-container img').clone();
                var isPostponed = false;
                //check for postponement
                if (isPostponed = gameTime.toLowerCase().indexOf('pp') !== -1) {
                    element.addClass('postponed');
                } else {
                    element.removeClass('postponed');
                }

                //add time sponsor image to opta widget
                if (timeSponsor.length > 0) {
                    setTimeout(function () {
                        var wrapper = $('<span></span>');
                        wrapper.append(timeSponsor);
                        $('.Opta .game-time td').append(wrapper);
                    }, 10);
                }

                if (isPostponed) {
                    gameTime = "Match postponed";
                }


                if (typeof (gameTime) !== 'undefined') {
                    var scoreline = element.find('tr.Opta-Scoreline');
                    var gameTimeElement = $('<tr class="game-time"><td colspan="9">' + gameTime + '</td></tr>');


                    if (element.find('.game-time').length == 0) {
                        scoreline.after(gameTimeElement);
                    }
                    else {
                        gameTimeElement = element.find('.game-time td');
                        gameTimeElement.empty().append(gameTime);
                    }
                }

                var homeCrestSrc = element.find('.Opta table .Opta-Home .Opta-Image-Team img').attr('src');
                var homeScore = element.find('.Opta-Home .Opta-Team-Score').html();

                if (typeof (homeScore) != "undefined") {
                    homeScore = homeScore.trim();
                }
                var awayCrestSrc = element.find('.Opta table .Opta-Away .Opta-Image-Team img').attr('src');
                var awayScore = element.find('.Opta-Away .Opta-Team-Score').html();

                if (typeof (awayScore) != "undefined") {
                    awayScore = awayScore.trim();
                }

                

                var homeCrestElement = $('<img src="' + homeCrestSrc + '" alt="home team crest" />');
                var awayCrestElement = $('<img src="' + awayCrestSrc + '" alt="home team crest" />');

                element.find('.home-crest').empty().append(homeCrestElement);
                element.find('.away-crest').empty().append(awayCrestElement);


                //show V before we get the 0 - 0 score (after kickoff)
                if (homeScore != '' && homeScore != null && homeScore != ' ' && homeScore != '&nbsp;') {
                    element.find('[data-club-score="home"]').html(homeScore);
                    element.find('.divider').html('-');
                }
                else {
                    element.find('.divider').html('V');
                }


                element.find('[data-club-score="home"]').html(homeScore);
                element.find('[data-club-score="away"]').html(awayScore);

                getMatchData('Referee', 10);
                getMatchData('Attendance', 10);


                /* Workaround to force sub widget to load */
                if (element.parents(".doubleheader").length == 0) {
                    element.find('.Opta-Nest-Control').click().hide();
                } else {
                    element.find('.Opta-Nest-Control').hide();
                    element.find(".Opta-Nest").remove();
                }
                element.find('.Opta-Collapsed').addClass('Opta-Expanded').removeClass('Opta-Collapsed');

                //console.log('match-header drawn');
                element.addClass("widget-loaded");

                //Replace No data message
                //Check for the override
                if (EFL && EFL.optaNoDataText) {
                    setNoDataMessage(element, ".Opta.Opta-Normal.Opta-Empty .Opta-Cf p", EFL.optaNoDataText);
                }

                // 2 seconds to give inner widget significant time to load up before determining height
                setTimeout(function () {
                    //console.log('adjust height');
                    currentComponentHeight = resizeComponentHeight(element, currentComponentHeight);
                }, 2000);

                // EFLRM-495 more reliable to detect widget drawn event before resizing
                Opta.events.subscribe('widget.drawn', function (data) {
                    if (data.widget.attr.widget === 'match_summary') {
                        currentComponentHeight = resizeComponentHeight(element, currentComponentHeight);
                    }
                });

                //inject (OG) after own goals. there is a nested widget that isn't registered fully so can't hook directly into 
                //its drawn event. Therefore have done it with a timeout hack so it may not appear for all users esp if on slow connection?
                //if this is not good enough we'll have to look at widget being fully registered which may have consequences..
                setTimeout(function () {
                    var ogs = element.find('.Opta-Icon.Opta-IconOwn').each(function () {
                        if ($(this).next().children(":first").text().indexOf('(OG)') < 0) {
                            $(this).next().children(":first").append(' (OG)');
                        }
                    });
                }, 2000);

                

                adjustAnchoredPosition();
                
            });
        } else {
            // manual, so no widget
            element.addClass("widget-loaded");

            adjustAnchoredPosition();

        }

        function setNoDataMessage(element, selector, text) {
            if (element.length > 0) {
                var message = element.find(selector);
                if (message.length > 0) {
                    message.empty().append(text);
                }
            }
        };

        function adjustAnchoredPosition() {
            if (location.hash) {
                if ($('body').hasClass('sticky-hero')) {
                    // following code tries to sort anchored content being hidden under fixed header
                    var movement;
                    movement = $('.site-header').height();

                    if ($('.widget-match-header-mini').length > 0 && $('.nav-wrapper').length > 0) {
                        movement = $('.widget-match-header-mini').height();
                    }

                    // maincontent position
                    var mainContentPosition = $('#maincontent').offset();

                    scrollDistance = mainContentPosition.top - movement;

                    $('html,body').animate({
                        scrollTop: mainContentPosition.top - movement
                    }, 200);
                }
            }
        }

        ///* add match heights if double header*/
        $('.doubleheader .widget-match-header').matchHeight();
        //Get data such as venue, attendance, refereee
        function getMatchData(label, retryCount) {
            retryCount--;

            setTimeout(function () {
                var value = element.find(".Opta-Matchdata dt:contains('" + label + "')").next().html();

                if (typeof (value) !== 'undefined') {
                    element.find('[data-dynamic-value="' + label + '"]').html(value).parent().addClass('has-' + label);
                }
                else {
                    if (retryCount > 0) {
                        getMatchData(label, retryCount);
                    }
                }

            }, 500);
        }

        function onTimeTick(time) {
            timer.html(time.strftime('<span class="date-part day"><span>%D</span><span class="days-label">Days</span></span><span class="date-part divide"> : </span><span class="date-part hour"><span>%H</span><span class="hours-label">Hrs</span></span><span class="date-part divide"> : </span><span class="date-part minute"><span>%M</span><span class="minutes-label">Mins</span></span>'));
        }

        function onTimerComplete(time) {
            var body = $('body');

            element.removeClass('pre-game');
            element.addClass('post-game');

            if (!body.hasClass('latestscores-disabled')) {
                body.addClass('latestscores');
            }
            if (!body.hasClass('livecommentary-disabled')) {
                body.addClass('livecommentary');
            }
            if (!body.hasClass('lineup-disabled')) {
                body.addClass('lineup');
            }
            if (!body.hasClass('stats-disabled')) {
                body.addClass('stats');
            }
            if (!body.hasClass('latesttable-disabled')) {
                body.addClass('latesttable');
            }
        }


        $(window).on('resize', function (event) {
            currentComponentHeight = 0;
            currentComponentHeight = resizeComponentHeight(element, currentComponentHeight);
        })

    }

    //start initialisation
    $(settings.trigger).each(init);

    function ensureDoubleDigits(value) {
        if (value.toString().length < 2) {
            value = '0' + value;
        }
        return value;
    }

    function dateComparison(a, b) {
        var result = 0;

        if (a > b) {
            result = 1;
        }

        if (a < b) {
            result = -1;
        }

        return result;
    }

    function resizeComponentHeight(element, currentHeight) {
        element.css('height', '');

        if ($(document).width() > 768) {
            var componentHeight = element.outerHeight();

            if (currentHeight < componentHeight) {
                currentHeight = componentHeight;
                element.css('height', currentHeight);
            }
        }

        return currentHeight;
    }
})();
;
(function () {
    'using strict';

    var settings = {
        trigger: '[data-widget="match-header-manual"]',
        scrollTriggerPoint: 450
    };

    var liveBar = $('.live-bar');
    var liveBarMobile = $('.live-bar-mobile');
    var lockTriggerElement = $('.scroll-lock-trigger');


    function init() {
        var body = $('body');
        var element = $(this);
        var kickoff = new Date(parseInt(element.find('#ko').val()));

        var optawidgetid = element.attr('widget-id');
        var timer = element.find('.timer');
        var pregame;
        var scopeId = element.data('scopeId');

        var miniHeaderHeight = 50;
        var miniHeaderHeightMobile = 40;

        var kickoffTimer = new window.EFL.Timer(timer, kickoff,
            {
                scopeId: scopeId,
                onTick: onTimeTick,
                onComplete: onTimerComplete
            });


        //update kickoff time
        element.find('.js-kickoff').html(ensureDoubleDigits(kickoff.getUTCHours() + isBSTinEffect(kickoff)) + ":" + ensureDoubleDigits(kickoff.getUTCMinutes()) + ' (UK)');
        element.find('.js-kickoffsbst').html(ensureDoubleDigits(kickoff.getUTCHours()) + ":" + ensureDoubleDigits(kickoff.getUTCMinutes()) + ' (UK)');

        if (pregame = dateComparison(kickoff, new Date()) === 1) {
            element.removeClass('post-game');
            element.addClass('pre-game');
            element.find('.attendance-wrapper .sr-only, .referee-wrapper .sr-only').attr('aria-hidden', 'true');
        }
        else {
            element.removeClass('pre-game');
            element.addClass('post-game');
            element.find('.attendance-wrapper .sr-only, .referee-wrapper .sr-only').attr('aria-hidden', 'false');
        }

        //update manual crests
        var homeCrestSrc = element.find('.manual-header .home-crest img').attr('src');
        var awayCrestSrc = element.find('.manual-header .away-crest img').attr('src');
        var homeCrestElement = $('<img src="' + homeCrestSrc + '" alt="home team crest" />');
        var awayCrestElement = $('<img src="' + awayCrestSrc + '" alt="away team crest" />');
        element.find('.widget-match-header-mini .home-crest').empty().append(homeCrestElement);
        element.find('.widget-match-header-mini .away-crest').empty().append(awayCrestElement);

        //mini hero activation
        if ($('.widget-match-header-mini').length != 0) {
            $(window).scroll(function () {
                var width = window.innerWidth
                || document.documentElement.clientWidth
                || document.body.clientWidth;

                if (width >= 768) {
                    settings.scrollTriggerPoint = lockTriggerElement.offset().top - miniHeaderHeight;
                }
                else {
                    settings.scrollTriggerPoint = lockTriggerElement.offset().top - miniHeaderHeightMobile;
                }

                if ($(this).scrollTop() >= settings.scrollTriggerPoint) {
                    body.addClass("sticky-hero");
            } else {
                    body.removeClass("sticky-hero");
            }
            });
        }


        if (element.find('[data-widget-id="match-header-'+optawidgetid+'"]').length > 0) {
            //register callback
            window.EFL.MatchCentre.registerWidget('match-header-' + optawidgetid, element, function (element) {
                var gameTime = element.find('.Opta-Outer.Opta-Time').text();

                if (typeof (gameTime) !== 'undefined') {
                    var scoreline = element.find('tr.Opta-Scoreline');
                    var gameTimeElement = $('<tr class="game-time"><td colspan="9">' + gameTime + '</td></tr>');


                    if (element.find('.game-time').length == 0) {
                        scoreline.after(gameTimeElement);
                    }
                    else {
                        gameTimeElement = element.find('.game-time td');
                        gameTimeElement.empty().append(gameTime);
                    }
                }

                var homeCrestSrc = element.find('.Opta table .Opta-Home .Opta-Image-Team img').attr('src');
                var homeScore = element.find('.Opta-Home .Opta-Team-Score').html();

                if (typeof (homeScore) != "undefined") {
                    homeScore = homeScore.trim();
                }
                var awayCrestSrc = element.find('.Opta table .Opta-Away .Opta-Image-Team img').attr('src');
                var awayScore = element.find('.Opta-Away .Opta-Team-Score').html();

                if (typeof (awayScore) != "undefined") {
                    awayScore = awayScore.trim();
                }


                var homeCrestElement = $('<img src="' + homeCrestSrc + '" alt="home team crest" />');
                var awayCrestElement = $('<img src="' + awayCrestSrc + '" alt="home team crest" />');

                element.find('.home-crest').empty().append(homeCrestElement);
                element.find('.away-crest').empty().append(awayCrestElement);
                element.find('[data-club-score="home"]').html(homeScore);
                element.find('[data-club-score="away"]').html(awayScore);

                getMatchData('Referee', 10);
                getMatchData('Attendance', 10);


                /* Workaround to force sub widget to load */
                if (element.parents(".doubleheader").length == 0) {
                    element.find('.Opta-Nest-Control').click().hide();
                } else {
                    element.find('.Opta-Nest-Control').hide();
                    element.find(".Opta-Nest").remove();
                }
                element.find('.Opta-Collapsed').addClass('Opta-Expanded').removeClass('Opta-Collapsed');

                console.log('match-header drawn');
                element.addClass("widget-loaded");
            });
        } else {
            // manual, so no widget
            element.addClass("widget-loaded");
        }

        function adjustAnchoredPosition() {
            if (location.hash) {
                var movement;
                movement = $('.site-header').height();

                if ($('.widget-match-header-mini').length > 0 && $('.nav-wrapper').length > 0) {
                    movement = $('.widget-match-header-mini').height();
                }

                // maincontent position
                var mainContentPosition = $('#maincontent').offset();

                scrollDistance = mainContentPosition.top - movement;

                $('html,body').animate({
                    scrollTop: mainContentPosition.top - movement
                }, 200);
            }
        }

        //Get data such as venue, attendance, refereee
        function getMatchData(label, retryCount) {
            retryCount--;

            setTimeout(function () {
                var value = element.find(".Opta-Matchdata dt:contains('" + label + "')").next().html();

                if (typeof (value) !== 'undefined') {
                    element.find('[data-dynamic-value="' + label + '"]').html(value).parent().addClass('has-' + label);
                }
                else {
                    if (retryCount > 0) {
                        getMatchData(label, retryCount);
                    }
                }

            }, 500);
        }

        function onTimeTick(time) {
            timer.html(time.strftime('<span class="date-part day"><span>%D</span><span class="days-label">Days</span></span><span class="date-part divide"> : </span><span class="date-part hour"><span>%H</span><span class="hours-label">Hrs</span></span><span class="date-part divide"> : </span><span class="date-part minute"><span>%M</span><span class="minutes-label">Mins</span></span>'));
        }

        function onTimerComplete(time) {
            element.removeClass('pre-game');
            element.addClass('post-game');

        }

        //Watch for manual header information
        var once = false;
        var previousData = {};
        window.EFL.eventDispatcher.registerHandler('club-commentary-update', function (data) {

            if (!deepCompare(data,previousData)) {
                if (data.Header != null) {
                    //{"ContentId":0,"MatchStatus":"Postponed","HomeScore":0,"AwayScore":0,"HomePenalties":0,"AwayPenalties":0}

                    if (typeof (data.Header.HomeScore) !== 'undefined') {
                        element.find(".score-container .home-score").text(data.Header.HomeScore);
                    }
                    if (typeof (data.Header.AwayScore) !== 'undefined') {
                        element.find(".score-container .away-score").text(data.Header.AwayScore);
                    }

                    element.find(".score-container .status").text(data.Header.MatchStatus);
                    if ((data.Header.HomePenalties + data.Header.AwayPenalties) > 0) {
                        element.find(".score-container .penalties").text("Pens(" + data.Header.HomePenalties + " - " + data.Header.AwayPenalties + ")").css("display", "block");
                    }
                    if (data.Header.Attendance != 0) {
                        element.find(".attendance-wrapper .people").text(data.Header.Attendance).parent().children().show();
                    }
                    if (data.Header.Referee != 0) {
                        element.find(".referee-wrapper .referee").text(data.Header.Referee).parent().children().show();
                    }
                }
                var isaway = $("body").is(".awaymatch");
                var homedecos = element.find(".homedecorations");
                var awaydecos = element.find(".awaydecorations");
                homedecos.empty();
                awaydecos.empty();
                if (data.Items && data.Items !== null) {
                    for (var i = 0; i < data.Items.length; i++) {
                        // Find existing item
                        var dataitem = data.Items[i];
                        if (typeof (dataitem) !== 'undefined' && typeof (dataitem.EventType) !== 'undefined') {
                            var target = isaway ? (dataitem.IsOpponent ? homedecos : awaydecos) : (dataitem.IsOpponent ? awaydecos : homedecos);
                            var namefirst = isaway ? dataitem.IsOpponent : !dataitem.IsOpponent;
                            var markupIcon = '';
                            var markupTime = '';
                            var markup = '';
                            var plyrname = $('[data-lineupid="' + dataitem.Player + '"] .name .n').text();
                            var ownGoal = false;
                            var ogText = '';

                            switch (dataitem.EventType) {
                                case 'substitution':
                                    plyrname = '';
                                    markupIcon = '<span title="Substitution" class="Opta-Icon Opta-IconSubstitution"></span>';
                                    markupTime = '<span class="Opta-Event-Text"><span class="Opta-Event-Time">' + dataitem.MatchTime + '<abbr title="Minute" class="">\'</abbr></span></span>';
                                    if (dataitem.SubOff) {
                                        plyrname = $('[data-lineupid="' + dataitem.SubOff + '"] .name .n').text();
                                    }
                                    if (dataitem.SubOn) {
                                        plyrname = (plyrname != '' ? plyrname + ' - ' : '') + $('[data-lineupid="' + dataitem.SubOn + '"] .name .n').text();
                                    }
                                    break;
                                    //case 'yellow card':
                                    //    markup = '<span title="Yellow card" class="Opta-Icon Opta-IconYellow"></span><span class="Opta-Event-Text"><span class="Opta-Event-Time">' + dataitem.MatchTime + '<abbr title="Minute" class="">\'</abbr></span></span>';
                                    //    break;
                                case 'red card':
                                    markupIcon = '<span title="Red card" class="Opta-Icon Opta-IconRed"></span>';
                                    markupTime = '<span class="Opta-Event-Text"><span class="Opta-Event-Time">' + dataitem.MatchTime + '<abbr title="Minute" class="">\'</abbr></span></span>';
                                    break;
                                case 'goal':
                                    // work out if the goal was an own goal
                                    if (dataitem.EventType === 'goal' && (dataitem.IsOpponent && dataitem.Player.indexOf('us_') > -1)
                                        || !dataitem.IsOpponent && dataitem.Player.indexOf('thm_') > -1) {
                                        ownGoal = true;
                                        markupIcon = '<span title="Own goal" class="Opta-Icon Opta-IconOwn"></span>';
                                    } else {
                                        markupIcon = '<span title="Goal" class="Opta-Icon Opta-IconGoal"></span>';
                                    }
                                    
                                    markupTime = '<span class="Opta-Event-Text"><span class="Opta-Event-Time">' + dataitem.MatchTime + '<abbr title="Minute" class="">\'</abbr></span></span>';
                                    break;
                            }

                            if (markupIcon != '' && markupTime != '') {
                                if (ownGoal) {
                                    ogText = ' (OG)';
                                }
                                plyrname = '<span class="player-name">' + plyrname + ogText + '</span>';

                                if (namefirst) {
                                    markup = markupTime + plyrname + markupIcon;
                                } else {
                                    markup = markupIcon + plyrname + markupTime;
                                }

                                markup = '<div class="decoration event-type-' + dataitem.EventType.replace(' ','_') + '">' + markup + '</div>';
                                target.append(markup);
                            }
                        }
                    }
                }
           }
            once = true;
            previousData = data;
        });

        adjustAnchoredPosition();
    }

    //start initialisation
    $(settings.trigger).each(init);

    function ensureDoubleDigits(value) {
        if (value.toString().length < 2) {
            value = '0' + value;
        }
        return value;
    }

    function dateComparison(a, b) {
        var result = 0;

        if (a > b) {
            result = 1;
        }

        if (a < b) {
            result = -1;
        }

        return result;
    }

    function deepCompare() {
        var i, l, leftChain, rightChain;

        function compare2Objects(x, y) {
            var p;

            // remember that NaN === NaN returns false
            // and isNaN(undefined) returns true
            if (isNaN(x) && isNaN(y) && typeof x === 'number' && typeof y === 'number') {
                return true;
            }

            // Compare primitives and functions.     
            // Check if both arguments link to the same object.
            // Especially useful on the step where we compare prototypes
            if (x === y) {
                return true;
            }

            // Works in case when functions are created in constructor.
            // Comparing dates is a common scenario. Another built-ins?
            // We can even handle functions passed across iframes
            if ((typeof x === 'function' && typeof y === 'function') ||
               (x instanceof Date && y instanceof Date) ||
               (x instanceof RegExp && y instanceof RegExp) ||
               (x instanceof String && y instanceof String) ||
               (x instanceof Number && y instanceof Number)) {
                return x.toString() === y.toString();
            }

            // At last checking prototypes as good as we can
            if (!(x instanceof Object && y instanceof Object)) {
                return false;
            }

            if (x.isPrototypeOf(y) || y.isPrototypeOf(x)) {
                return false;
            }

            if (x.constructor !== y.constructor) {
                return false;
            }

            if (x.prototype !== y.prototype) {
                return false;
            }

            // Check for infinitive linking loops
            if (leftChain.indexOf(x) > -1 || rightChain.indexOf(y) > -1) {
                return false;
            }

            // Quick checking of one object being a subset of another.
            // todo: cache the structure of arguments[0] for performance
            for (p in y) {
                if (y.hasOwnProperty(p) !== x.hasOwnProperty(p)) {
                    return false;
                }
                else if (typeof y[p] !== typeof x[p]) {
                    return false;
                }
            }

            for (p in x) {
                if (y.hasOwnProperty(p) !== x.hasOwnProperty(p)) {
                    return false;
                }
                else if (typeof y[p] !== typeof x[p]) {
                    return false;
                }

                switch (typeof (x[p])) {
                    case 'object':
                    case 'function':

                        leftChain.push(x);
                        rightChain.push(y);

                        if (!compare2Objects(x[p], y[p])) {
                            return false;
                        }

                        leftChain.pop();
                        rightChain.pop();
                        break;

                    default:
                        if (x[p] !== y[p]) {
                            return false;
                        }
                        break;
                }
            }

            return true;
        }

        if (arguments.length < 1) {
            return true; //Die silently? Don't know how to handle such case, please help...
            // throw "Need two or more arguments to compare";
        }

        for (i = 1, l = arguments.length; i < l; i++) {

            leftChain = []; //Todo: this can be cached
            rightChain = [];

            if (!compare2Objects(arguments[0], arguments[i])) {
                return false;
            }
        }

        return true;
    }
})();
;
(function() {
  'using strict';

  var settings = {
    trigger: '[data-widget="match-stats"]',
  };

  //season player stats menu - position scroll in correct position on mobile
  function setNavMenuScroll(element) {
      var hasTabs;
    var widgetNav = element.find('.Opta-Nav');
    var widgetActiveTab = element.find('.Opta-Tabs .Opta-On').first();
    hasTabs = widgetActiveTab.length > 0;
    var halfScreenWidth = $(window).width() / 2;

    if (hasTabs) {
        var tabOffset = widgetActiveTab.position().left;
        var tabHalfWidth = widgetActiveTab.width() / 2;
        widgetNav.scrollLeft(tabOffset + tabHalfWidth - halfScreenWidth);

    }
  }


  function init() {
      var element = $(this);
      var isInitialised = false;
    if (element.find('[data-widget-id="match-stats"]').length > 0) {
      window.EFL.MatchCentre.registerWidget('match-stats', element, function(element) {

        setNavMenuScroll(element);
        element.find('.Opta-Tabs').first().find('li').off('click').on('click', function() {
          setNavMenuScroll(element);
        });

        if (!isInitialised) {
            var tab = $('.Opta-Tabs li.Opta-On a');
            var title = $(tab).text();

        /*    if (typeof (title) !== 'undefined') {
                window.EFL.analyticsController.pageView(window.location.pathname + 'match-stats/' + title.toLowerCase(),  'match-stats ' + title.toLowerCase());
            }*/

            element.find('.Opta-Tabs li a').off('click', trackTab).on('click', trackTab);
        }


        isInitialised = true;
        element.addClass('widget-loaded');
        console.log('match-stats drawn');

        //Replace No data message
        //Check for the override
        if (EFL && EFL.optaNoDataText) {
            setNoDataMessage(element, ".Opta.Opta-Normal.Opta-Empty .Opta-Cf p", EFL.optaNoDataText);
        }


      }, function () {
          element.addClass('widget-loaded');
      });
    }


  }

  function setNoDataMessage(element, selector, text) {
      if (element.length > 0) {
          var message = element.find(selector);
          if (message.length > 0) {
              message.empty().append(text);
          }
      }
  };

  function trackTab(event) {
      try{
          var element = $(this);

          var title = $(element).text();

          if (typeof (title) !== 'undefined') {
              window.EFL.analyticsController.pageView(window.location.pathname + 'match-stats/' + title.toLowerCase(), 'match-stats ' + title.toLowerCase());
          }
      }
      catch (e) {

      }
  }

  $(function() {
    $(settings.trigger).each(init);
  });

})();
;
/*

JSON STRUCTURE


var filters = [
{
  name: 'team',
  options:[
    { label: "label A", value: "value A", selected:"true" },
    { label: "label B", value: "value B", selected:"false" },
    { label: "label C", value: "value C", selected:"false" }
  ]
},
{
name: 'season',
options:[
    { label: "label D", value: "value D", selected:"false" },
    { label: "label E", value: "value E", selected:"true" },
    { label: "label F", value: "value F", selected:"false" }
  ]
},

{
  name: 'competition',
  options:[
    { label: "label G", value: "value G", selected:"false" },
    { label: "label H", value: "value H", selected:"false" },
    { label: "label I", value: "value I", selected:"true" }
  ]
}

];


The job of the filter builder is literally only creating the filters


*/


var FilterBuilder = (function () {
    return function (element) {

        var properties = {
            element: element,
            scopeId: ''
            //filterSection
            //itemTemplate
            //componentTemplate
            //component,


        };

        var methods = {
            init: function () {
                properties.scopeId = element.getAttribute('data-scope-id').replace(/-/g, '');
                fct = document.getElementById('filter-component-template_' + properties.scopeId);
                //EFLRM-235 - this protects against an editor pasting the raw html of a filter into a wysiwyg and it causing the issue
                //it did on 28/10/2017
                if (fct == null)
                {
                    return;
                    //give up
                }
                properties.componentTemplate = document.getElementById('filter-component-template_' + properties.scopeId).innerHTML;
                properties.component = document.createElement('div');
                properties.component.innerHTML = properties.componentTemplate;

                properties.itemTemplate = document.getElementById('filter-item-template_' + properties.scopeId).innerHTML;
                properties.filterSection = properties.component.querySelectorAll('.panel-group');

                if (properties.filterSection.length > 0) {
                    properties.filterSection = properties.filterSection[0];
                }

            },
            create: function (filtersData) {
                var filters = '';

                for (var i = 0; i < filtersData.length; i++) {
                    filters += methods.buildFilter(filtersData[i]);
                }

                //EFLRM-235 - this protects against an editor pasting the raw html of a filter into a wysiwyg and it causing the issue
                //it did on 28/10/2017
                if (properties.filterSection == null) {
                    return;
                    //give up
                }

                properties.filterSection.innerHTML = filters;
                properties.element.innerHTML = properties.component.innerHTML
            },
            buildFilter: function (filterData) {
                var selectList;
                var filterHeading;
                var filterHeader;
                var overlayHeading;
                var overlay;

                if (typeof filterData.name === 'undefined')
                {
                    return;
                }

                //create markup from template
                var itemElement = document.createElement('div');
                itemElement.innerHTML = properties.itemTemplate;
                itemElement.children[0].setAttribute('data-filter-name', filterData.name.toLowerCase());
                itemElement.children[0].setAttribute('id', filterData.name.toLowerCase() + '-filter_' + properties.scopeId);

                //set headings
                filterHeader = itemElement.querySelectorAll('.header')[0];
                filterHeader.setAttribute('data-target', '#' + filterData.name.toLowerCase() + '-overlay_' + properties.scopeId);

                filterHeading = itemElement.querySelectorAll('.header h4')[0];
                filterHeading.innerHTML = filterData.name;


                overlay = itemElement.querySelectorAll('.filter-overlay')[0];
                overlay.setAttribute('id', filterData.name.toLowerCase() + '-overlay_' + properties.scopeId);
                overlayHeading = itemElement.querySelectorAll('.filter-overlay h4')[0];
                overlayHeading.innerHTML = filterData.name;

                selectedItemHeading = itemElement.querySelectorAll('.filter-selected-value h3')[0];

                selectList = itemElement.querySelector('ul');

                //attach count of options to filter - used to hide filters with 1 option in css (optional)
                itemElement.firstElementChild.setAttribute('data-option-count', filterData.options.length);
                //attach isHidden property from json to filter - used to hide filter regardless of the count above
                itemElement.firstElementChild.setAttribute('data-filter-hidden', filterData.isHidden);

                //populate options
                methods.buildFilterOptions(filterData, selectList, selectedItemHeading);

                return itemElement.innerHTML;
            },
            buildFilterOptions: function (filterData, selectList, selectedItemHeading) {
                var list = '';

                for (var i = 0; i < filterData.options.length; i++) {
                    filter = filterData.options[i];

                    //Cameron:Only add the filter if there is a label.
                    if (filter.label != "") {

                        list += '<li> <a href="#" data-value="' + filter.value + '" data-selected="' + filter.selected + '" '

                        if (typeof filter.showSubgroup != 'undefined') {
                            list += 'data-showSubgroup="' + filter.showSubgroup + '"';
                        }

                        list += '>' + filter.label + '</a></li>';

                        if (filter.selected || filter.selected === 'true') {
                            selectedItemHeading.innerHTML = filter.label;
                        }
                    }

                }
                selectList.innerHTML = list;

                Ps.initialize(selectList, {
                    scrollYMarginOffset: 5,
                });
            },

            update: function (filtersData) {
                var filter, selectList, selectedItemHeading;

                for (var i = 0; i < filtersData.length; i++) {
                    filter = properties.element.querySelector('[data-filter-name="' + filtersData[i].name.toLowerCase() + '"]');
                    selectList = filter.querySelector('ul');
                    selectedItemHeading = filter.querySelector('.filter-selected-value h3');

                    methods.clearFilterOptions(selectList);
                    methods.buildFilterOptions(filtersData[i], selectList, selectedItemHeading)
                }
            },

            clearFilterOptions: function (selectList) {
                selectList.innerHTML = null;
            }



        };

        methods.init();

        return {
            create: methods.create,
            update: methods.update
        };
    };
})();
;
/*

filter.js is responsible for collecting a selection of filters and sending an event out to observers.

*/

var Filter = (function() {

  return function(name, element) {

    var properties = {
      element: element,
      filter: {},
      name: name,
      elements: element.querySelectorAll('.filter-dropdown'),
      selectedValue: '',
      selectedLabel: '',
      selectedHandlers: [],
      onOverlayOpenCallback: []
    };

    var methods = {
      init: function() {
        //init
        properties.name = name;
        properties.filter = methods.initFilter(properties.element);
        //EFLRM-235 - this protects against an editor pasting the raw html of a filter into a wysiwyg and it causing the issue
        //it did on 28/10/2017
        if (typeof filter === "undefined") {
            return;
            //give up
        }
        methods.bindEvents(filter);

        //select defaults
        var selectedElement = element.querySelectorAll('[data-selected="true"]');

        if (selectedElement.length > 0) {
          methods.selectOption(selectedElement[0]);
        }
      },
      initFilter: function(element) {
        var filter = {};

        filter.element = element;
        filter.id = filter.element.getAttribute('id');
        filter.header = filter.element.querySelectorAll('.header')[0];
        filter.overlay = filter.element.querySelectorAll('.filter-overlay')[0];
        filter.selections = typeof(filter.overlay) != 'undefined' ? filter.overlay.querySelectorAll('li') : '';
        filter.numberOfResults = filter.selections.length;
        filter.selectedValueElement = filter.element.querySelectorAll('[data-selected-value]');

        return filter;
      },
      openOverlay: function (event) {
          event.preventDefault();
        var element = properties.filter.element;

        for (var i = 0; i < properties.onOverlayOpenCallback.length; i++) {
          if (typeof(properties.onOverlayOpenCallback[i]) === 'function') {
            properties.onOverlayOpenCallback[i]();
          }
        }

        if (element.classList.contains('active')) {
            element.classList.remove('active');
        } else {
            element.classList.add('active');
        }

        element.querySelector('.header').setAttribute('aria-expanded', 'true');
        element.querySelector('.filter-overlay').setAttribute('aria-expanded', 'true');
      },
      onSelection: function (event) {
          event.preventDefault();
        var selectedItem;

        if (event.target.getAttribute('data-value') === null) {
          var selectedItems = event.target.querySelectorAll('[data-value]');

          if (selectedItems.length > 0) {
            selectedItem = selectedItems[0];
          }
        } else {
          selectedItem = event.target;
        }

        methods.clearSelections();
        methods.selectOption(selectedItem);
      },
      clearSelections: function() {
        for (var i = 0; i < properties.elements.length; i++) {
          properties.elements[i].setAttribute('data-selected', 'false');
        }
      },
      selectOption: function(item) {
        item.setAttribute('data-selected', 'true');
        properties.selectedValue = item.getAttribute('data-value');
        properties.selectedLabel = item.innerHTML;

        var header = properties.filter.header.querySelectorAll('[data-selected-value]');

        for (var j = 0; j < header.length; j++) {
          header[j].innerHTML = properties.selectedLabel;
        }

        for (var i = 0; i < properties.selectedHandlers.length; i++) {
          if (typeof(properties.selectedHandlers[i]) === 'function') {
            properties.selectedHandlers[i](properties.selectedValue);
          }
        }
      },
      closeOverlay: function() {
          element.classList.remove('active');
          //EFLRM-235 - this protects against an editor pasting the raw html of a filter into a wysiwyg and it causing the issue
          //it did on 28/10/2017
          if (element.querySelector('.header') == null) {
              return;
              //give up
          }
          element.querySelector('.header').setAttribute('aria-expanded', 'false');
          element.querySelector('.filter-overlay').setAttribute('aria-expanded', 'false');
      },
      bindEvents: function (filter) {
          //EFLRM-235 - this protects against an editor pasting the raw html of a filter into a wysiwyg and it causing the issue
          //it did on 28/10/2017
          if (typeof properties.filter.header === "undefined") {
              return;
              //give up
          }
          properties.filter.header.addEventListener('click', methods.openOverlay);

          for (var i = 0; i < properties.filter.selections.length; i++) {
              properties.filter.selections[i].addEventListener('click', methods.onSelection);

          }
      },
      unbindEvents: function(filter) {
        properties.filter.header.removeEventListener('click', methods.openOverlay);

        for (var i = 0; i < properties.filter.selections.length; i++) {
          properties.filter.selections[i].removeEventListener('click', methods.onSelection);

        }
      },
      update: function(element){
        properties.element = element;
        methods.unbindEvents(properties.filter);
        methods.init();
      },
      clear: function(){
          properties.selectedHandlers = [];
          methods.unbindEvents(properties.filter);
      },
      destroy: function(){
        methods.clear();
        methods.unbindEvents(properties.filter);
      //  methods = null;
      //  properties = null
      }
    };



    methods.init();


    return {
        get: function () {
            return {
                name: properties.name,
                selectedValue: properties.selectedValue
            };
        },
      registerSelectedHandler: function(callback) {
        properties.selectedHandlers.push(callback);
      },
      onOverlayOpen: function(callback) {
        properties.onOverlayOpenCallback.push(callback);
      },
      close: methods.closeOverlay,
      update: methods.update,
      clear: methods.clear,
      destroy:methods.destroy
    }

  }

})();
;
/*

The purpose of FilterGroup is to group together individual Filter objects and report filter selections as a group.

*/

var FilterGroup = (function () {

    return function (element) {

        var properties = {
            filters: [],
            onSelectionCallback: []

        };

        var methods = {
            collectFilters: function () {
                var filters = {};
                var filter;
                for (var i = 0; i < properties.filters.length; i++) {
                    filter = properties.filters[i].get();
                    filters[filter.name] = filter.selectedValue;
                }
                return filters;
            },
            onNewSelection: function (value) {
                for (var i = 0; i < properties.onSelectionCallback.length; i++) {
                    properties.onSelectionCallback[i](methods.collectFilters());
                }

                methods.closeGroup();
            },
            closeGroup: function () {
                for (var i = 0; i < properties.filters.length; i++) {
                    properties.filters[i].close();
                }
            },
            closeOnBodyClick: function (event) {
                if (methods.findParentBySelector(event.target, '.filter-dropdown') === null) {
                    methods.closeGroup();
                }
            },
            collectionHas: function (a, b) { //helper function (see below)
                for (var i = 0, len = a.length; i < len; i++) {
                    if (a[i] == b) return true;
                }
                return false;
            },
            findParentBySelector: function (elm, selector) {
                var all = document.querySelectorAll(selector);
                var cur = elm.parentNode;
                while (cur && !methods.collectionHas(all, cur)) { //keep going up until you find a match
                    cur = cur.parentNode; //go up
                }
                return cur; //will return null if not found
            },
            registerFilter: function (filter) {
                filter.registerSelectedHandler(methods.onNewSelection);
                filter.onOverlayOpen(methods.closeGroup);
                properties.filters.push(filter);
            },
            registerOnSelectionCallback: function (callback) {
                properties.onSelectionCallback.push(callback);
            },
            bindEvents: function () {
                document.body.addEventListener('click', methods.closeOnBodyClick);
            },
            unbindEvents: function () {
                document.body.removeEventListener('click', methods.closeOnBodyClick);
            },
            clear: function () {
                properties.onSelectionCallback = [];

                for (var i = 0; i < properties.filters.length; i++) {
                    properties.filters[i].destroy();
                }

                properties.onSelectionCallback = [];
                properties.filters = [];
            },
            destroy: function () {
                for (var i = 0; i < properties.filters.length; i++) {
                    properties.filters[i].destroy();
                }

                properties.onSelectionCallback = null;
                properties = null;
                methods = null;
            }

        };

        //init
        methods.bindEvents();


        return {
            get: methods.collectFilters,
            register: methods.registerFilter,
            onSelection: methods.registerOnSelectionCallback,
            clear: methods.clear,
            destroy: methods.destroy
        }

    }

})();
;
/* Data Structure */

/*

All changed, just look at the page

*/


function FixtureFilterDataProvider(data) {
    var _data = {};
    var _dependencies = {};
    this.load = function (newData) {
        _data = newData;
    }
    this.loadDependencies = function (newDependencies) {
        _dependencies = newDependencies;
    }
    this.get = function () {
        return _data;
    }
    this.getDependencies = function () {
        return dependencies;
    }

  this.load(data);

  var methods = {
    createFilterData: function(data, filters) {
      var filterData = {},
        option = {};
      var filteredValues = [];
      var doFilter;
      // this will be true when no data is available to pull down
      // the flag is used to update the competition filter with just "All competitions"
      data.noData = false;

      if (data.Id === 'competition' && (data.DependentValues != null && !$.isEmptyObject(data.DependentValues))) {
          filteredValues = methods.createFilteredValues(filters, data);
      }

      filterData = methods.createFilterOptions(data, filterData);

      //if there was a filter provided check if that exists and if so make that the selected option
      filterData = methods.selectFilteredOptionByDefault(filters, data, filterData);

      //if there is no selected filter then the top will be selected by default
      filterData = methods.checkDefaultValue(filterData);

      return filterData;

    },
    clearSelections: function(filterData) {
      for (var i = 0; i < filterData.options.length; i++) {
        filterData.options[i].selected = 'false';
      }

      return filterData;
    },
    createFilterOptions: function(data, filterData) {
      var filterData = {};
      var allOptions = [];

      filterData.name = data.Title;
      //state whether filter should be hidden based on json property, this flag is used in filter-builder.js
      filterData.isHidden = data.IsHidden;
        filterData.options = [];
        if (data.FilterList != null) {
            for (var i = 0; i < data.FilterList.length; i++) {
                var property = data.FilterList[i];
                option = {};
                option.value = property.Key;
                option.label = property.Value;
                option.selected = (option.value == data.DefaultId).toString();
                option.showSubgroup = property.ShowSubgroup;

                if (data.Id === 'competition' && (data.DependentValues != null && !$.isEmptyObject(data.DependentValues))) {

                    if (typeof (filteredValues) !== 'undefined' && filteredValues.indexOf(option.value) !== -1 && !data.noData) {
                        filterData.options.push(option);
                        allOptions.push(property.Key);
                    }
                } else {

                    filterData.options.push(option);
                    allOptions.push(property.Key);
                }
            }
        }

        else if (typeof data.Values != 'undefined') {
          for (var i = 0; i < data.Values.length; i++) {
              var property = data.Values[i];
              option = {};
              option.value = property.Key;
              option.label = property.Value;
              option.selected = (option.value == data.DefaultId).toString();

              if (data.Id === 'competition' && (data.DependentValues != null && !$.isEmptyObject(data.DependentValues))) {

                  if (typeof (filteredValues) !== 'undefined' && filteredValues.indexOf(option.value) !== -1 && !data.noData) {
                      filterData.options.push(option);
                      allOptions.push(property.Key);
                  }
              } else {

                  filterData.options.push(option);
                  allOptions.push(property.Key);
              }
          }
      }

      //if there is an all items title, add all items option to start to filter
      if (data.AllItemsTitle) {
        option = {};
        option.value = allOptions.join(',');
        option.label = data.AllItemsTitle;

        filterData.options.unshift(option);
      }

      return filterData;
    },
    selectFilteredOptionByDefault: function(filters, data, filterData){
      if (typeof(filters) !== 'undefined') {
        var filter = methods.getFilterByName(data.Id, filters);
        for (var i = 0; i < filterData.options.length; i++) {
          if (filterData.options[i].value === filters[filterData.name.toLowerCase()]) {
            methods.clearSelections(filterData);
            filterData.options[i].selected = 'true';
          }
        }
      }
      return filterData;
    },
    getDefaultFilters: function(data) {
        var filters = {};
        for (var i = 0; i < data.length; i++) {
            if (typeof data[i].Id != 'undefined') {
                filters[data[i].Id.toLowerCase()] = data[i].DefaultId;
            }
        }
      return filters;
    },
    getFilterByName: function(name, filters) {
      var i = 0;
      var found = false;
      var filter;
      while (!found && i < filters.length) {

        if (filters[i].name === name) {
          filter = filters[i];
          found = true;
        }
        i++;
      }

      return filter;
    },
    createFilteredValues: function (filters, data) {
      var key = filters["season"] + '-' + filters["team"];
      var dependantValue = data.DependentValues[key];

      // a single team has been selected but no data is being returned
      // therefore set some flags which are used for the "all competitions" option
      if (filters['team'].split(',').length <= 1 && typeof (dependantValue) === 'undefined') {
          data.noData = true;
      }

      // when all teams are selected
      if (typeof(filters['team']) !== 'undefined' && filters['team'].split(',').length > 1) {
          filteredValues = data['AllIds'].split(',');
      } else if (typeof (dependantValue) !== 'undefined') {
          filteredValues = dependantValue.split(',');
      }
    },
    checkDefaultValue: function(filterData) {
      var hasDefault = false;
      var i = 0;
      while (!hasDefault && i < filterData.options.length) {
        hasDefault = filterData.options[i].selected === 'true';
        i++;
      }

      if (!hasDefault && filterData.options.length > 0) {
        // if no default value was found then just make the first value default.
        filterData.options[0].selected = 'true';
      }

      return filterData;
    }
  };

  this.build = function(filters) {
    var data = this.get();
    var trimmedData = [];
    var filterData = {};

    if (typeof(filters) === 'undefined') {
      filters = methods.getDefaultFilters(data);
    }

    for (var i = 0; i < data.length; i++) {
      trimmedData.push(methods.createFilterData(data[i], filters));
    }

    return trimmedData;
  }

}

;
/* filter component acts as a bridge between HTML and JS and is used to hook up and initialize filters */

/*

filter-component.js is responsible for collecting a selection of filters and sending an event out to observers.

*/


(function (eventDispatcher) {

    var settings = {
        trigger: '[data-component="filter"]'
    };




    var methods = {
        init: function () {
            var element = this;
            var scopeId = element.getAttribute('data-scope-id');
            var filtersData = window["filters_" + scopeId.replace(/-/g,'')];

            if (typeof (filtersData) !== 'undefined') {
                //create json data representing filters
                var dataProvider = new FixtureFilterDataProvider(filtersData);
                var data = dataProvider.build();
                

                //build filter drop downs
                var filterBuilder = new FilterBuilder(element);
                filterBuilder.create(data);

                //group them together for multi-filter functionality
                var filterGroup = methods.createFilterGroup(element, dataProvider, filterBuilder);

                $(element).find('[data-toggle][data-target]').off('click', methods.toggleMobileAccordion).on('click', methods.toggleMobileAccordion);

                //handle adhoc requests for filters
                eventDispatcher.registerHandler('request-filters', function (callback) {
                    callback(filterGroup.get());
                }, scopeId);

            }
            else {
                element.innerHTML = "filter data missing";
            }

        },

        onSelection: function (filters, element, dataProvider, filterBuilder, filterGroup) {
            var scopeId = element.getAttribute('data-scope-id');
            eventDispatcher.dispatch('on-filter-change', filters, scopeId);

            //close drop downs on mobile

            filterGroup.clear();

            var data = dataProvider.build(filters);
            filterBuilder.update(data);



            var filterElements = element.querySelectorAll('.filter-dropdown');

            for (var i = 0; i < filterElements.length; i++) {
                filterGroup.register(new Filter(filterElements[i].getAttribute('data-filter-name'), filterElements[i]));
            }

            //register callback to catch filter changes
            filterGroup.onSelection(function (filters) {
                methods.onSelection(filters, element, dataProvider, filterBuilder, filterGroup)
            });

            $(element).find('[data-toggle][data-target]').off('click', methods.toggleMobileAccordion).on('click', methods.toggleMobileAccordion);

            //send out default selections
            eventDispatcher.dispatch('on-filters-change', filterGroup.get(), scopeId);

        },
        createFilterGroup: function (element, dataProvider, filterBuilder) {
            var filters = [];
            var filterGroup = new FilterGroup(element);
            var filterElements = element.querySelectorAll('.filter-dropdown');
            var scopeId = element.getAttribute('data-scope-id');

            for (var i = 0; i < filterElements.length; i++) {
                filterGroup.register(new Filter(filterElements[i].getAttribute('data-filter-name'), filterElements[i]));
            }

            //register callback to catch filter changes
            filterGroup.onSelection(function (filters) {
                methods.onSelection(filters, element, dataProvider, filterBuilder, filterGroup)
            });

            //send out default selections
            eventDispatcher.dispatch('on-filters-change', filterGroup.get(), scopeId);

            return filterGroup;
        },
        toggleMobileAccordion: function () {
            var target = $(this).attr('data-target');
            $(target).collapse("toggle");
        }
    };

    var components = document.querySelectorAll(settings.trigger);

    for (var i = 0; i < components.length; i++) {
        methods.init.call(components[i]);
    }

})(window.EFL.eventDispatcher);
;
(function ($) {
    'use strict';

    var settings = {
        trigger: '[data-widget="commentary"]',
        changeEvent: 'switch-change',

        handler: '/api/commentary/',

        template_types: {
            default: 'default',
            fixture: 'fixture',
            goal: 'goal',
            quotation: 'quotation'
        },

        templates: {},

        crest_urls: {
            club: $('input[type=hidden][name=clublogo]').val(),
            opponent: $('input[type=hidden][name=opponentlogo]').val()
        },

        special_event_types: [
            'breaking-news',
            'extra-full-time',
            'extra-half-time',
            'extra-time',
            'full-time',
            'goal',
            'half-time',
            'highlight',
            'kickoff',
            'penalty',
            'red-card',
            'season-pass',
            'second-half',
            'substitution',
            'yellow-card',
            'injury'
        ],

        embed_items: [
            {
                term: 'facebook',
                class: 'facebook'
            },
            {
                term: 'instagram',
                class: 'instagram'
            },
            {
                term: 'twitter',
                class: 'twitter'
            },
            {
                term: 'youtube',
                class: 'video'
            },
            {
                term: 'vimeo',
                class: 'video'
            }
        ]
    };

    var containerid = $('[name="ContainerId"]').val();
    var container = $('#commentarycontainer');
    var keyMomentsCount = 0;

    // bomb out if nowhere to put commentary, or no id
    if (!container.length || !containerid) {
        return;
    }

    // Same script runs both
    var includeEditControls = $('form.commentaryeditor').length > 0;

    // Either every 10 seconds or on an update
    setInterval(renderCommentary, 10 * 1000);
    $(document).on('CommentaryUpdated', renderCommentary);
    renderCommentary(true);

    function renderCommentary(isInitial) {
        if (window.EFL !== undefined && window.EFL.MatchCentre !== undefined && window.EFL.MatchCentre.areWidgetsPaused()) {
            //don't render
            return;
        }
        var action = 'get/';

        if (includeEditControls) {
            action = 'getfresh/';
        }

        $.ajax({
            url: settings.handler + action + containerid,
            context: container
        })
        .done(function (data) {
            var $control_header = container.find('.control-header');
            // Build out commentary

            build_summary(data);

            $control_header.find('.updated-time').empty().append(data.LastModified + ' (UK)');
            $control_header.removeClass('hidden');

            // Remove all items not found in the data
            var htmlitems = container.find('.items .item');

            htmlitems.each(function (index, value) {
                var $item, itemId, match_found;

                $item = $(value);
                itemId = parseInt( $item.attr('data-id') );

                match_found = !data.Items ? false : $.grep(data.Items, function (item, index) { return item.Id === itemId; });

                if (!match_found || !match_found.length) {
                    $item.remove();
                }
            });

            if (data.Items && data.Items !== null) {
                // remove the blog placeholder message once there's blog content
                container.find('.placeholder-items').hide();
                // Iterate through the data
                for (var j = 0; j < data.Items.length; j++) {

                    var dataitem, $item, $new_item,
                        get_new = false;

                    // Find existing item
                    dataitem = data.Items[j];
                    $item = htmlitems.filter('[data-id="' + dataitem.Id + '"]');

                    // if item already exists, update it, else create a new item
                    if ($item.length) {

                        // update the existing item if the EventType hasn't changed

                        if (dataitem.Image && !$item.find('.image-embed').length) {
                            get_new = true;
                        }

                        if (dataitem.EmbeddedMarkup && !$item.find('.embed').length) {
                            get_new = true;
                        }

                        if (!$item.hasClass(getEventClass(dataitem.EventType))) {
                            get_new = true;
                        }

                        if (get_new) {
                            $new_item = newCommentaryItem(dataitem);
                            $item.replaceWith($new_item);
                            updateCommentaryItem(dataitem, $new_item, true);
                        }
                        else {
                            updateCommentaryItem(dataitem, $item, false);
                        }
                    }
                    else {
                        $item = newCommentaryItem(dataitem);

                        if ($item) {
                            container.find('.items').prepend($item.first());
                            updateCommentaryItem(dataitem, $item, true);
                        }
                    }
                }
            } else {
                // show the blog placeholder message if there's no blog content
                container.find('.placeholder-items').show();
            }

            if (isInitial) {
                window.EFL.eventDispatcher.dispatch('club-commentary-update-initial', data);
            }

            // Now tell other elements about the update
            window.EFL.eventDispatcher.dispatch('club-commentary-update', data);
            window.EFL.eventDispatcher.dispatch('update-iframes');
        });

        function getEventClass(eventType) {
            var event_class = '';

            if (eventType) {
                event_class = eventType.replace('(', '').replace(')', '').toLowerCase().trim().replace(' ', '-');

                // fix for invalid .2nd-half class (should only affect historic commentary entries)
                event_class = event_class === '2nd-half' ? 'second-half' : event_class;
            }

            return event_class;
        }

        function build_summary(data) {

            var $summary, $edit_controls, $summaryContent, $summary_list;

            if (data) {
                $summary = container.find('.summary');
                container.data('data', data);

                $summaryContent = $('<div class="commentary-entry-inner"><h2 class="h4 title">Summary</h4><ul></ul></div>');
                $summary_list = $summaryContent.find('ul');

                for (var i = 1; i <= 6; i++) {
                    if (data['Summary' + i] && data['Summary' + i] !== '') {
                        $summary_list.append('<li><span>' + data['Summary' + i] + '</span></li>');
                    }
                }

                $summary.empty().append($summaryContent);

                if (includeEditControls) {
                    $edit_controls = $('<div class="edit-controls" />');
                    $edit_controls.append('<input type="button" value="Edit Summary" class="editsummary" />');
                    $summary.append($edit_controls);
                }

                if (data.ShowSummary) {
                    $summary.removeClass('summary-hidden');
                }
                else {
                    $summary.addClass('summary-hidden');
                }
            }
        }

        function newCommentaryItem(dataitem) {
            var $templates, item;

            $templates = $('#templates');

            if (dataitem && dataitem.EventType) {

                item = getTemplate($templates, dataitem);

                if (item) {
                    item.attr('data-id', dataitem.Id);
                    item.data('changed', dataitem.Changed);

                    if (dataitem.IsOpponent) {
                        item.addClass('opposition');
                    }
                }
            }

            return item;
        }

        function getTemplate($templates, dataitem) {

            var template_type;

            if ($templates && dataitem && dataitem.EventType) {

                switch (getEventClass(dataitem.EventType)) {

                    case 'goal':
                    case 'breaking-news':
                        template_type = settings.template_types.goal;
                        break;

                    case 'full-half':
                        template_type = settings.template_types.fixture;
                        break;

                    case 'quote':
                        template_type = settings.template_types.quotation;
                        break;

                    case 'highlight':
                    case 'penalty':
                    case 'half-time':
                    case 'kickoff':
                    case 'extra-time':
                    case 'extra-half-time':
                    case '2nd-half':
                    case 'second-half':
                    default:
                        template_type = settings.template_types.default;
                        break;
                }

                // if the template has not already been stored, store it
                if (!settings.templates[template_type] || !settings.templates[template_type].length) {
                    settings.templates[template_type] = $($templates.find('[data-template="' + template_type + '"]').html());
                }

                return settings.templates[template_type].clone();
            }

            return false;
        }

        // Returns a set of markup to represent a commentary item
        function updateCommentaryItem(dataitem, item, force) {
            var itemchanged, datachanged,
                $content, $embed, $image_embed, $crest, $crest_img, $cite,
                _class, eventClass, ownGoal = false;

            if (!dataitem || !item) {
                return;
            }

            itemchanged = new Date(item.data('changed'));
            datachanged = new Date(dataitem.Changed);

            if (force || itemchanged < datachanged) {
                // console.log("item older, time to update", dataitem);

                _class = '';
                eventClass = '';
                $content = item.find('.content-column .content');
                $embed = item.find('.embed');
                $image_embed = item.find('.image-embed');
                $crest = item.find('.crest');
                $cite = item.find('.content-column .cite');

                if (dataitem.EventType) {
                    var isKeyMoment = isSpecialEvent(dataitem.EventType);
                    _class = isKeyMoment ? ' special-event' : '';
                    eventClass = getEventClass(dataitem.EventType);

                    if (eventClass === 'season-pass') {
                        item.addClass('watermark-bottom-left');
                    }

                    // work out if the goal was an own goal
                    if (dataitem.EventType === 'goal' && (dataitem.IsOpponent && dataitem.Player != null && dataitem.Player.indexOf('us_') > -1)
                        || !dataitem.IsOpponent && dataitem.Player != null && dataitem.Player.indexOf('thm_') > -1) {
                        ownGoal = true;
                        item.addClass('own-goal');
                    }

                    if (isKeyMoment) {
                        keyMomentsCount++;
                    }
                }

                if (keyMomentsCount == 0) {
                    container.addClass('no-special-events');
                }
                else {
                    container.removeClass('no-special-events');
                }

                item.data('changed', dataitem.Changed);
                item.attr('data-type', dataitem.EventType);
                item.data('item', dataitem);

                item.addClass('item ' + eventClass + ' ' + _class);
                item.find('.time-column').empty().append(formatTime(dataitem));

                if (dataitem.Content) {
                    $content.empty().append(formatContent(dataitem));
                }
                else {
                    $content.remove();
                }

                if ($cite.length) {
                    $cite.empty();

                    if (dataitem.Cite) {
                        $cite.append(dataitem.Cite);
                        $cite.removeClass('hidden');
                    }
                    else {
                        $cite.remove();
                    }
                }

                // Set embeds

                // build an array of all embed classes
                var embed_classes = [];
                $.each(settings.embed_items, function() {
                    embed_classes.push(this.class);
                });

                // remove all existing embed classes
                item.removeClass(embed_classes.join(' '));

                // if embed markup is present, apply the appropriate embed class
                if (dataitem.EmbeddedMarkup) {
                    $embed.removeClass('hidden').empty().append(dataitem.EmbeddedMarkup);
                    item.addClass(getEmbedItemClass(dataitem.EmbeddedMarkup));
                }
                else {
                    $embed.remove();
                }

                if (dataitem.Image) {
                    $image_embed.removeClass('hidden').empty().append('<img src="' + dataitem.Image + '" />');
                }
                else {
                    $image_embed.remove();
                }

                if (dataitem.IsOpponent) {
                    item.addClass('opposition');
                }
                else {
                    item.removeClass('opposition');
                }

                // set the crest URL
                if (dataitem.IsOpponent && settings.crest_urls.opponent) {
                    dataitem.Crest = settings.crest_urls.opponent;
                }
                else if (!dataitem.IsOpponent && settings.crest_urls.club) {
                    dataitem.Crest = settings.crest_urls.club;
                }
                else {
                    dataitem.Crest = '';
                }

                // Set crest image
                if (dataitem.Crest && $crest.length) {
                    $crest_img = $('<img />');
                    $crest_img.attr('src', dataitem.Crest);
                    $crest_img.attr('alt', '');

                    $crest.empty().append($crest_img);
                    $crest.removeClass('hidden');
                }
                else {
                    $crest.addClass('hidden');
                }

                // Edit controls
                if (includeEditControls) {
                    item.find('.edit-controls').empty().append('<input type="button" value="Edit" class="edit" /><input type="button" value="Delete" class="delete" />');
                }
                else {
                    item.find('.edit-controls').remove();
                }

            }
        }

        function isSpecialEvent(event_type) {
            var event_class,
                isSpecialEvent = false;

            event_class = getEventClass(event_type);

            for (var i = 0; i < settings.special_event_types.length; i++) {
                if (settings.special_event_types[i] === event_class) {
                    isSpecialEvent = true;
                }
            }

            return isSpecialEvent;
        }

        // checks the embed markup for the first occurrence of a social media term and returns a class based on it
        // returns a default of 'embed' if no occurences of any of the terms are found
        function getEmbedItemClass(EmbeddedMarkup) {
            var arr = [],
                returnClass = 'embed';

            // build an array of embed items with indexOf
            for (var i = 0; i < settings.embed_items.length; i++) {
                arr.push({
                    index: EmbeddedMarkup.indexOf( settings.embed_items[i].term ),
                    class: settings.embed_items[i].class
                });
            }

            // sort the array by indexOf occurance within the embed markup
            arr = arr.sort(function(a, b) {
                return a.index - b.index;
            });

            // return the earliest occurring term's class
            $.each(arr, function(index, value) {
                if (value.index > -1 && value.class) {
                    returnClass = value.class;
                    return;
                }
            });

            return returnClass;
        }

        // Generate the visible time
        function formatTime(dataitem) {
            var dataval, hrs, mins;

            if (dataitem.MatchTime > 0) {
                var output = dataitem.MatchTime.toString();
                if (dataitem.InjuryTime > 0) {
                    output += "+" + dataitem.InjuryTime.toString();
                }
                return output + '&rsquo;';
            }
            else {
                dataval = new Date(dataitem.DateCreated.indexOf("+00:00") < 0 ? dataitem.DateCreated + "Z" : dataitem.DateCreated); //Treat as UTC

                hrs = dataval.getUTCHours() + '';
                hrs = hrs.length === 1 ? '0' + hrs : hrs;

                mins = dataval.getUTCMinutes() + '';
                mins = mins.length === 1 ? '0' + mins : mins;

                return hrs + ':' + mins;
            }
        }

        // Generate the content to display
        function formatContent(dataitem) {
            var $content = $('<div />');

            if (dataitem && dataitem.Content) {
                $content.append(dataitem.Content);
            }

            return $content.html();
        }
    }

    // Toggle key moments
    container.on(settings.changeEvent, function (event, filter) {

        var timeout;

        if (filter) {
            container.addClass('filtered');

            //filtered-complete set after animation to ensure that refresh in data doesn't cause a blip
            clearTimeout(timeout);
            timeout = setTimeout(function () {
                container.addClass('filtered-complete');
                window.EFL.eventDispatcher.dispatch('update-adition-scroll-lock');
            }, 500);
        }
        else {
            clearTimeout(timeout);
            container.removeClass('filtered-complete');
            timeout = setTimeout(function () {
                container.removeClass('filtered');
                window.EFL.eventDispatcher.dispatch('update-adition-scroll-lock');
            }, 500);
        }

    });

})(window.jQuery);;
// Converts Neulion access tokens from cookies to local storage

window.EFL = window.EFL || {};
window.EFL.VideoStorage = function () {
    'use strict';

    var cookies = window.EFL.local.cookies;
    var storage = window.EFL.local.storage;

    function changeCookieToLocalStorage(name, keepCookie) {
        if (cookies.get(name)) {
            storage.set(name, cookies.get(name));
            if (!keepCookie) {
                cookies.remove(name);
            }
        }
    }

    function changeLocalStorageToCookie(name, keepStorage) {
        if (storage.get(name)) {
            cookies.set(name, storage.get(name));
            if (!keepStorage) {
                storage.remove(name);
            }
        }
    }

    if (window.EFL.video) {
        //these are in server response after login. Put them in local storage
        changeCookieToLocalStorage(window.EFL.video.videoTokenName, false);
        changeCookieToLocalStorage(window.EFL.video.videoRefreshTokenName, false);

        //make sure key cookies/storage haven't got out of sync
        if (!cookies.get(window.EFL.video.videoLoggedInName) && storage.get(window.EFL.video.videoLoggedInName)) {
            changeLocalStorageToCookie(window.EFL.video.videoLoggedInName, true);
        }
        if (!cookies.get(window.EFL.video.videoAccessLevelName) && storage.get(window.EFL.video.videoAccessLevelName)) {
            changeLocalStorageToCookie(window.EFL.video.videoAccessLevelName, true);
        }

        //make sure local storage values match the cookies (they are the master)
        changeCookieToLocalStorage(window.EFL.video.videoLoggedInName, true);
        changeCookieToLocalStorage(window.EFL.video.videoAccessLevelName, true);

        //make sure if logged out accesslevel isn't set
        if (cookies.get(window.EFL.video.videoLoggedInName) === 'false') {
            cookies.remove(window.EFL.video.videoAccessLevelName);
            storage.remove(window.EFL.video.videoAccessLevelName);
        }

        //need to make sure any clients who cleared cookies but left local storage are logged out and reset
        //and they shouldn't get into that state again
        if (storage.get('videoreset') !== 1) {
            //clear up
            cookies.remove(window.EFL.video.videoLoggedInName);
            storage.remove(window.EFL.video.videoLoggedInName);
            cookies.remove(window.EFL.video.videoTokenName);
            storage.remove(window.EFL.video.videoTokenName);
            cookies.remove(window.EFL.video.videoRefreshTokenName);
            storage.remove(window.EFL.video.videoRefreshTokenName);
            storage.remove(window.EFL.video.videoFirstName);
            storage.remove(window.EFL.video.videoTrackUserName);
            cookies.remove(window.EFL.video.videoAccessLevelName);
            storage.remove(window.EFL.video.videoAccessLevelName);
            storage.remove(window.EFL.video.packagesTokenName);
            storage.remove(window.EFL.video.packagesDateTokenName);
            storage.remove(window.EFL.video.nextGameTokenName);
            storage.remove(window.EFL.video.nextGameDateTokenName);
            storage.remove(window.EFL.video.configTokenName);
            storage.remove(window.EFL.video.configDateTokenName);
            window.sessionStorage.removeItem('ucalyptus');

            storage.set('videoreset', 1);
        }
    }
    else {
        //probably editing a block without a page - nothing to do
    }
}
;
/* eslint-disable indent, vars-on-top */

window.EFL = window.EFL || {};
window.EFL.VideoController = (function () {
    "use strict";

    var playerLoaded = false;

    var cookies = window.EFL.local.cookies;
    var storage = window.EFL.local.storage;
    var attempts = 50;

    function changeLocalStorageToCookie(name, keepStorage) {
        if (storage.get(name)) {
            cookies.set(name, storage.get(name));
            if (!keepStorage) {
                storage.remove(name);
            }
        }
    }

    return function (config) {
        var methods = {
            // Load in multiple scripts then fire a callback
            loadScripts: function (scripts, callback) {
                var loaded = 0;
                if (!scripts.length) { // Ensure we have an array
                    scripts = [scripts];
                }

                var success = function () {
                    if (++loaded >= scripts.length) {
                        callback();
                    }
                };

                for (var x = 0; x < scripts.length; x++) {
                    $.ajax({
                        url: scripts[x],
                        dataType: 'script',
                        success: success
                    });
                }
            },

            // Load the necessary Neulion scripts
            loadPlayer: function (callback) {
                var trackerJs = window.EFL.video.cdnUrl + 'scripts/nltracker.js';
                var playerJs = window.EFL.video.cdnUrl + 'scripts/nlplayer2.js';

                if (playerLoaded) {
                    callback();
                } else {
                    this.loadScripts(
                        [
                            trackerJs,
                            playerJs
                        ],
                        function () {
                            playerLoaded = true;
                            callback();
                        }
                    );
                }
            },

            getBrandColour: function () {
                function rgb2hex(rgb) {
                    if (/^#[0-9A-F]{6}$/i.test(rgb)) return rgb.replace('#', '0x');

                    rgb = rgb.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/);
                    function hex(x) {
                        return ('0' + parseInt(x).toString(16)).slice(-2);
                    }
                    return '0x' + hex(rgb[1]) + hex(rgb[2]) + hex(rgb[3]);
                }

                try {
                    var colour = $('.video-area').css('border-bottom-color');
                    colour = rgb2hex(colour);

                    if (colour === '0x000000') {
                        colour = rgb2hex($('.site-header h1'));
                    }

                    return colour;
                } catch (e) {
                    return '0xFFFFFF';
                }
            },

            initPlayer: function (live, clubevent, audio, video, callback) {
                var uid = window.sessionStorage.getItem('ucalyptus');
                var loggedIn = cookies.get(window.EFL.video.videoLoggedInName);
                var token = storage.get(window.EFL.video.videoTokenName) || cookies.get(window.EFL.video.videoTokenName);

                if ((loggedIn === 'true' && uid !== null) || loggedIn !== 'true') {
                    this.initPlayerUidReady(live, clubevent, audio, video, callback);
                }
                else {
                    this.getUserDetails(token, function () {
                        methods.initPlayerUidReady(live, clubevent, audio, video, callback);
                    });
                }
            },

            initPlayerUidReady: function (live, clubevent, audio, video, callback) {
                var settings = {
                    api: window.EFL.video.apiUrl,
                    locResource: window.EFL.video.cdnUrl,
                    locQos: window.EFL.video.qosUrl,
                    site: window.EFL.video.clubid,
                    nosp: true,
                    autostart: true,
                    noFlash: true,
                    nlHTML5: true,
                    useCustomControls: true,
                    unsupportedCallback: methods.unsupportedCallback,
                    statusCallback: methods.tracking.statusCallback,
                    externalTrackCallback: 'NLGoogleTagManager',
                    brand: methods.getBrandColour(), // Set the brand colour based on the footer
                    customFlashVars: {
                        logo: window.EFL.video.sponsorLogoUrl,
                        logolink: window.EFL.video.sponsorUrl
                    },
                    drm: {
                        pr: window.EFL.video.drmPrUrl,
                        wv: window.EFL.video.drmWvUrl,
                        fp: window.EFL.video.drmFpUrl,
                        credentials: false
                    },
                    concurrency: {
                        pcm: { server: window.EFL.video.pcmUrl },
                        callback: concurrencyNotifyUser
                    },
                    errorCallback: errorNotifyUser,
                    locEDL: window.EFL.video.edlUrl,
                    trackMilestones: [25, 50, 75, 100],
                    customScripts: window.EFL.video.customScripts,
                    customStyles: window.EFL.video.customStyles,
                    locStats: window.EFL.video.locStats,
                    locTeamImg: window.EFL.video.locTeamImg,
                    customPath: '../../efl/site_4/',
                    noAccessCallback: function () { }, // These events used by Realise not Neulion
                    videoLoadedCallback: function () { } // These events used by Realise not Neulion
                    // gaa: ''
                };

                //if pre-roll ad id defined set up config for playing ads
                if (window.EFL.video.prerollsid !== '') {
                    settings.hasAds = true;
                }
                else {
                    settings.hasAds = false;
                }

                var loggedIn = cookies.get(window.EFL.video.videoLoggedInName);

                //in case cookies/storage have got out of sync 
                if (!cookies.get(window.EFL.video.videoLoggedInName) && storage.get(window.EFL.video.videoLoggedInName)) {
                    loggedIn = storage.get(window.EFL.video.videoLoggedInName);
                    changeLocalStorageToCookie(window.EFL.video.videoLoggedInName, true);
                }

                //add uid if user is logged in - for NeuLion QOS (debugging user issues and for reporting)
                if (loggedIn === 'true') {
                    var uid = window.sessionStorage.getItem('ucalyptus');

                    if (uid !== null) {
                        uid = window.atob(uid);
                    }
                    else {
                        uid = '';
                    }

                    settings.uid = uid;
                }
                //add tuid if user is logged in
                if (loggedIn === 'true') {
                    settings.tuid = storage.get(window.EFL.video.videoTrackUserName) || cookies.get(window.EFL.video.videoTrackUserName);
                }

                if (live || audio) {
                    settings.customFlashVars.logo = window.EFL.video.liveSponsorLogoUrl;
                    settings.customFlashVars.logolink = window.EFL.video.liveSponsorUrl;

                }

                // Override default settings with those passed in config
                config = config || {};
                for (var x in config) {
                    settings[x] = config[x];
                }
                config = settings;

                this.loadPlayer(function () {
                    console.log(config);
                    nlRenderPlayer(config, null);

                    if (callback) {
                        callback();
                    }
                });
            },

            getUserDetails: function getUserDetails(token, callback) {
                $.ajax({
                    url: window.EFL.video.apiUrl + '/account/profile',
                    dataType: 'json',
                    data: {
                        format: 'json'
                    },
                    headers: {
                        'Authorization': 'Bearer ' + token
                    },
                    success: function (response) {

                        if (response.code === 'noaccess') {
                            console.log('Failed to retrieve user details from Neulion');
                        } else {

                            //store their username in session storage as its an email address and we don't want it
                            //lingering in cookies or local storage beyond the session
                            //also obfuscate it a but just in case..
                            //get back out with window.atob(window.sessionStorage.getItem('ucalyptus'))
                            //Cameron picked this name/spelling!
                            var enc = window.btoa(response.user.username);
                            window.sessionStorage.setItem('ucalyptus', enc);
                            if (callback) {
                                callback();
                            }
                        }
                    },
                    error: function () {
                        console.log('getUserDetails - error');

                        console.log('An error occurred while retrieving user details from Neulion');
                    }
                });
            },

            access: {
                init: function () {
                    var value = storage.get(window.EFL.video.videoTokenName) || cookies.get(window.EFL.video.videoTokenName);
                    if (value === undefined) {
                        methods.access.setupAnonymous();
                    }
                },

                check: function (videoName, live, clubevent, audio, video, retry) {
                    var token = storage.get(window.EFL.video.videoTokenName) || cookies.get(window.EFL.video.videoTokenName);
                    if (live) {
                        if (videoName === "") {
                            //no id found on match for us to look anything up in NeuLion
                            //e.g. its not an opta match
                            //and no manual neulion fixture id has been put into the epi fixture object
                            return;
                        }

                        //modify 6-digit game opta game id to include a prefix if appropriate
                        var extid = videoName.toString();
                        if (extid.match(/^([0-9])([0-9])([0-9])([0-9])([0-9])([0-9])$/)) {
                            extid = window.EFL.video.nlExternalIdPrefix + extid;
                        }

                        $.ajax({
                            url: window.EFL.video.apiUrl + 'game',
                            dataType: 'json',
                            data: {
                                extid: extid,
                                purchases: 'true'
                            },
                            headers: {
                                'Authorization': 'Bearer ' + token
                            },
                            success: function (details) {
                                methods.access.handle(details, live, clubevent, audio, video);
                            },
                            error: function (request) {
                                methods.access.failed(request, videoName, live, clubevent, audio, video, retry);
                            }
                        });
                    } else {
                        $.ajax({
                            url: window.EFL.video.apiUrl + 'video/' + videoName,
                            dataType: 'json',
                            data: {
                                format: 'json',
                                clubid: window.EFL.video.clubid
                            },
                            headers: {
                                'Authorization': 'Bearer ' + token
                            },
                            success: function (details) {
                                window.EFL.beginDateTimeGMT = typeof (details.beginDateTimeGMT) !== 'undefined' ? details.beginDateTimeGMT : "";
                                window.EFL.endDateTimeGMT = typeof (details.endDateTimeGMT) !== 'undefined' ? details.endDateTimeGMT : "";
                                window.EFL.liveState = typeof (details.liveState) !== 'undefined' ? details.liveState : "";
                                methods.access.handle(details, live, clubevent, audio, video);
                            },
                            error: function (request) {
                                methods.access.failed(request, videoName, live, clubevent, audio, video, retry);
                            }
                        });
                    }
                },

                handle: function (details, live, clubevent, audio, video) {
                    //on a live broadcast video there will be two audio tracks
                    //we need to set 'home' or 'away' appropriately in this instance
                    var audioTrack = '';

                    if (details.blackout && !audio) {
                        alert('Live video not available in your location');
                    } else if (details.noAccess) {
                        window.EFL.packages = details;

                        if (config.noAccessCallback) {
                            config.noAccessCallback();
                        }
                    } else {
                        if (config.videoLoadedCallback) {
                            config.videoLoadedCallback(details.seoName);
                        }

                        if (window.EFL.programs) {
                            var programs = window.EFL.programs;

                            //check what audio programs are available and decide which we want

                            if (audio === 'home') {
                                //try for main audio
                                if (programs['audio']) {
                                    audio = 'audio';
                                }
 
                            } else if (audio === 'away') {
                                //try for dedicated away audio 
                                if (programs['audio-away']) {
                                    audio = 'audio-away';
                                }

                                //season 2 - away audio is actually home audio as
                                //all clubs have their own CMS and audio is set up
                                //this way now
                                if (programs['audio']) {
                                    audio = 'audio';
                                }
                            }

                            //check what video programs are available and decide which we want

                            if (video === 'home') {
                                //try for dedicated home video (although this type isn't really expected)
                                if (programs['home']) {
                                    video = 'home';
                                }
                                //try for broadcast video
                                else if (programs['broadcast']) {
                                    audioTrack = 'home';
                                    video = 'broadcast';
                                }
                            } else if (video === 'away') {
                                //try for dedicated away video
                                if (programs['away']) {
                                    video = 'away';
                                }
                                //try for broadcast video
                                else if (programs['broadcast']) {
                                    audioTrack = 'away';
                                    video = 'broadcast';
                                }
                            }
                        }

                        // Track what type of video this is: Free/Freemium/Premium
                        var videoVersion = '';
                        if (!details.accessSkus) {
                            videoVersion = 'Free';
                        } else {
                            videoVersion = 'Freemium';

                            for (var sku in details.accessSkus) {
                                if (sku.toUpperCase().indexOf('MONTH') > -1 || sku.toUpperCase().indexOf('SEASON') > -1) {
                                    videoVersion = 'Premium';
                                }
                            }
                        }
                        window.EFL.analyticsController.track({ 'video-version': videoVersion });

                        var drm = ((live && !audio) || (!live && (typeof (details.drm) !== 'undefined')));

                        // Actually play the video
                        if (live) {
                            window.EFL.analyticsController.track({ 'video-type': 'Live' });
                            methods.playVideo(details.extId, details.seoName, details.seoName, drm, live, clubevent, audio, video, details.season, audioTrack);
                        } else {
                            window.EFL.analyticsController.track({ 'video-type': 'VOD' });
                            methods.playVideo(details.id, details.name, details.seoName, drm, live, clubevent, audio, video);
                        }
                    }
                },

                failed: function (request, seoname, live, clubevent, audio, video, retry) {
                    if (request.status === 401) { // If unauthorized get a fresh token
                        if (retry !== false) { // But only if we've just tried and it failed again
                            var loggedIn = cookies.get(window.EFL.video.videoLoggedInName);

                            if (loggedIn === 'true') { // Have to refresh via the server as its a user token
                                var refreshToken = storage.get(window.EFL.video.videoRefreshTokenName) || cookies.get(window.EFL.video.videoRefreshTokenName);

                                if (!refreshToken) {
                                    methods.access.forceLogin();
                                    return false;
                                }

                                $.ajax({
                                    type: 'POST',
                                    url: '/api/refreshaccesstoken/get',
                                    data: { token: refreshToken },
                                    success: function (data) {
                                        window.EFL.local.storage.set(window.EFL.video.videoTokenName, window.EFL.local.cookies.get(window.EFL.video.videoTokenName));

                                        methods.access.check(seoname, live, clubevent, audio, video, false);
                                    },
                                    error: function (data) {
                                        methods.access.forceLogin();
                                    }
                                });
                            } else { // User is not logged in
                                methods.access.setupAnonymous(function () {
                                    methods.access.check(seoname, live, clubevent, audio, video, false);
                                });
                            }
                        }
                    }
                },

                // If something's gone really wrong (like the refresh token isn't working)
                forceLogin: function () {
                    $.ajax({
                        url: window.EFL.logoutUrl,
                        dataType: 'jsonp',
                        async: false,
                        complete: function () {
                            var cookies = window.EFL.local.cookies;
                            var storage = window.EFL.local.storage;

                            cookies.remove(window.EFL.video.videoLoggedInName);
                            cookies.remove(window.EFL.video.videoTokenName);
                            storage.remove(window.EFL.video.videoTokenName);
                            cookies.remove(window.EFL.video.videoRefreshTokenName);
                            storage.remove(window.EFL.video.videoRefreshTokenName);
                            storage.remove(window.EFL.video.videoFirstName);
                            cookies.remove(window.EFL.video.videoAccessLevelName);

                            window.location = window.EFL.video.loginUrl;
                        }
                    });
                },

                setupAnonymous: function (callback) {
                    $.ajax({
                        url: window.EFL.video.idpUrl + '/secure/accesstoken',
                        dataType: 'json',
                        data: {
                            format: 'json'
                        },
                        success: function (response) {
                            storage.set(window.EFL.video.videoTokenName, response.data.accessToken);
                            cookies.set(window.EFL.video.videoLoggedInName, 'false');

                            if (callback) {
                                callback();
                            }
                        }
                    });
                }
            },

            getSessionGuid: function (forceNew) {
                function s4() {
                    return Math.floor((1 + Math.random()) * 0x10000).toString(16).substring(1);
                }

                if (cookies.get('videosession') && !forceNew) {
                    return cookies.get('videosession');
                } else {
                    var guid = s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4();
                    if (!forceNew) {
                        //if we're forcing a new one its for pcm, so don't store it as the session id
                        cookies.set('videosession', guid);
                    }
                    return guid;
                }
            },
            getProgramNumber: function (programName) {
                var programTypes = [
                    { numeric: 1024, text: 'audio-away' },
                    { numeric: 512, text: 'audio-home' },
                    { numeric: 256, text: 'audio' },
                    { numeric: 128, text: 'halftime-highlight' },
                    { numeric: 64, text: 'full-continuous-highlight' },
                    { numeric: 32, text: 'condensed-away' },
                    { numeric: 16, text: 'condensed-home' },
                    { numeric: 8, text: 'condensed-broadcast' },
                    { numeric: 4, text: 'away' },
                    { numeric: 2, text: 'home' },
                    { numeric: 1, text: 'broadcast' }
                ];

                for (var x = 0; x < programTypes.length; x++) {
                    var programType = programTypes[x];
                    if (programType.text === programName)
                    {
                        return programType.numeric;
                    }
                }
            },
            listContains: function (list, value) {
                return jQuery.inArray(value + '', list.replace(/,\s+/g, ',').split(',')) >= 0;
            },
            playVideo: function (id, name, seoName, drm, live, clubevent, audio, video, season, audioTrack) {
                function playVideo() {
                    nlPlayVideo(videoSettings);
                    window.EFL.video.name = name;
                    window.EFL.video.seoName = seoName;

                    if (audio) {
                        window.EFL.analyticsController.track({ event: 'audio-engagement', category: 'Audio Engagement', action: name, label: 'Play' });
                    } else {
                        window.EFL.analyticsController.track({ event: 'video-engagement', category: 'Video Engagement', action: name, label: 'Play' });
                    }

                    $('#nl-video-player').off('click', playVideo).removeClass('await-click');
                }

                if (nlPlayerReady) {
                    var token = storage.get(window.EFL.video.videoTokenName) || cookies.get(window.EFL.video.videoTokenName);

                    var videoSettings = {
                        id: id,
                        name: name,

                        publishPointParams: {
                            token: token,
                            deviceid: methods.getSessionGuid(false)
                        }
                    };

                    //if logged we pass a unique pcid so NeuLion's PCM (Player Concurrency Manager)
                    //can uniquely identify user session and block shared account access (see callback concurrencyNotifyUser set in playerconfig )
                    var loggedIn = cookies.get(window.EFL.video.videoLoggedInName);
                    if (loggedIn === 'true' && window.EFL.video.pcmUrl !== '')
                    {
                        videoSettings.publishPointParams.pcid = methods.getSessionGuid(window.EFL.video.pcidUseSession === 'false');
                    }

                    if (drm) {
                        videoSettings.isDRM = true;
                    }
                    if (audio) {
                        window.EFL.audio = audio;
                        videoSettings.isAudio = true;
                    }

                    if (live) {
                        videoSettings.type = 'game';

                        videoSettings.gt = methods.getProgramNumber(video);

                        if (methods.listContains(window.EFL.video.pauseWidgetStates, window.EFL.gameState) && 
                            methods.listContains(window.EFL.video.pauseWidgetTypes, videoSettings.gt))
                        {
                            console.log("starting live video, pausing widgets and showing control");
                            $('#pauseOptaWidgets').removeClass('hidden');
                            window.EFL.MatchCentre.pauseWidgets();
                        }

                        if (season) {
                            videoSettings.season = season;
                        }

                        // gameState
                        // -1 - unavailable
                        // 0 - upcoming
                        // 1 - live
                        // 2 - dvr
                        // 3 - archive
                        // 4 - pending archive

                        videoSettings.isLive = (window.EFL.gameState === 1 || window.EFL.gameState === 2);
                        videoSettings.isDVRLive = window.EFL.gameState === 2;

                        //there are only multiple audio tracks on live broadcast video
                        //i.e. gt = 1 and gs = 1, and under these circumstances audioTrack will either be
                        //'home' or 'away'
                        if (audioTrack !== '' && window.EFL.gameState === 1) {
                            videoSettings.audioTrack = audioTrack;
                        }

                        if (window.EFL.leagueId !== '') {
                            videoSettings.league = window.EFL.leagueId;
                        }

                        videoSettings.isIDExternal = true;
                        if (window.EFL.gameState === 1) {
                            videoSettings.beginDateTimeGMT = window.EFL.beginDateTimeGMT;
                        }
                        if (window.EFL.gameState === 2) {
                            videoSettings.beginDateTimeGMT = window.EFL.beginDateTimeGMT;
                            videoSettings.endDateTimeGMT = window.EFL.endDateTimeGMT;
                        }

                        //just pass today's date (they will actually just use the date part and not the time)
                        videoSettings.date = new Date().toISOString().replace('Z', ''); // e.g. "2016-11-21T08:00:00.000";

                        videoSettings.gs = window.EFL.gameState;
                    }
                    else if (clubevent) {
                        if (clubevent)
                        {
                            videoSettings.publishPointParams.clubid = window.EFL.video.clubid;
                        }
                        videoSettings.type = 'video';
                        videoSettings.isLive = window.EFL.liveState === 1 || window.EFL.liveState === 2;
                        videoSettings.isDVRLive = window.EFL.liveState === 2;
                        if (window.EFL.liveState === 1) {
                            videoSettings.beginDateTimeGMT = window.EFL.beginDateTimeGMT;
                        }
                        if (window.EFL.liveState === 2) {
                            videoSettings.beginDateTimeGMT = window.EFL.beginDateTimeGMT;
                            videoSettings.endDateTimeGMT = window.EFL.endDateTimeGMT;
                        }
                    }
                    else {
                        // Don't pass the clubid for live games
                        videoSettings.publishPointParams.clubid = window.EFL.video.clubid;
                    }

                    if (audio) {
                        videoSettings.gt = methods.getProgramNumber(audio);
                    }

                    //show pre-roll ad if site has a preroll ad id set
                    //yet to agree on values, but the gist is that we will pass X if user is logged in with premium account and Y
                    //if freemium account or not logged in. Going with X=premium, Y=free for now
                    //values now agreed as X=live and Y=vod
                    //this is confusing as we're only showing these for VOD and not live video
                    //but these were the values initially configured, so Russ will segment using these values

                    //Update 10/9/18 : We're now switching to vodf for free/anonymous users
                    //and vodp for premium users

                    //make sure we only play these on VODs
                    if (!clubevent && !live && !audio && !video) {
                        //if its disabled or not configured this will be empty
                        if (window.EFL.video.prerollsid !== '') {
                            var loggedin = cookies.get(window.EFL.video.videoLoggedInName) === 'true';
                            var stream = "vodf";
                            var accessLevel = cookies.get(window.EFL.video.videoAccessLevelName);
                            if (loggedin && accessLevel === "premium") {
                                stream = "vodp";
                            }

                            //don't play on packages page (showreel)
                            if (window.location.pathname !== window.EFL.video.packagesUrl) {
                               videoSettings.adTag = 'https://adfarm1.adition.com/banner?sid=' + window.EFL.video.prerollsid + '&wpt=X&prf[stream]=' + stream + '&prf[videoid]=' + id;
                            }
                        }
                    }

                    $(window).trigger('throttled-resize');

                    console.log(videoSettings);
                    $('#nl-video-player').css('visibility', 'visible');

                    

                    //make sure player window shown for match audio/video
                    if (live) {
                        $('#hiddenVideo-match').removeClass('collapse');
                        $(window).trigger('throttled-resize');
                    }

                    // Android needs to detect an additional click on the video to start properly
                    if (isMobile.android.device) {
                        $('#nl-video-player').on('click', playVideo).addClass('await-click');
                    } else {
                        playVideo();
                    }
                } else { // For some reason the NL player hasn't always loaded in time so try again in a moment
                    if (--attempts > 0) {
                        setTimeout(function () {
                            console.log('NL player not available yet - attempts remaining ' + attempts);
                            //console.log(typeof (nlPlayVideo), nlConfigs, document.getElementById(nlConfigs.playerId), nlPlayerReady);
                            methods.playVideo(id, name, seoName, drm, live, clubevent, audio, video, season, audioTrack);
                        }, 500);
                    } else {
                        console.log('Gave up trying to load NL player');
                    }
                }
            },

            unsupportedCallback: function () {
                console.log('Unfortunately the video player is not supported on this browser.');
            },

            tracking: {
                nlInitCustomTracker: function (config) {
                    // console.log('nlInitCustomTracker', config);
                },

                statusCallback: function (event) { // loadedmetadata, paused, resume, stopped, disconnected
                    // console.log('event', event);

                    if (event === 'stopped') {
                        window.EFL.analyticsController.track({ event: 'video-engagement', category: 'Video Engagement', action: window.EFL.video.name, label: '100%' });
                    }
                },

                NLGoogleTagManager: function (htmlid, action, data) {
                    // console.log('NLGoogleTagManager', htmlid, action, data);

                    if (dataLayer && data.video.id !== null) {
                        var trackObj = {};

                        switch (action) {
                            case 'videostart':
                                if (data.video.live) {
                                    // Track video start - live content
                                }
                                else {
                                    // Track video start - archive content
                                }
                                break;

                            case 'videocomplete':
                                // Track video complete
                                break;

                            case 'videopercent':
                                // Track video percent
                                window.EFL.analyticsController.track({ event: 'video-engagement', category: 'Video Engagement', action: window.EFL.video.name, label: data.value + '%' });
                                break;

                            case 'videoduration':
                                if (data.video.live) {
                                    // Track video duration - live content
                                }
                                else {
                                    // Track video duration - archive content
                                }
                                break;

                                // Video Actions tracking

                            case 'videostate':
                                switch (data.value) {
                                    case 'playing':
                                        // Track Resume
                                        break;

                                    case 'paused':
                                        // Track Pause
                                        if (window.EFL.audio) {
                                            window.EFL.analyticsController.track({ event: 'audio-engagement', category: 'Audio Engagement', action: window.EFL.video.name, label: 'Pause' });
                                        } else {
                                            window.EFL.analyticsController.track({ event: 'video-engagement', category: 'Video Engagement', action: window.EFL.video.name, label: 'Pause' });
                                        }
                                        break;

                                    case 'seeking':
                                        // Track Seeking
                                        break;
                                }
                                break;

                            case 'bitrate':
                                // Track Bitrate change
                                break;

                            case 'windowmode':
                                // Track Fullscreen
                                break;

                            case 'volume':
                                // Track Volume change
                                break;

                            case 'slow motion':
                                // Track Slow motion button click
                                break;

                            case 'go to live':
                                // Track Go Live button click
                                break;

                            case 'ff:archive':
                            case 'ff:live':
                                // Track Seek Forward 10 Seconds button click;
                                break;

                            case 'back up:archive':
                            case 'back up:live':
                                // Track Seek Backward 10 Seconds button click;
                                break;

                            case 'start over:archive':
                            case 'start over:live':
                                // Track Start Over;
                                break;
                        }

                        if (trackObj !== null && trackObj.event !== null) {
                            //dataLayer.push(trackObj);
                        }
                    }
                }
            }
        };

        window.nlInitCustomTracker = methods.tracking.nlInitCustomTracker;
        window.NLGoogleTagManager = methods.tracking.NLGoogleTagManager;

        return {
            playVideo: function (videoName, live, clubevent, audio, video, dontHide) {
                if (!dontHide) {
                    $('#nl-video-player').css('visibility', 'hidden');
                }

                // Decide whether or not to show our mobile warning message
                if (live && !audio) {

                    //make sure player window shown or warning won't display
                    if (live) {
                        $('#hiddenVideo-match').removeClass('collapse');
                        $(window).trigger('throttled-resize');
                    }

                    $('.drm-warning').removeClass('hidden');

                    // On Apple and Android hide the player and prevent progress
                    if (isMobile.apple.device || isMobile.android.device) {
                        $('.video-area').addClass('hidden');
                        return false;
                    } else {
                        $('.video-area').removeClass('hidden');
                    }
                } else {
                    $('.drm-warning').addClass('hidden');
                    $('.video-area').removeClass('hidden');
                }

                if (!playerLoaded) {
                    console.log('Initialising player');

                    methods.initPlayer(live, clubevent, audio, video, function () {
                        methods.access.init();
                        methods.access.check(videoName, live, clubevent, audio, video);
                    });
                } else {
                    methods.access.init();
                    methods.access.check(videoName, live, clubevent, audio, video);
                }
            }
        };
    };

    function concurrencyNotifyUser()
    {

        var isInFullScreen = (document.fullscreenElement && document.fullscreenElement !== null) ||
            (document.webkitFullscreenElement && document.webkitFullscreenElement !== null) ||
            (document.mozFullScreenElement && document.mozFullScreenElement !== null) ||
            (document.msFullscreenElement && document.msFullscreenElement !== null);

        if (isInFullScreen) {
            if (document.exitFullscreen) {
                document.exitFullscreen();
            } else if (document.webkitExitFullscreen) {
                document.webkitExitFullscreen();
            } else if (document.mozCancelFullScreen) {
                document.mozCancelFullScreen();
            } else if (document.msExitFullscreen) {
                document.msExitFullscreen();
            }
        } 

        var $videoBlock = $('.video-area');

        $videoBlock.find('.video-player').remove();
        $videoBlock.addClass('player-removed');
        $videoBlock.prepend('<div class="video-message"><p>' + window.EFL.video.concurrencyMessage + '</p></div>');
    }

    function errorNotifyUser(message) {

        if (typeof message !== 'undefined' && typeof message.code !== 'undefined' && message.code === 'adBlockerDetected') {

            var isInFullScreen = (document.fullscreenElement && document.fullscreenElement !== null) ||
                (document.webkitFullscreenElement && document.webkitFullscreenElement !== null) ||
                (document.mozFullScreenElement && document.mozFullScreenElement !== null) ||
                (document.msFullscreenElement && document.msFullscreenElement !== null);

            if (isInFullScreen) {
                if (document.exitFullscreen) {
                    document.exitFullscreen();
                } else if (document.webkitExitFullscreen) {
                    document.webkitExitFullscreen();
                } else if (document.mozCancelFullScreen) {
                    document.mozCancelFullScreen();
                } else if (document.msExitFullscreen) {
                    document.msExitFullscreen();
                }
            }

            var $videoBlock = $('.video-area');

            $videoBlock.find('.video-player').remove();
            $videoBlock.addClass('player-removed');
            $videoBlock.prepend('<div class="video-message"><p>' + window.EFL.video.adBlockMessage + '</p></div>');
        }
    }
})();;
/* eslint-disable indent, vars-on-top */
/*
	Clicking triggers a video to play
    Actual video player stuff is handled by video-controller.js
    Sign-up/register for access stuff is handled by video-access.js

	Example usage
        <div class="video-area">
            <a class="video-splash" href="#" style="background-image: url(/images/sample-news.jpg);" data-playvideo-id="charlton-highlightstestfree">
                <div>
                    <span class="icon-Forward-Arrow"></span>
                    <h2>Bobby signed a one-year-deal on Monday</h2>
                    <p class="detail">1 Month ago</p>
                </div>
            </a>
            <div class="video-player" data-matchparentdimensions></div>
        </div>
*/

window.EFL = window.EFL || {};
window.EFL.VideoTrigger = function () {
    'use strict';

    //video unavailable message should only be shown once per page so enable the first one if it is present on page load
    $('.video-down').first().removeClass('hidden');

    var settings = {
        trigger: 'playvideo-id',
        area: 'playvideo-area',
        isLive: 'playvideo-live',
        isClubEvent: 'playvideo-clubevent',
        checkEventStart: 'check-event-start',
        isAudio: 'playvideo-audio',
        isVideo: 'playvideo-video',
        getDetail: 'getdetail',
        isDisabled: 'playback-disabled',
        isMatchAudio: 'match-audio',
        isMatchVideo: 'match-video',

        areaClass: 'video-area',
        playerClass: 'video-player',
        playingClass: 'video-playing',
        playedClass: 'video-played',
        containerClass: 'video-container',
        metadataClass: 'video-metadata',

        nlPlayerId: 'nl-video-player',

        scrollToPlayer: true,
        scrollOffset: 100,
        scrollSpeed: 300,

        freemiumAccessClass: 'freemium-video',
        premiumAccessClass: 'premium-video',
        premiumAccessEvent: 'premiumVideoAccess-preCrossDomainCheck',
        eventNotLiveClass: 'event-not-live'
    };

    if ($('[data-' + settings.trigger + ']').length) {
        var currentVideo = '';

        var isSafari = function () {
            var isDesktopSafari = !!navigator.platform && /MacIntel/.test(navigator.platform) && !!navigator.userAgent && /Safari/.test(navigator.userAgent) && !/Chrome/.test(navigator.userAgent);
            return isDesktopSafari;
        };

        var isEdge = function () {
            return (window.navigator.userAgent.indexOf('Edge') > -1);
        };

        var isClubEvent = $('[data-' + settings.isClubEvent + ']').length == 1 ? true : false;
        var shouldCheckEvent = $('[data-' + settings.checkEventStart + ']').length == 1 ? true : false;
        var eventName = $('[data-' + settings.trigger + ']').attr('data-' + settings.trigger);
        var checkEventStartedIntervalMs = window.EFL.local.time.minute;

        var checkIfEventStarted = function (name) {
            $.ajax({
                url: '/api/events/islive',
                dataType: 'json',
                data: {
                    seoName: name
                },
                success: function (details) {
                    if (details) {
                        //event state change - reload the page
                        window.location.reload(true);
                    }
                    else {
                        setTimeout(function () {
                            checkIfEventStarted(name);
                        }, checkEventStartedIntervalMs);
                    }
                }
            });
        };

        if (shouldCheckEvent) {
            setTimeout(function () {
                checkIfEventStarted(eventName);
            }, checkEventStartedIntervalMs);


        }

        // Fetch and render metadata
        var renderMetadata = function (seoName, location) {
            $(location).empty();
            isClubEvent = location.parent().find('.video-splash').first().attr('data-playvideo-clubevent') == 'true';
            $.ajax({
                type: 'GET',
                url: '/api/videodetail/get',
                data: {
                    seoName: encodeURIComponent(seoName),
                    isClubEvent: isClubEvent
                },
                success: function (details) {
                    $(location).html(details);
                }
            });
        };

        var setBackgroundImage = function ($player, imgUrl) {
            if (!$player || !imgUrl) {
                return false;
            }

            imgUrl = imgUrl.replace('_es.jpg', '_eh.jpg'); // Convert to "hero" size
            imgUrl = imgUrl.replace('_eb.jpg', '_eh.jpg');

            $player.css('background-image', 'url(' + imgUrl + ')');
        };

        var setVideoClass = function ($player, $target) {
            if (!$player || !$target) {
                return false;
            }

            if ($target.hasClass(settings.freemiumAccessClass)) {
                $player.addClass(settings.freemiumAccessClass);
            } else {
                $player.removeClass(settings.freemiumAccessClass);
            }

            if ($target.hasClass(settings.premiumAccessClass)) {
                $player.addClass(settings.premiumAccessClass);
            } else {
                $player.removeClass(settings.premiumAccessClass);
            }

            if ($target.hasClass(settings.eventNotLiveClass)) {
                $player.addClass(settings.eventNotLiveClass);
            } else {
                $player.removeClass(settings.eventNotLiveClass);
            }
        };

        // If a player doesn't have a particular video specified
        var findVideoContent = function (player) {
            var $player = $(player);
            var videos = $('.article[data-' + settings.trigger + '][data-' + settings.trigger + ' != ""]').not('.video-match').not('.premium-match');
            if (videos.length) {
                var $target = videos.first();
                var seoName = $target.attr('data-' + settings.trigger); // Extract the video ID

                // Find where to put the metadata
                var $container = $player.closest('.' + settings.containerClass);
                var $metadata = $container.find('.' + settings.metadataClass);

                renderMetadata(seoName, $metadata);
                setBackgroundImage($player, $target.find('img').first().attr('src')); // Extract the thumbnail image
                setVideoClass($player, $target);

                $player.data(settings.trigger, seoName);
            }
        };

        // The user has clicked a link to trigger a video playing
        // eslint-disable-next-line complexity
        var videoClick = function (event) {
            event.preventDefault();

            var $area;
            var $self = $(this);
            var videoName = $self.data(settings.trigger);
            var isLive = ($self.attr('data-' + settings.isLive) || false);
            var isClubEvent = ($self.attr('data-' + settings.isClubEvent) || false);
            var isAudio = ($self.attr('data-' + settings.isAudio) || false);
            var isVideo = ($self.attr('data-' + settings.isVideo) || false);
            var isDisabled = ($self.attr('data-' + settings.isDisabled) || false);
            var isMatchAudio = ($self.attr('data-' + settings.isMatchAudio) || false);
            var isMatchVideo = ($self.attr('data-' + settings.isMatchVideo) || false);
            var currentTarget = $(event.currentTarget);
            var videoImage = currentTarget.find('img').first().attr('src');


            if (isDisabled) {
                return false;
            }

            var playerConfig = {
                containerId: settings.nlPlayerId,
                noAccessCallback: function () {
                    premiumVideoAccess(videoName);
                }
            };

            //Stored globally so that it can be accessed by video-packages.js for re-opening popup when complete
            window.EFL.video.isMatchAudio = isMatchAudio;
            window.EFL.video.isMatchVideo = isMatchVideo;

            if ($self.hasClass(settings.freemiumAccessClass)) {
                if ($self.hasClass(settings.freemiumAccessClass)) {
                    delete window.EFL.packages;
                }

                premiumVideoAccess(videoName);
                return false;
            } else if ($self.hasClass(settings.premiumAccessClass) && window.EFL.packages) {
                // This is a premium video and we've previously determined the user doesn't have access
               
                premiumVideoAccess(videoName);

                return false;
            } else {
               
                var stopAtFirstPlayer = false;

                // If there is a specific player 
                if ($('.master-player').length && !$self.closest('.master-player').length) {
                    $area = $('.master-player').first();
                    stopAtFirstPlayer = true;
                } else if ($self.data(settings.area)) {
                    $area = $($self.data(settings.area)); // If a video area is specified then use that
                } else if ($self.closest('.' + settings.areaClass).length) {
                    $area = $self.closest('.' + settings.areaClass); // Else see if there's a parent we can use
                } else if (isLive) {
                    stopAtFirstPlayer = true;
                } else if ($self.parents('.video-archive-container').length > 0) {
                    var $archiveContainer = $self.parents('.video-archive-container');
                    $area = $archiveContainer.find('.' + settings.areaClass);
                } else {
                    $area = $('.' + settings.areaClass).first(); // Otherwise find the first player instance on the page
                    stopAtFirstPlayer = ($('.' + settings.areaClass).length > 1);
                }
                var $player = $area.find('.' + settings.playerClass);
                var $container = $area.closest('.' + settings.containerClass);
                var $metadata = $container.find('.' + settings.metadataClass);
                var $otherLinks = $('[data-' + settings.trigger + '="' + videoName + '"]');

                $('.audio-player').removeClass('audio-player');

                if (isAudio) {
                    $area.addClass('audio-player');
                    $area.find('.' + settings.areaClass).addClass('audio-player');
                }

                if (isAudio && (isMobile.apple.device || isSafari() || isEdge())) {
                    $('.cookies-warning').removeClass('hidden');
                } else {
                    $('.cookies-warning').addClass('hidden');
                }
                
                // If the video player is an accordion, ensure we open it
                if (!$self.hasClass(settings.premiumAccessClass) && !stopAtFirstPlayer) {
                    if (!$self.hasClass(settings.premiumAccessClass)) {
                        if ($area.hasClass('.collapse')) {
                            $area.collapse();
                        } else if ($area.closest('.collapse').length) {
                            $area.closest('.collapse').collapse();
                        }
                    }
                }

                // If the video player is on match centre and we're using first player, it will be hidden, so needs to be opened
                if (!$self.hasClass(settings.premiumAccessClass) && (stopAtFirstPlayer && $('body').hasClass('match-centre'))) {
                    if (!$self.hasClass(settings.premiumAccessClass)) {
                        if ($area.hasClass('.collapse')) {
                            $area.collapse();
                        } else if ($area.closest('.collapse').length) {
                            $area.closest('.collapse').collapse();
                        }
                    }
                }

                // A dirty delay in case the player is hidden in an accordion
                setTimeout(function () {
                    // Don't start the video if it's already playing or if an ad is currently playing
                    var videoAdIsPlaying = (typeof nlIsAdPlaying === 'function') && nlIsAdPlaying();

                    if (videoName !== currentVideo && !videoAdIsPlaying) {
                        // Create a player instance if it doesn't already exist
                        if (!$('#' + settings.nlPlayerId).length) {
                            $player.append('<div id="' + settings.nlPlayerId + '" data-matchparentdimensions></div>');
                        } else {
                            //we used to do this in all circumstances, but when playing preroll video ads
                            //the moving of the player in the dom caused events not to fire
                            //something within thousands of line of neulion player and google IMA SDK javascript
                            //we still need to do this when multiple players required on the page
                            //so only do it when absolutely necessary (can't actually load multiple NL video players on a single page)
                            //We should also disabled ads from playing or there will be an 8 second timeout while it fails to load the ad
                            //- this only lasts on this pageview and will be re-enabled on next page load
                            //this scenario shouldn't happen too often so doesn't lose too many potential ad views
                            //https://jira.neulion.com/browse/EFL-1111
                            //https://realise-mt.atlassian.net/browse/EFLRM-253

                            if ($player.find('#' + settings.nlPlayerId).length < 1) {
                                //stop ads playing if enabled
                                window.EFL.video.prerollsid = '';

                                //move the player to the closest and most appropriate spot to play the video
                                $('#' + settings.nlPlayerId).appendTo($player);
                            }
                        }

                        $('#' + settings.nlPlayerId).css('visibility', 'hidden');
                        $(window).trigger('throttled-resize');

                        playerConfig.videoLoadedCallback = function (seoName) {
                            currentVideo = seoName;

                            // Set appropriate CSS class
                            $('.' + settings.playingClass).removeClass(settings.playingClass);
                            $area.addClass(settings.playingClass);
                            $area.find('.' + settings.areaClass).addClass(settings.playingClass);
                            $self.addClass(settings.playingClass);
                            $otherLinks.addClass(settings.playingClass);
                            $area.addClass(settings.playedClass);
                            $area.find('.' + settings.areaClass).addClass(settings.playedClass);
                            $self.addClass(settings.playedClass);
                            $otherLinks.addClass(settings.playedClass);

                            if (!isLive) {
                                var $tempPlayer = $area.find('.video-splash').first();
                                renderMetadata(seoName, $metadata);
                                setBackgroundImage($tempPlayer, videoImage); // Extract the thumbnail image
                                setVideoClass($tempPlayer, currentTarget);
                                $tempPlayer.data(settings.trigger, seoName);
                            }
                        };

                        // Create the video player
                        var videoController = new window.EFL.VideoController(playerConfig);

                        setTimeout(function () {
                            videoController.playVideo(videoName, isLive, isClubEvent, isAudio, isVideo);
                        }, 1000);
                    }

                    if (!$self.hasClass(settings.premiumAccessClass)) {
                        // Scroll the player into view
                        if (settings.scrollToPlayer && !elementInViewport($area)) {
                            $('html, body').animate({
                                scrollTop: $area.offset().top - settings.scrollOffset
                            }, settings.scrollSpeed);
                        }
                    }
                }, 100);

                if ($self.hasClass(settings.premiumAccessClass) || stopAtFirstPlayer) {
                    return false;
                }
            }
        };

        // The user has attempted to access a video they don't currently have access to
        var premiumVideoAccess = function (video) {
            window.EFL.eventDispatcher.dispatch(settings.premiumAccessEvent, video);
        };

        var elementInViewport = function (element) {
            var $element = $(element);

            var viewportTop = $(window).scrollTop();
            var viewportBottom = viewportTop + $(window).height();

            var elementTop = $element.offset().top;
            var elementBottom = elementTop + $element.height();

            return ((elementBottom <= viewportBottom) && (elementTop >= viewportTop));
        };


        // If first video player is empty then populate it
        $('[data-' + settings.trigger + ']').each(function () {
            if ($(this).attr('data-' + settings.trigger) === '' && $(this).height()) {
                findVideoContent(this);
            }
        });

        // Match metadata for all visible video players if required
        $('[data-' + settings.trigger + ']').each(function () {
            if ($(this).attr('data-' + settings.trigger) !== '' && $(this).height() && $(this).attr('data-' + settings.getDetail) == "true") {
                renderMetadata($(this).attr('data-' + settings.trigger), $(this).parent().parent().nextAll('.' + settings.metadataClass).first());
            }
        });

        $('body').on('click', '[data-' + settings.trigger + ']', videoClick);

        // Ability to autoplay a video on page load
        if (window.location.search.indexOf('autoplayvideo=true') > -1) {
            window.EFL.eventDispatcher.registerHandler('videoAccessChecked', function () {
                $('.video-available .play-video [data-' + settings.trigger + '][data-' + settings.isVideo + ']').first().click();
            });
        }

        // Ability to autoplay audio on page load
        if (window.location.search.indexOf('autoplayaudio=true') > -1) {
            window.EFL.eventDispatcher.registerHandler('videoAccessChecked', function () {
                $('.audio-available .play-audio [data-' + settings.trigger + '][data-' + settings.isAudio + ']').first().click();
            });
        }

        window.EFL.eventDispatcher.registerHandler('resetVideo', function () {
            if ($('#nl-video-player').css('visibility') == 'hidden') {
                // Sometimes the premium video event can cause the currently playing video to become hidden - so unhide it
                $('#nl-video-player').css('visibility', 'visible');
                nlResumeVideo();
            } 
        });
    }
}
;
/* eslint-disable indent, vars-on-top */
/* Requires EventDispatcher */
window.EFL = window.EFL || {};
window.EFL.VideoAccess = function () {
    'use strict';

    var settings = {
        trigger: '[data-videoaccess-check]',
        event: 'premiumVideoAccess-postCrossDomainCheck',
        audioClass: 'audio-available',
        videoClass: 'video-available',
        liveClass: 'live-available',
        premiumAccessClass: 'premium-video',
        passesClass: 'passes-available',
    };

    var cookies = window.EFL.local.cookies;
    var storage = window.EFL.local.storage;

    window.EFL.eventDispatcher.registerHandler(settings.event, function () {
        window.EFL.eventDispatcher.dispatch('resetVideo');

        //make sure the user doesn't get redirected back to the previous page after login through the popup
        storage.remove('login_referrer');

        if ($('.packages-modal').length) {
            $('.packages-modal').appendTo('body'); // Move to modal to end of the DOM so that it doesn't inherit any parent mark-up styles

            if (cookies.get(window.EFL.video.videoLoggedInName) === 'true') {
                $('#packages-modal').html($('#packages-modal-loggedin').html()).modal();
            } else {
                $('#packages-modal').html($('#packages-modal-loggedout').html()).modal();
                window.EFL.analyticsController.pageView('sign-in/', 'Sign-in-Form-Start');
            }

            //function in video-incognito.js
            checkIncognito(document.querySelectorAll(settingsIncognito.trigger));
        } else { // fallback for if the modal is missing for some reason
            if (cookies.get(window.EFL.video.videoLoggedInName) === 'true') {
                alert('You are logged in but do not have access to view that content');
            } else {
                alert('You must be logged in to view that content');
            }
        }
    });

    if ($(settings.trigger).length) {
        var $body = $('body');

        var eventStarted = false;

        var updateAudioVideo = function (videoAvailable, audioAvailable) {
            if (videoAvailable) {
                $body.addClass(settings.videoClass);
            } else {
                $body.removeClass(settings.videoClass);
            }

            if (audioAvailable) {
                $body.addClass(settings.audioClass);
            } else {
                $body.removeClass(settings.audioClass);
            }
        };

        var updateLive = function (liveAvailable) {
            if (liveAvailable) {
                $body.addClass(settings.liveClass);
            } else {
                $body.removeClass(settings.liveClass);
            }
        };

        var updatePasses = function (passes) {
            // if packages are available, then audio and video won't be visible yet
            $body.removeClass(settings.audioClass, settings.videoClass);
            // add passes class to the body
            $body.addClass(settings.passesClass);
            // add individual pass type classes to body
            $.each(passes, function (index, passType) {
                $body.addClass(passType);
            });

            // have to dynamically find the last visible button to remove the right border from
            $body.find('.pass-button:visible').last().addClass('no-border');
        };

        var flagAudioVideoPremium = function () {
            $body.find('.play-video, .play-audio').find('a').addClass(settings.premiumAccessClass);
        };

        var flagVideoPremium = function () {
            $body.find('.play-video').find('a[data-match-video="true"]').addClass(settings.premiumAccessClass);
        };

        var buildPackagesModel = function (data, id, homeaway, showAudioMatchPrePurchase){
            $.ajax({
                type: 'POST',
                data: {
                    bundles: JSON.stringify(data.bundlePurchases),
                    dateTimeGMT: data.dateTimeGMT,
                    gamePurchases: JSON.stringify(data.gamePurchases),
                    showAudioMatchPrePurchase: showAudioMatchPrePurchase,
                    gameState: data.gameState,
                    blackout: JSON.stringify(data.blackout),
                    grouping: data.grouping,
                    programPurchases: JSON.stringify(data.programPurchases),
                    free: data.free,
                    noAccess: data.noAccess,
                    matchId: data.id,
                    extId: data.extId,
                    availablePrograms: data.availablePrograms,
                    url: window.location.href,
                    homeAway: homeaway
                },
                url: '/api/videoaccess/checkaccess',
                // eslint-disable-next-line complexity
                success: function (details) {
                    // Let's check we have some HTML back from the controller
                    // Update the HTML if so, otherwise we keep the "Subscribe to Ifollow" link shown (not for Match Center though).
                    // EFLRM-634 - Make sure showAudioMatchPrePurchase is true
                    if (details.html != "" && showAudioMatchPrePurchase) {
                        $(".subscribe-links").html(details.html);
                    }
                    $(".subscribe-links").removeClass("hidden");
                    
                    window.EFL.gameState = data.gameState;
                    window.EFL.beginDateTimeGMT = typeof (data.dateTimeGMT) !== 'undefined' ? data.dateTimeGMT : "";
                    window.EFL.endDateTimeGMT = typeof (data.endDateTimeGMT) !== 'undefined' ? data.endDateTimeGMT : "";
                    window.EFL.leagueId = typeof (data.leagueId) !== 'undefined' ? data.leagueId : "";

                    if (data.gameState == 0 || data.gameState == -1) {
                        // add available pass classes to body
                        if (details.passClasses.length > 0 || $('[data-match-message]').text().length > 0) {
                            if (window.EFL.video.packagesSuppressed == 'true') {
                                details.passClasses = [];
                            }
                            updatePasses(details.passClasses);
                        }
                    }

                    if (data.gameState === 0) {
                        // Listen for match-centre announcing kickoff
                        if (!eventStarted) {
                            if ($body.hasClass('livecommentary')) {
                                checkAccess(id, homeaway, showAudioMatchPrePurchase);
                                eventStarted = true;
                            } else {
                                window.EFL.eventDispatcher.registerHandler('on-kickoff-countdown-complete', function () {
                                    checkAccess(id, homeaway, showAudioMatchPrePurchase);
                                    eventStarted = true;
                                });
                            }
                        } else { // Or if match-centre say kick-off has happened but Neulion doesn't, keep checking
                            setTimeout(function () {
                                checkAccess(id, homeaway, showAudioMatchPrePurchase);
                            }, 1000 * 60 * 5); // Once every five minutes
                        }
                    }
                    else if (data.gameState === 1) {
                        updateLive(true);
                        eventStarted = true;
                    }

                    if (data.availablePrograms > 0) {
                        window.EFL.programs = details.parsedPrograms;
                    }

                    updateAudioVideo(details.videoAvailable, details.audioAvailable);

                    if (details.flagAudioVideoPremium) {
                        flagAudioVideoPremium();
                        window.EFL.packages = data;
                    }
                    else if (details.flagVideoPremium) {
                        //season 2 behaviour. Marquee games are blacked out but may show domestic 
                        //users a match pass to lift video restrictions
                        flagVideoPremium();
                        window.EFL.packages = data;
                    }
                    else {
                        // If somehow the user has got this far but isn't "fully" logged in, log them out
                        // unless its a free video/audio game so they don't need to be logged in
                        if (!storage.get(window.EFL.video.videoFirstName) && data.free != 1) {
                            window.EFL.local.cookies.remove(window.EFL.video.videoTokenName);
                            window.EFL.local.storage.remove(window.EFL.video.videoTokenName);
                            window.EFL.local.cookies.remove(window.EFL.video.videoRefreshTokenName);
                            window.EFL.local.storage.remove(window.EFL.video.videoRefreshTokenName);
                            flagAudioVideoPremium();
                        }
                    }

                    window.EFL.eventDispatcher.dispatch('videoAccessChecked');
                }
            })

        }

        var checkAccess = function (id, homeaway, showAudioMatchPrePurchase, retry) {
            var token = storage.get(window.EFL.video.videoTokenName) || cookies.get(window.EFL.video.videoTokenName);

            if (id === '') {
                //no id found on match for us to look anything up in NeuLion
                //e.g. its not an opta match
                //and no manual neulion fixture id has been put into the epi fixture object
                return;
            }

            //modify 6-digit game opta game id to include a prefix if appropriate
            var extid = id.toString();
            if (extid.match(/^([0-9])([0-9])([0-9])([0-9])([0-9])([0-9])$/)) {
                extid = window.EFL.video.nlExternalIdPrefix + extid;
            }

            $.ajax({
                url: window.EFL.video.apiUrl + 'game',
                data: {
                    extid: extid,
                    purchases: 'true'
                },
                headers: {
                    'Authorization': 'Bearer ' + token
                },
                dataType: 'json',
                success: function (data) {
                    buildPackagesModel(data, id, homeaway, showAudioMatchPrePurchase);
                },
                error: function (request) {
                    if (request.status === 404) {
                        //nasty situation in testing season 2 in staging with some opta games
                        //having 6 digit external ids and some the same but prefixed with a 'g'
                        //this little hacks allows it to cope with both in some fashion
                        if (window.EFL.video.nlExternalIdPrefix != '') {
                            //look for the game without the prefix
                            window.EFL.video.nlExternalIdPrefix = '';
                            checkAccess(id, homeaway, false);
                        }
                        //no game data at NeuLion but still want to show message if there is one
                        if ($('[data-match-message]').text().length > 0) {
                            updatePasses();
                        }
                        updateAudioVideo(false, false);
                    }
                    else if (request.status === 401) { // If unauthorized get a fresh token
                        if (retry !== false) { // But only if we've just tried and it failed again
                            setupAnonymous(function () {
                                checkAccess(id, homeaway, false);
                            });
                        } else {
                            flagAudioVideoPremium();
                        }
                    }
                    else {
                        if (retry !== false) { // But only if we've just tried and it failed again
                            setupAnonymous(function () {
                                checkAccess(id, homeaway, false);
                            });
                        } else {
                            flagAudioVideoPremium();
                        }
                    }

                    window.EFL.eventDispatcher.dispatch('videoAccessChecked');
                }
            });
        };

        var setupAnonymous = function (callback) {
            $.ajax({
                url: window.EFL.video.idpUrl + '/secure/accesstoken',
                dataType: 'json',
                data: {
                    format: 'json'
                },
                success: function (response) {
                    storage.set(window.EFL.video.videoTokenName, response.data.accessToken);
                    cookies.set(window.EFL.video.videoLoggedInName, 'false');

                    if (callback) {
                        callback();
                    }
                }
            });
        };

        $(settings.trigger).each(function () {
            var id = $(this).data('videoaccess-check');
            var homeaway = $(this).data('videoaccess-check-homeaway');
            var showAudioMatchPrePurchase = $(this).data('video-access-audiomatch-prepurchase');

            var token = storage.get(window.EFL.video.videoTokenName) || cookies.get(window.EFL.video.videoTokenName);

            if (token === undefined) {
                setupAnonymous(function () {
                    checkAccess(id, homeaway, showAudioMatchPrePurchase);
                });
            } else {
                checkAccess(id, homeaway, showAudioMatchPrePurchase);
            }
        });
    }
}
;
/* eslint-disable indent */
/*Video packages functionality for all EFL clubs */

window.EFL = window.EFL || {};
window.EFL.VideoPackages = function () {
    'use strict';

    var settings = {
        trigger: 'data-video-packages',
        event: 'get-packages',
        layoutAttr: 'data-video-packages-layout'
    };

    var gameid;

    function init() {

        var setGiftPurchase = function (item) {
            var packagePanel = $(item).closest('.package-panel');
            //is it a match pass? Need to change the target as its different markup
            if ($(item).data("mp")) {
                packagePanel = $(item).closest('.purchase-info');
            }

            //Different method when its in the comparision table
            if ($(item).data("subtable")) {
                packagePanel = $(item).closest('.gift-control');
            }

            var ctaButton = $(packagePanel).find('a.btn-primary');

            //Different method when its in the comparision table
            if ($(item).data("subtable")) {
                ctaButton = $(".packages-comparison").find('a.btn-primary[data-sku="' + $(item).data("sku") + '"]');
            }

            var href = $(ctaButton).attr('href');

            var isChecked = $(packagePanel).find($('.gift-checkbox')).is(':checked');
            if (href != null) {
                if (isChecked) {
                    href = href.replace('register=true', 'register=gift');
                }
                else {
                    href = href.replace('register=gift', 'register=true');
                }
                $(ctaButton).attr('href', href);
            }
            return;
        };

        if ($('[' + settings.trigger + ']').length) {
            $('[' + settings.trigger + ']').each(function () {
                var $el = $(this);
                var layout = $el.attr(settings.layoutAttr) || 'compare'; // select or compare

                var cookies = window.EFL.local.cookies;
                var storage = window.EFL.local.storage;

                var setupAnonymous = function (callback) {
                    $.ajax({
                        url: window.EFL.video.idpUrl + '/secure/accesstoken',
                        dataType: 'json',
                        data: {
                            format: 'json'
                        },
                        success: function (response) {
                            storage.set(window.EFL.video.videoTokenName, response.data.accessToken);
                            cookies.set(window.EFL.video.videoLoggedInName, 'false');
                            if (callback) {
                                callback();
                            }
                        }
                    });
                };

                var setupAnonymousNoStore = function (callback) {
                    $.ajax({
                        url: window.EFL.video.idpUrl + '/secure/accesstoken',
                        dataType: 'json',
                        data: {
                            format: 'json'
                        },
                        success: function (response) {
                            window.sessionStorage.setItem('anonymousToken', response.data.accessToken);
                            if (callback) {
                                callback();
                            }
                        },
                        error: function (xhr, textStatus, errorThrown) {
                            console.log("ERRRO");
                        }
                    });
                };

                //setupAnonymousNoStore();

                // First call to Neulion to get basic details on what packages are available
                var getPackages = function (layout, retry) {

                    var token = storage.get(window.EFL.video.videoTokenName) || cookies.get(window.EFL.video.videoTokenName);

                    if (window.EFL.packages) {
                        if (typeof (window.EFL.packages.free) !== 'undefined') {
                            //game is free or freemium so we want to offer the free pass
                            getContent(window.EFL.packages, true, layout, retry);
                        }
                        else {
                            //game is premium so don't show the free pass
                            getContent(window.EFL.packages, false, layout, retry);
                        }
                    } else {
                        var ajaxData = {};

                        $.ajax({
                            url: window.EFL.video.apiUrl + 'packages',
                            dataType: 'json',
                            data: ajaxData,
                            headers: {
                                'Authorization': 'Bearer ' + token
                            },
                            error: function (request) {
                                failed(request, layout, retry);
                            },
                            success: function (data) {
                                getContent(data, true, layout, retry);
                            }
                        });
                    }
                };

                var attachPanelClicks = function (items) {
                    var env = findBootstrapEnvironment();

                    $.each(items, function () {
                        if ($(this).find('a.btn-primary').parent('.hidden').length < 1) {
                            if (env !== 'xs') {
                                $(this).addClass('clickable').off('click').on('click', function (e) {
                                    var isGiftControl = $(e.target).is('.gift-label') || $(e.target).is('.gift-checkbox');
                                    if (isGiftControl) {
                                        e.stopPropagation();
                                        setGiftPurchase(e.target);
                                    }
                                    else {
                                        window.open($(this).find('a.btn-primary').attr('href'), '_self');
                                    }
                                });
                            } else {
                                $(this).removeClass('clickable').off('click');
                            }
                        }
                    });
                };

                // Second call to our own API to get full content for each package from Epi, merged with the NeuLion data
                var getContent = function (data, includeFree, layout, retry) {
                    //console.log("audio : " + window.EFL.video.isMatchAudio);
                    //console.log("video : " + window.EFL.video.isMatchVideo);
                    var token = storage.get(window.EFL.video.videoTokenName) || cookies.get(window.EFL.video.videoTokenName);

                    var loggedIn = $.cookie(window.EFL.video.videoLoggedInName);

                    var pageURL = window.location.href;

                    if (typeof window.EFL.video.isMatchAudio !== 'undefined' && window.EFL.video.isMatchAudio !== false) {
                        pageURL = window.location.protocol + '//' + window.location.host + window.location.pathname + "?autoplayaudio=true";
                    } else if (typeof window.EFL.video.isMatchVideo !== 'undefined' && window.EFL.video.isMatchVideo !== false) {
                        pageURL = window.location.protocol + '//' + window.location.host + window.location.pathname + "?autoplayvideo=true";
                    }

                    $.ajax({
                        type: 'POST',
                        url: '/api/packages/getpackages',
                        data: {
                            packages: JSON.stringify(window.EFL.packages || data || []),
                            loggedIn: loggedIn,
                            layout: layout,
                            page: pageURL,
                            includeFreePass: includeFree,
                            isMatchVideo: typeof window.EFL.video.isMatchVideo !== 'undefined' && window.EFL.video.isMatchVideo !== false,
                            isMatchAudio: typeof window.EFL.video.isMatchAudio !== 'undefined' && window.EFL.video.isMatchAudio !== false,
                            siteType: window.EFL.video.siteType,
                            videoRegion: window.dataLayer[findDLIndex('video-region')]['video-region'],
                            lbe: window.dataLayer[findDLIndex('league-blackout-excluded')]['league-blackout-excluded'],
                            hideSections: $el.attr("data-hide-sections"),
                            isPackagesPage: $el.attr("data-on-packages-page"),
                            isClubBlock: $el.attr("data-club-block")
                            // select or compare
                        },
                        error: function (request) {
                            console.error('Error whilst retrieving packages content');
                        },
                        success: function (details) {
                            $el.html(details);

                            //make sure injected off site links track
                            $(".video-packages a:not([data-track-event], [data-dropdown-trigger])").on("click", function (e) { trackLink(e, this); });

                            window.EFL.eventDispatcher.dispatch("markup-injected");

                            //EFLRM-564
                            var currenthash = document.location.hash;
                            if (currenthash !== '') {
                                setTimeout(function () {
                                    if (currenthash) {
                                        window.scrollTo(0, 0);
                                        window.location.href = currenthash;
                                    }
                                }, 1);
                            }

                            //look for a match id to check with neulion
                            var regex = /data-match-check-id\=\"([A-Za-z0-9 _]*)\"/g;
                            var src = regex.exec(details);

                            while (src !== null) {

                                //check game at neulion
                                var matchid = src[1];

                                if (matchid !== "") {
                                    //modify 6-digit game opta game id to include a prefix if appropriate
                                    var extid = matchid.toString();

                                    if (extid.match(/^([0-9])([0-9])([0-9])([0-9])([0-9])([0-9])$/)) {
                                        extid = window.EFL.video.nlExternalIdPrefix + extid;
                                    }

                                    //store it in case we need it later
                                    gameid = extid;

                                    $.ajax({
                                        url: window.EFL.video.apiUrl + 'game',
                                        data: {
                                            extid: extid,
                                            purchases: 'true'
                                        },
                                        headers: {
                                            'Authorization': 'Bearer ' + token
                                        },
                                        dataType: 'json',
                                        error: function (request) {
                                            $('.match-pass-notready-' + getUrlParameter(this.url, 'extid')).removeClass('hidden');
                                        },
                                        success: function (data) {
                                            //console.log(data);

                                            var audioMatchPass;
                                            var videoMatchPass;

                                            var isUKSkySports = false;
                                            //is this the special case where its on Sky Sports and therefore should never be available to video
                                            if ($('.match-pass-buy-container-' + data.extId).data('ukskysports')) {
                                                console.log("Is UKSS" + data.extId);
                                                isUKSkySports = true;
                                            }

                                            if (typeof data.gamePurchases !== 'undefined') {
                                                for (var j = 0; j < data.gamePurchases.length; j++) {
                                                    if (data.gamePurchases[j].sku.indexOf("MATCHA") > 0) {
                                                        audioMatchPass = data.gamePurchases[j];
                                                    }
                                                    else {
                                                        //Only set to video pass 
                                                        if (!isUKSkySports) {
                                                            videoMatchPass = data.gamePurchases[j];
                                                            console.log(data.extId + " videomatchpass=" + data.gamePurchases[j].sku);
                                                        }
                                                        
                                                    }
                                                }
                                            }

                                            if (typeof data.gamePurchases !== 'undefined' && typeof audioMatchPass !== 'undefined' && data.gameState < 2 && (typeof data.grouping === 'undefined' || data.grouping !== "marquee") && $('.audio-match-pass-buy-container-' + data.extId).attr('data-enabled') === "True")
                                            //audio match pass
                                            {
                                                //as we're going to display purchase button for a specific match pass
                                                //we should show the exact price
                                                //and make sure the purchase link has the correct SKU

                                                $('.audio-match-pass-price-' + data.extId).html(audioMatchPass.product.price.display);
                                                $('.audio-match-pass-price-' + data.extId).removeClass('hidden');
                                                $('.audio-match-pass-buy-' + data.extId).attr('href', $('.audio-match-pass-buy-' + data.extId).attr('href').replace('TEMPSKU', audioMatchPass.sku));
                                                $('.audio-match-pass-buy-' + data.extId).attr('href', $('.audio-match-pass-buy-' + data.extId).attr('href').replace('TEMPMATCHID', data.id));
                                                $('.audio-match-pass-buy-container-' + data.extId).removeClass('hidden');

                                                //work out if this game purchase is giftable
                                                var giftable = false;
                                                for (var i = 0; i < giftableProducts.length; i++) {
                                                    if (giftableProducts[i].key === data.gamePurchases[0].sku && giftableProducts[i].value === 'true') {
                                                        //console.log(data.gamePurchases[0].sku + ' is giftable on game ' + data.id);
                                                        //Show the checkbox for the gift
                                                        //console.log('.gift_' + data.extId + " show");
                                                        $('.gift_' + data.extId).removeClass('hidden');
                                                        giftable = true;
                                                    }
                                                }

                                            }
                                            //see if there are any match passes available and show appropriate content
                                            //but only through a blackout if its a UK Deny as season 2 domestic passes
                                            //are the only ones that lift a blackout and this one specifically
                                            //and only if gameState is -1, 0 or 1 (i.e. upcoming or live) 
                                            //Video match pass
                                            else if (typeof data.gamePurchases !== 'undefined' && typeof videoMatchPass !== 'undefined' && data.gameState < 2 &&
                                                (typeof data.blackout === 'undefined' || (typeof data.grouping !== 'undefined' && data.grouping === "marquee"))) {
                                                //package may be listed with a price range
                                                //as we're going to display purchase button for a specific match pass
                                                //we should show the exact price
                                                //and make sure the purchase link has the correct SKU

                                                $('.match-pass-price-' + data.extId).html(videoMatchPass.product.price.display);
                                                $('.match-pass-price-' + data.extId).removeClass('hidden');
                                                $('.match-pass-buy-' + data.extId).attr('href', $('.match-pass-buy-' + data.extId).attr('href').replace('TEMPSKU', videoMatchPass.sku));
                                                $('.match-pass-buy-' + data.extId).attr('href', $('.match-pass-buy-' + data.extId).attr('href').replace('TEMPMATCHID', data.id));
                                                $('.match-pass-buy-container-' + data.extId).removeClass('hidden');

                                                //work out if this game purchase is giftable
                                                var giftable = false;
                                                for (var i = 0; i < giftableProducts.length; i++) {
                                                    if (giftableProducts[i].key === data.gamePurchases[0].sku && giftableProducts[i].value === 'true') {
                                                        console.log(data.gamePurchases[0].sku + ' is giftable on game ' + data.id);
                                                        giftable = true;
                                                        //Show the checkbox for the gift
                                                        console.log('.gift_' + data.extId + " show");
                                                        $('.gift_' + data.extId).removeClass('hidden');
                                                    }
                                                }
                                            }
                                            else if ((data.noAccess || typeof data.blackout !== 'undefined') && typeof videoMatchPass === 'undefined') {
                                                $('.match-pass-notready-' + getUrlParameter(this.url, 'extid')).removeClass('hidden');
                                            }
                                            //You've already got access to this match pass    
                                            else {
                                                $('.match-pass-haveaccess-' + data.extId).removeClass('hidden');
                                                //Need to check if its available as a gift 
                                                if (loggedIn === 'true' && window.EFL.video.giftingEnabled === 'true') {
                                                    loggedInCheckMatchPassGift(data.extId);
                                                }
                                            }
                                        }
                                    });
                                }

                                src = regex.exec(details);
                            }
                        }
                    });
                };

                var loggedInCheckMatchPassGift = function (theId) {
                    //logged in and no match pass available
                    //now we need to get an anonymous token and check the game so that we can
                    //enable gift purchase if it is available

                    //get anonymous token and then check game service with it
                    setupAnonymousNoStore(checkGameServiceAnon(theId));
                };

                var checkGameServiceAnon = function (theId) {
                    $.ajax({
                        url: window.EFL.video.apiUrl + 'game',
                        data: {
                            extid: theId,
                            purchases: 'true'
                        },
                        headers: {
                            'Authorization': 'Bearer ' + window.sessionStorage.getItem('anonymousToken')
                        },
                        dataType: 'json',
                        error: function (request) {
                            console.log('failed checkGameServiceAnon ' + gameid);
                        },
                        success: function (data) {
                            console.log('success checkGameServiceAnon ' + gameid + " id-" + theId);
                            //see if there are any match passes available and show appropriate content
                            //but only through a blackout if its a UK Deny as season 2 domestic passes
                            //are the only ones that lift a blackout and this one specifically
                            //and only if gameState is -1, 0 or 1 (i.e. upcoming or live) 
                            if (data.gamePurchases !== undefined && data.gameState < 2 &&
                                (data.blackout === undefined || (data.grouping !== undefined && data.grouping === "marquee"))) {
                                //package may be listed with a price range
                                //as we're going to display purchase button for a specific match pass
                                //we should show the exact price
                                //and make sure the purchase link has the correct SKU

                                //work out if this game purchase is giftable
                                var giftable = false;
                                for (var i = 0; i < giftableProducts.length; i++) {
                                    if (giftableProducts[i].key === data.gamePurchases[0].sku && giftableProducts[i].value === 'true') {
                                        giftable = true;
                                    }
                                }

                                if (giftable) {
                                    var audioMatch = false;
                                    //Need to work out if its audio or video...
                                    if (data.gamePurchases[0].sku.indexOf("MATCHA") > 0) {
                                        audioMatch = true;
                                    }

                                    if (audioMatch) {
                                        //Change the text to audio match
                                        $('.pass-price p.bold', '.match-pass-giftonly-buy-container-' + data.extId).html("Audio match pass");
                                    }

                                    //Add the price and show it
                                    $('.match-pass-price-' + data.extId).html(data.gamePurchases[0].product.price.display);
                                    $('.match-pass-price-' + data.extId).removeClass('hidden');

                                    //Sort the url with the sku and ID and gift option
                                    $('.match-pass-buy-' + data.extId).attr('href', $('.match-pass-buy-' + data.extId).attr('href').replace('TEMPSKU', data.gamePurchases[0].sku));
                                    $('.match-pass-buy-' + data.extId).attr('href', $('.match-pass-buy-' + data.extId).attr('href').replace('TEMPMATCHID', data.id));
                                    //This might have been switched to register true hence needs put back
                                    $('.match-pass-buy-' + data.extId).attr('href', $('.match-pass-buy-' + data.extId).attr('href').replace('register=true', 'register=gift'));

                                    //Hide the 'You've already got access to this match pass' box
                                    $('.match-pass-haveaccess-' + data.extId).addClass('hidden');

                                    //Show the 'you've already got this match pass but its still available as a gift' box
                                    $('.match-pass-giftonly-buy-container-' + data.extId).removeClass('hidden');


                                }
                            }
                        }
                    });
                };

                var failed = function (request, data, layout, retry) {
                    if (request.status === 401) { // If unauthorized get a fresh token
                        if (retry !== false) { // But only if we've just tried and it failed again
                            setupAnonymous(function () {
                                getPackages(layout, false);
                            });
                        }
                    } else {
                        if (retry !== false) { // But only if we've just tried and it failed again
                            setupAnonymous(function () {
                                getPackages(layout, false);
                            });
                        }
                    }
                };

                // Start the process, but only once the datalayer info as loaded as that is required in order to present the correct data
                var goGetPackages = function (retry) {
                    var token = storage.get(window.EFL.video.videoTokenName) || cookies.get(window.EFL.video.videoTokenName);
                    if (typeof findDLIndex('video-region') === 'undefined' || typeof findDLIndex('league-blackout-excluded') === 'undefined') {
                        //wait and try again if we haven't hit retry limit
                        if (retry < 50) {
                            setTimeout(function () { goGetPackages(retry + 1); }, 500);
                        }
                    }
                    else {
                        if (token === undefined) {
                            setupAnonymous(function () {
                                getPackages(layout);
                            });
                        } else {
                            getPackages(layout);
                        }
                    }
                };

                goGetPackages(1);
            });
        }

        $('body').on('click', '.gift-label', function (e) {
            e.stopPropagation();
            setGiftPurchase($(this));
        });
    }

    window.EFL.eventDispatcher.registerHandler(settings.event, init);

    init();
}

function getUrlParameter(url, name) {
    name = name.replace(/[\[]/, '\\[').replace(/[\]]/, '\\]');
    var regex = new RegExp('[\\?&]' + name + '=([^&#]*)');
    var results = regex.exec(url);
    return results === null ? '' : decodeURIComponent(results[1].replace(/\+/g, ' '));
};
/*
    This scripts performs several related actions:
        1) If the user loads a page and is logged in, set their first name instead of "Sign in/register" (all pages)
        2) If the user has just come from the registration flow, sign them into our system then perform 1) (all pages)
        3) Sets the flag for 2) - only runs on the registration page (data-videocheckloggedin)
        4) When the user logins to Neulion, store their first name in a cookie and break out of the login iFrame (data-videologuserin)
*/


window.EFL = window.EFL || {};
window.EFL.VideoLoggedIn = function () {
    'use strict';

    //probably editing a block without a page
    if (!window.EFL.video) {
        //give up
        return;
    }

    var settings = {
        loginElement: '.my-account-link a',
        premiumAccessCookie: window.EFL.video.videoLoggedInName,
        accessLevelCookie: window.EFL.video.videoAccessLevelName,
        usernameCookie: window.EFL.video.videoFirstName,
        trackUsernameCookie: window.EFL.video.videoTrackUserName,
        flagCookie: 'videocheckloggedin',
        flagCookieFlag: 'videocheckingloggedin',
        maxlength: 14
    };

    var cookies = window.EFL.local.cookies;
    var storage = window.EFL.local.storage;
    
    var api = '';
    if (window.EFL.video && window.EFL.video.apiUrl) {
        api = window.EFL.video.apiUrl;
    }
    if ($('[data-videologuserin]').length) {
        api = $('[data-videologuserin]').data('videologuserin');
    }

    // 2a)
    if (
        cookies.get(settings.flagCookie) === 'true' && // Flag is set
        storage.get(settings.usernameCookie) && // Username IS set
        $('.neulion.login').length == 0 && // Not on Neulion login page
        window.location.href.indexOf(window.EFL.siteLoginUrl) == -1 // Not on our login page
    ) {
        console.log('User may have upgraded their subscription');

        var token = storage.get(window.EFL.video.videoTokenName) || cookies.get(window.EFL.video.videoTokenName);

        //check packages and user details so we can set the access level correctly
        getUserPackages(token, function () {
            getUserDetails(token, function () {
                updateUserName();
                updateAccessLevel();
            });
        });
    }

    // 2b)
    if (
        cookies.get(settings.flagCookie) === 'true' && // Flag is set
        !storage.get(settings.usernameCookie) && // Username IS NOT set
        $('.neulion.login').length == 0 && // Not on Neulion login page
        window.location.href.indexOf(window.EFL.siteLoginUrl) == -1 // Not on our login page
    ) {

        if (cookies.get(settings.flagCookieFlag) === undefined) {

            //modify cookie to only live for a minute in case user didn't complete registration
            //or we'll be trying on every page request for the rest of the session

            //this one is a flag to say we are checking - can't get expiry time to know if this is already underway
            cookies.set(settings.flagCookieFlag, 'true', 1 * window.EFL.local.time.minute);
            //this one is a flag to say we need to check
            cookies.set(settings.flagCookie, 'true', 1 * window.EFL.local.time.minute);
        }

        console.log('Fetching user details...');

        var $hiddenIframe = $('<iframe/>') // Need to hide this and delete once done
        .attr('src', decodeURIComponent(window.EFL.siteLoginUrl))
        .css({
            'height': 0,
            'visibility': 'hidden',
            'position': 'fixed',
            'top': 0,
            'left': '-999999px'
        })
        .on('load', function () {
            // Work out whether the user has actually logged in
            if ($(this).contents().find('iframe.neulion').length) {
                checkForUsername();
            } else {
                $hiddenIframe.remove();
                cookies.remove(settings.flagCookie); // Delete the logincheck cookie
            }
        })
        .appendTo('body');

        var checkForUsername = function () {
            if (cookies.get(settings.premiumAccessCookie) === 'true' && storage.get(settings.usernameCookie)) {
                updateUserName();
                updateAccessLevel();
                $hiddenIframe.remove();
            } else {
                setTimeout(checkForUsername, 500);
            }
        };
    }

    // 1) - Had to move this down as it was deleting a cookie we needed
    if (cookies.get(settings.premiumAccessCookie) === 'true' && storage.get(settings.usernameCookie)) {
        updateUserName();
        updateAccessLevel();
    }
    if (cookies.get(settings.premiumAccessCookie) === 'true' && !storage.get(settings.usernameCookie)) {
        console.log('User is logged in but we have no username');
    }

    // TODO: This functionality and all its callers should be removed once Dice is enabled.
    function updateUserName() {
        var username = storage.get(settings.usernameCookie);
        username = username.substring(0, settings.maxlength);

        $(settings.loginElement).html(username);
        $(window).trigger('throttled-resize'); // Recalculate page layout

        if (!$('[data-videocheckloggedin]').length) {
            cookies.remove(settings.flagCookie); // Delete the logincheck cookie
        }
    }

    function updateAccessLevel() {
        //clear any cached NeuLion responses in local storage that could have changed
        storage.remove(window.EFL.video.packagesTokenName);
        storage.remove(window.EFL.video.packagesDateTokenName);
        storage.remove(window.EFL.video.nextGameTokenName);
        storage.remove(window.EFL.video.nextGameDateTokenName);

        if (cookies.get(settings.premiumAccessCookie) === 'true' && storage.get(settings.usernameCookie)) {
            var accessLevel = cookies.get(settings.accessLevelCookie);

            if (accessLevel === 'freemium' || accessLevel === 'premium') {
                $('.freemium-video').removeClass('freemium-video'); // Unblock freemium videos
            }
            if (accessLevel === 'premium') {
                $('.premium-video').removeClass('premium-video'); // Unblock premium videos
            }
        }
    }

    var isPremium = function (subs, isVip) {
        
        var clubid = window.EFL.video.clubid.replace('efl', '');

        if (subs != undefined) {
            for (var x = 0; x < subs.length; x++) {
                var sub = subs[x];

                if (sub.sku && (sub.sku.toUpperCase().indexOf('MONTH') > -1 || sub.sku.toUpperCase().indexOf('SEASON') > -1 || sub.sku.toUpperCase().indexOf('DAY') > -1 || sub.sku.toUpperCase().indexOf('WEEK') > -1)) {
                    return true;
                }
            }
        }

        if (isVip) {
            return true;
        }
        
        return false;
    };

    function getUserPackages(token, callback) {
        console.log('getUserPackages');

        $.ajax({
            url: api + '/account/subscriptions',
            dataType: 'json',
            data: {
                format: 'json'
            },
            headers: {
                'Authorization': 'Bearer ' + token
            },
            success: function (response) {
                console.log('getUserPackages - success', response);

                if (response.code === 'noaccess') {
                    console.log('Failed to retrieve package details from Neulion');
                } else {
                    //we now pick up on VIP in the user details call which seems to have a flag that wasn't there before (or we missed it)
                    //var isVip = (response._ && !response.subs);
                    var isVip = false;

                    cookies.set(settings.accessLevelCookie, isPremium(response.subs, isVip) ? 'premium' : 'freemium');

                    console.log('Got package details', response.subs, isPremium(response.subs, isVip), isVip);

                    if (callback) {
                        callback();
                    }
                }
            },
            error: function () {
                console.log('getUserPackages - error');
                console.log('An error occurred while retrieving package details from Neulion');
            }
        });
    }

    function getUserDetails(token, callback) {
        console.log('getUserDetails');

        $.ajax({
            url: api + '/account/profile',
            dataType: 'json',
            data: {
                format: 'json'
            },
            headers: {
                'Authorization': 'Bearer ' + token
            },
            success: function (response) {
                console.log('getUserDetails - success', response);

                //if VIP make sure set to premium
                if (response.user.isVIP) {
                    console.log("User is a VIP");
                    cookies.set(settings.accessLevelCookie, 'premium');
                }

                if (response.code === 'noaccess') {
                    console.log('Failed to retrieve user details from Neulion');
                } else {
                    window.EFL.analyticsController.track({
                        'event': 'user-id',
                        'user-id': response.user.trackUsername
                    }, window.parent.dataLayer);

                    console.log('Got user details', response.user.firstName, response.user.trackUsername);

                    storage.set(settings.usernameCookie, response.user.firstName);
                    storage.set(settings.trackUsernameCookie, response.user.trackUsername);

                    //store their username in session storage as its an email address and we don't want it
                    //lingering in cookies or local storage beyond the session
                    //also obfuscate it a but just in case..
                    //get back out with window.atob(window.sessionStorage.getItem('ucalyptus'))
                    //Cameron picked this name/spelling!
                    var enc = window.btoa(response.user.username);
                    window.sessionStorage.setItem('ucalyptus', enc);

                    if (callback) {
                        callback();
                    }
                }
            },
            error: function () {
                console.log('getUserDetails - error');

                console.log('An error occurred while retrieving user details from Neulion');
            }
        });
    }

    function iFrameBuster() {
        window.removeEventListener('beforeunload', loginWarning);

        var storage = window.EFL.local.storage;

        if (storage.get('login_referrer') && storage.get('login_referrer').length > 0)
        {
            parent.location.href = storage.get('login_referrer');
        }
        else if (
            (parent.location !== self.location) && // Page is an iFrame
            (parent.location.origin === self.location.origin) // Parent and iFrame are on same domain
        ) {
            //Ideally we want to reload without POST data, but sometimes we need to if there is a '#' in the URL.
            //using the hash property of a location isn't enough as it doesn't catch just a # with no following text.
            if (parent.location.href.indexOf("#") != -1) {
                parent.location.reload(); // Reload the parent page
                
            } else {
                parent.location = parent.location; // Reload the parent page  
            }
            
        }
    }

    // 3) This element should exist on the registration page
    // Sets a flag so that we run the rest of this code on the next page the user loads
    if ($('[data-videocheckloggedin]').length) {
        console.log('Setting registration flag cookie');
        cookies.set(settings.flagCookie, 'true');
    }

    // If logged in and a trackUsername exists in storage then track the trackUsername
    if (cookies.get(settings.premiumAccessCookie)) {
        var trackUsername = storage.get(settings.trackUsernameCookie);

        if (typeof (trackUsername) !== 'undefined') {
            window.EFL.analyticsController.track({
                'event': 'user-id',
                'user-id': trackUsername
            });
        }
    }

    var loginWarning = function (event) {
        var message = 'Please wait until logging in has completed';
        event.returnValue = message;
        return message;
    };

    // 4) The element should exist on the logged in page
    // Fetches additional user details and busts the iFrames if needed
    if ($('[data-videologuserin]').length) {
        if (window.self !== window.top) {
            window.EFL.analyticsController.pageView('sign-in/success', 'Sign-in-Form-Success', 'content-view', window.parent.dataLayer);
        }

        window.addEventListener('beforeunload', loginWarning);

        token = storage.get(window.EFL.video.videoTokenName) || cookies.get(window.EFL.video.videoTokenName);

        console.log('Logging user in', token);

        getUserPackages(token, function () {
            getUserDetails(token, iFrameBuster);
        });
      
        // If for some reason the above AJAX calls should fail, this ensures the user doesn't end up stranded on this page
        setTimeout(function () {
            console.log('Waited too long to fetch user details - will try again on next page');

            cookies.set(settings.flagCookie, 'true');
            iFrameBuster();
        }, 2 * 60 * 1000);
    }
}
;
(function () {
    'use strict';

    var settings = {
        trigger: '[data-logout]'
    };

    if ($(settings.trigger).length) {
        $(settings.trigger).on('click', function () {
            var logoutWarning = function (event) {
                var message = 'Please wait until logging out has completed';
                event.returnValue = message;
                return message;
            };

            window.addEventListener('beforeunload', logoutWarning);

            var logoutUrl = $(this).attr('data-logouturl');
            $.ajax({
                url: logoutUrl,
                dataType: 'jsonp',
                async: false,
                complete: function () {
                    var cookies = window.EFL.local.cookies;
                    var storage = window.EFL.local.storage;
                    
                    cookies.remove(window.EFL.video.videoLoggedInName);
                    storage.remove(window.EFL.video.videoLoggedInName);
                    cookies.remove(window.EFL.video.videoTokenName);
                    storage.remove(window.EFL.video.videoTokenName);
                    cookies.remove(window.EFL.video.videoRefreshTokenName);
                    storage.remove(window.EFL.video.videoRefreshTokenName);
                    storage.remove(window.EFL.video.videoFirstName);
                    storage.remove(window.EFL.video.videoTrackUserName);
                    cookies.remove(window.EFL.video.videoAccessLevelName);
                    storage.remove(window.EFL.video.videoAccessLevelName);
                    storage.remove(window.EFL.video.packagesTokenName);
                    storage.remove(window.EFL.video.packagesDateTokenName);
                    storage.remove(window.EFL.video.nextGameTokenName);
                    storage.remove(window.EFL.video.nextGameDateTokenName);
                    storage.remove(window.EFL.video.configTokenName);
                    storage.remove(window.EFL.video.configDateTokenName);
                    window.sessionStorage.removeItem('ucalyptus');

                    window.removeEventListener('beforeunload', logoutWarning);

                    window.location.href = window.location.href;
                }
            });

            return false;
        });
    }
}());;
/*
    The default setting on iOS and Safari block cookies from being created on an unvisited domain
    This results in the Neulion login/register iFrame system failing on those platforms
    This fix works around this by quickly redirecting the user to Neulion and then back to where they were first
*/

/* Requires EventDispatcher */
window.EFL = window.EFL || {};
window.EFL.VideoCrossDomain = function () {
    'use strict';

    var settings = {
        trigger: '[data-video-crossdomain]',
        cookie: 'videocrossdomaincheck'
    };

    var cookies = window.EFL.local.cookies;
    var storage = window.EFL.local.storage;

    if (window.EFL != undefined && window.EFL.video != undefined && window.EFL.video.loginUrl != undefined && window.EFL.video.loginUrl == parent.location.pathname && document.referrer.indexOf(window.EFL.video.loginUrl) < 0)
    {
        if (document.referrer && document.referrer.split('/')[2] == window.EFL.video.thisDomain) {
            storage.set('login_referrer', document.referrer);
        }

        if (storage.get('login_referrer') != undefined && storage.get('login_referrer').indexOf('neulion') > 0) {
            storage.remove('login_referrer');
        }
    }

    function isSafari() {
        var isDesktopSafari = !!navigator.platform && /MacIntel/.test(navigator.platform) && !!navigator.userAgent && /Safari/.test(navigator.userAgent) && !/Chrome/.test(navigator.userAgent);
        return isDesktopSafari;
    }

    function crossDomainCheck(video) {
        // Only run this code on iOS or Safari
        var activateCrossDomainFix = isMobile.apple.device || isSafari();

        if (activateCrossDomainFix && !cookies.get(settings.cookie)) {
            var returnUrl = encodeURIComponent(window.location.href);

            // TODO - handle idpUrl not being available!
            var redirectUrl = window.EFL.video.idpUrl + '/efl/redirect.htm?redirect=' + returnUrl;

            cookies.set(settings.cookie, video || 'true'); // Don't run this code again this session

            window.location = redirectUrl;

            return false;
        } else {
            return true;
        }
    }

    // If the user had triggered a modal, re-trigger it once they complete the redirect
    // May need to revisit this once we have basic/freemium/premium
    function triggerVideoAccess() {
        if (cookies.get(settings.cookie) && cookies.get(settings.cookie) !== 'true') {

            $(document).ready(function () {
                window.EFL.eventDispatcher.dispatch('premiumVideoAccess-postCrossDomainCheck');
            });
            
            /* May need to re-instate at some point - video specific modal triggering
            var video = cookies.get(settings.cookie);
            var $video = $('[data-playvideo-id="' + video + '"]');
            if ($video.length) {
                $video.first().click();
            } else {
                window.EFL.eventDispatcher.dispatch('premiumVideoAccess-postCrossDomainCheck');
            }
            */

            cookies.set(settings.cookie, 'true');
        }

        return false;
    }

    window.EFL.eventDispatcher.registerHandler('premiumVideoAccess-preCrossDomainCheck', function (video) {
        if (crossDomainCheck(video) === true) {
            $(document).ready(function () {
                window.EFL.eventDispatcher.dispatch('premiumVideoAccess-postCrossDomainCheck');
            });
        }
    });

    if ($(settings.trigger).length) {
        crossDomainCheck();
    }

    $(document).ready(triggerVideoAccess);
}
;
// Captures analytics during the signup process
(function () {
    'use strict';

    var allowMessage = true;
    var intendedOrigin = ".neulion.com";
    var isStepOneTracked = false;
    var promo1 = '', promo2 = '';

    self.addEventListener("message", receiveMessage, false);

    function receiveMessage(event) {
        try {
            var origin = event.origin || event.originalEvent.origin;

            if (allowMessage && origin.indexOf(intendedOrigin) !== -1) {
                var giftStatus = window.EFL.analyticsController.giftStatus();
                //console.log(event.data);

                var qs = getQueryStrings();
                var sku = qs['sku'];
                var matchid = qs['id'];
                var externalMatchId = qs['eid'];
                var cookies = window.EFL.local.cookies;
                var storage = window.EFL.local.storage;
                var token = storage.get(window.EFL.video.videoTokenName) || cookies.get(window.EFL.video.videoTokenName);
                // If we don't have stuff in local storage OR if the seoTrackingNameID does no equal the current package ID in the query string
                // Go off and get the SEO Name from Endeavor
                if (typeof externalMatchId !== 'undefined' && (window.sessionStorage.getItem("seoTrackingName") === null || window.sessionStorage.getItem("seoTrackingNameID") === null || window.sessionStorage.getItem("seoTrackingNameID") !== externalMatchId)) {
                    $.ajax({
                        url: window.EFL.video.apiUrl + 'game',
                        async: false,
                        data: {
                            extid: externalMatchId,
                            purchases: 'true'
                        },
                        headers: {
                            'Authorization': 'Bearer ' + token
                        },
                        dataType: 'json',
                        error: function (request) {
                        },
                        success: function (data) {
                            window.sessionStorage.setItem('seoTrackingName', data.seoName);
                            window.sessionStorage.setItem('seoTrackingNameID', externalMatchId);
                            processMessage();
                        }
                    });
                }
                else {
                    processMessage();
                }

                if (typeof event.data !== 'undefined' && typeof event.data.EFLpromoOptIn !== 'undefined') {
                    promo1 = event.data.EFLpromoOptIn;
                }

                if (typeof event.data !== 'undefined' && typeof event.data.clubpromoOptIn !== 'undefined') {
                    promo2 = event.data.clubpromoOptIn;
                }

                if (typeof event.data !== 'undefined' && typeof event.data.stepId !== 'undefined' && event.data.stepId >= 2) {
                    window.EFL.analyticsController.track({ 'subscription-package': getPassTypeFromSKU(sku) + ' Pass' + ' ' + getRegionFromSKU(sku) + ' pass' });
                }
            }
        }
        catch (e) {
            console.log("exception", e);
        }

        function processMessage() {
            //console.log(event);
            if (typeof sku !== 'undefined') {
                //console.log('sku is ' + cleanSKU(sku));
                var region = getRegionFromSKU(sku);
                //console.log('region is ' + region);
                var passType = getPassTypeFromSKU(sku);
                //console.log('type is ' + passType);
            }
            if (typeof matchid !== 'undefined') {
                //console.log('match id is ' + matchid);
            }
            if (typeof externalMatchId !== 'undefined') {
                //console.log('externalMatchId id is ' + externalMatchId);
            }
            if (typeof event.data !== 'undefined' && typeof event.data.stepId !== 'undefined' && typeof event.data.price !== 'undefined') {
                //new virtual page view url
                var maxStep = 3;
                var currentStep = parseInt(event.data.stepId) + 1;
                currentStep = currentStep <= maxStep ? currentStep : maxStep;
                var stepName = getStep(event.data.stepId);
                var passTypeUrl = passType.toLowerCase();
                passTypeUrl = passTypeUrl.replace(' ', '-');
                var regionUrl = region.toLowerCase();
                if (regionUrl === 'premier & national') {
                    regionUrl = 'premier-national';
                }
                else if (regionUrl === 'global free') {
                    regionUrl = 'global-free';
                }
                var trackUrl = '/club-tv/packages/' + passTypeUrl + '/' + regionUrl + '/step-' + currentStep + '/' + stepName + '/';
                //console.log('trackUrl is ' + trackUrl);
                window.EFL.analyticsController.pageView(trackUrl, 'Registration');
                //push into GA Enhanced Ecommerce
                //checkout
                var checkout = new Object();
                checkout.event = 'checkout';

                checkout['gift-status-hit'] = giftStatus;
                
                checkout.ecommerce = new Object();
                checkout.ecommerce.currencyCode = event.data.currency;
                checkout.ecommerce.checkout = new Object();
                checkout.ecommerce.checkout.actionField = new Object();
                checkout.ecommerce.checkout.actionField.step = currentStep;
                checkout.ecommerce.checkout.products = [];
                var product = new Object();
                product.name = getPassTypeFromSKU(sku) + ' Pass';
                product.id = cleanSKU(sku);
                product.price = event.data.price;
                product.brand = $('[data-analytics-club-name]').data('analytics-club-name');
                product.category = getRegionFromSKU(sku);
                if (window.sessionStorage.getItem("seoTrackingName") !== null && window.sessionStorage.getItem("seoTrackingName").length > 0) {
                    product.variant = window.sessionStorage.getItem("seoTrackingName");
                }
                else if (typeof matchid !== 'undefined') {
                    product.variant = matchid;
                }
                product.quantity = 1;

                product.dimension12 = giftStatus;
                
                checkout.ecommerce.checkout.products.push(product);
                //console.log(checkout);
                dataLayer.push(checkout);
                if (stepName === 'success') {
                    var purchase = new Object();
                    purchase.event = 'transaction';

                    purchase['gift-status-hit'] = giftStatus;
                    
                    purchase.ecommerce = new Object();
                    purchase.ecommerce.currencyCode = event.data.currency;
                    purchase.ecommerce.purchase = new Object();
                    purchase.ecommerce.purchase.actionField = new Object();
                    if (typeof event.data.orderId !== 'undefined' && event.data.orderId.length > 0) {
                        purchase.ecommerce.purchase.actionField.id = event.data.orderId;
                    }
                    if (cleanSKU(sku) === 'BASIC') {
                        //no real transaction id so need to add a fake one so that GA transaction is valid
                        //as its a required field and also make sure its unique
                        purchase.ecommerce.purchase.actionField.id = 'free' + new Date().getTime() + Math.floor(Math.random() * 10);
                    }
                    purchase.ecommerce.purchase.actionField.revenue = event.data.price;
                    purchase.ecommerce.purchase.products = [];
                    product = new Object();
                    product.name = getPassTypeFromSKU(sku) + ' Pass';
                    product.id = cleanSKU(sku);
                    product.price = event.data.price;
                    product.brand = $('[data-analytics-club-name]').data('analytics-club-name');
                    product.category = getRegionFromSKU(sku);
                    if (window.sessionStorage.getItem("seoTrackingName") !== null && window.sessionStorage.getItem("seoTrackingName").length > 0) {
                        product.variant = window.sessionStorage.getItem("seoTrackingName");
                    }
                    else if (typeof matchid !== 'undefined') {
                        product.variant = matchid;
                    }
                    product.quantity = 1;

                    product.dimension12 = giftStatus;
                    
                    purchase.ecommerce.purchase.products.push(product);
                    //console.log(purchase);
                    dataLayer.push(purchase);
                    // Remove those local storage items we created after a successful match purchase
                    window.sessionStorage.removeItem("seoTrackingName");
                    window.sessionStorage.removeItem("seoTrackingNameID");
                }
            }
            else {
                console.log("message received did not contain all of the required data");
                //report
            }
        }
    }

    if (typeof window.postMessage === 'undefined') {
        console.log("browser does not support postMessage");
    }

    function getPackage(id) {
        var packageName;
        switch (id) {
            case 0:
                packageName = "match";
                break;
            case 1:
                packageName = "month";
                break;
            case 2:
                packageName = "season";
                break;
            case 4:
                packageName = "free";
                break;
            case 5:
                packageName = "day";
                break;
            case 6:
                packageName = "week";
                break;
        }

        return packageName;
    }

    function getStep(id) {
        var stepName;
        switch (id) {
            case 0:
                stepName = "create-account";
                break;
            case 1:
                stepName = "payment-details";
                break;
            case 2:
                stepName = "success";
                break;
            case 3:
                stepName = "success";
                break;
        }

        return stepName;
    }

    function getQueryStrings() {
        var assoc = {};
        var decode = function (s) { return decodeURIComponent(s.replace(/\+/g, " ")); };
        var queryString = location.search.substring(1);
        var keyValues = queryString.split('&');

        for (var i in keyValues) {
            var key = keyValues[i].split('=');
            if (key.length > 1) {
                assoc[decode(key[0])] = decode(key[1]);
            }
        }

        return assoc;
    }

    function getPassTypeFromSKU(sku) {
        var passType;
        var modifiedSku = cleanSKU(sku);
        if (modifiedSku.indexOf('BASIC') >= 0) {
            passType = "Free";
        }
        else if (modifiedSku.indexOf("SEASON") >= 0) {
            passType = "Season";
        }
        else if (modifiedSku.indexOf("MONTH") >= 0) {
            passType = "Month";
        }
        else if (modifiedSku.indexOf("WEEK") >= 0) {
            passType = "Week";
        }
        else if (modifiedSku.indexOf("DAY") >= 0) {
            passType = "Day";
        }
        else if (modifiedSku.indexOf("MATCHA") >= 0) {
            passType = "Audio Match";
        }
        else if (modifiedSku.indexOf("MATCH") >= 0) {
            passType = "Match";
        }
        else {
            passType = "Unknown";
        }
        return passType;
    }

    function cleanSKU(sku) {
        //remove the first letter of the sku e.g. the P, B or G (Package, Bundle or Game indicator to get the raw sku)
        var modifiedSKU = sku;
        if (modifiedSKU.substring(0, 1) == 'P' || modifiedSKU.substring(0, 1) == 'G' || modifiedSKU.substring(0, 1) == 'B') {
            modifiedSKU = modifiedSKU.substring(1);
        }
        return modifiedSKU;
    }

    function getRegionFromSKU(sku) {
        var regionName;
        var modifiedSku = cleanSKU(sku);
        if (modifiedSku.indexOf('BASIC') >= 0) {
            regionName = "Global Free";
        }
        else if (modifiedSku.endsWith("PN")) {
            regionName = "Premier & National";
        }
        else if (modifiedSku.endsWith("D") || modifiedSku.endsWith("A")) {
            regionName = "Domestic";
        }
        else if (modifiedSku.endsWith("E") || modifiedSku.endsWith("I")) {
            regionName = "International";
        }
        else {
            regionName = "Unknown";
        }
        return regionName;
    }
}());;

    var settingsIncognito = {
        trigger: '[data-replace-incognito]'
    };



    function checkIncognito(element) {

        //https://gist.github.com/jherax/a81c8c132d09cc354a0e2cb911841ff1
        //Re-written without promises for IE11
        //Also, safari detection in above github no longer worked.  Order must be preserved so that Chrome detection is above Safari
        //As Chrome also returns safari in navigator string.  Github detection also relies on the webkit prefix.

        var isPrivate = false;
        
        function testLocalStorage(){
            try {
                if (localStorage.length) {
                    return false;
                }
                else {
                    localStorage.x = 1;
                    localStorage.removeItem('x');
                    return false;
                }
            } catch (e) {
                // Safari only enables cookie in private mode
                // if cookie is disabled then all client side storage is disabled
                // if all client side storage is disabled, then there is no point
                // in using private mode
                if (navigator.cookieEnabled) {
                    return true;
                } else {
                    return false;
                }
            }
        }

        // Chrome & Opera
        if (window.webkitRequestFileSystem){
            window.webkitRequestFileSystem(0,0, function () { setInfo(false, element); }, function () { setInfo(true, element); });
            return;
        }

        // Firefox
        if ('MozAppearance' in document.documentElement.style) {
     
            const db = indexedDB.open('test');

            db.onerror = function () {
                isPrivate = true;
                setInfo(isPrivate, element);
               
            };
            db.onsuccess = function () {

                isPrivate = false;
                setInfo(isPrivate, element);
            };

            return;  
        }

        // Safari
        var is_safari = navigator.userAgent.indexOf("Safari") > -1;
        if (navigator.userAgent.indexOf("Safari") > -1) {
            isPrivate = testLocalStorage();
            setInfo(isPrivate, element);
            return;
        }

        // IE10+ & Edge
        if (!window.indexedDB && (window.PointerEvent || window.MSPointerEvent)) {
            isPrivate = true;
            setInfo(isPrivate, element);
            return;
        }

        
}

    function setInfo(isPrivate, element) {
        if (isPrivate) {
            var warning = "<p>Sorry, but you cannot sign in or register using private browsing mode. Please switch off private browsing and then try again.</p>";
            //Just taking the first.  THere shouldn't be more than one per page anyway.

            element[0].innerHTML = warning;
        }
    }

    if (document.querySelectorAll(settingsIncognito.trigger).length > 0) { 
        checkIncognito(document.querySelectorAll(settingsIncognito.trigger));
    }
    ;
// SnapEngage script - applies the SnapEngage chat widget to any page where a video is present

$(function () {
    'use strict';

    var settings = {
        trigger: '[data-playvideo-id]',
        disable: '[data-snapengage-disable]',
        chat_link_text: 'iFollow Support'
    };

    function init() {

        //must be editing a block so don't show as variable not available
        if (typeof snapEngageClubReference === 'undefined') {
            return;
        }
        var se = document.createElement('script');
        se.type = 'text/javascript';
        se.async = true;
        se.src = '//storage.googleapis.com/code.snapengage.com/js/' + snapEngageClubReference + '.js';
        var done = false;

        se.onload = se.onreadystatechange = function () {
            if (!done && (!this.readyState || this.readyState === 'loaded' || this.readyState === 'complete')) {
                done = true;
                // Place your SnapEngage JS API code below
                // SnapEngage.allowChatSound(true); Example JS API: Enable sounds for Visitors.
            }
        };

        var s = document.getElementsByTagName('script')[0];
        s.parentNode.insertBefore(se, s);

        // Remove chat button - to be handled entirely by NeuLion (see EFLTECH-2348)
        // $('body').append(
        // 	'<a href="#" class="btn btn-primary snap-engage-link" data-snap-engage-link="">' + settings.chat_link_text + '</a>'
        // );
        //
        // $('[data-snap-engage-link]').on('click', function(e) {
        // 	e.preventDefault();
        // 	SnapEngage.startLink();
        // });
    }

    var hasInit = false;

    if ($(settings.trigger).length && !$(settings.disable).length) {
        if (window.EPiCookiePro !== null && typeof window.EPiCookiePro !== 'undefined') {

            if (window.EPiCookiePro.IsCookieCategoryAllowed(window.EPiCookiePro.ConsentCategories.LiveChat)) {
                hasInit = true;
                init();
            } else {
                var btn = document.getElementById('fakeChatButton');
                btn.style.display = 'block';
            }

            // Used by cookie scripts to initialise snapengage if it hasn't 
            $(document).on('initSnapEngage',
                function () {
                    if (!hasInit) {
                        hasInit = true;
                        init();
                    }
                });

        }
        else {
            init();
        }
    }
});;
window.EFL = window.EFL || {};
window.EFL.galleries = window.EFL.galleries || {};

window.EFL.galleries.loadGalleries = function () {

    if ($('.fotorama-gallery').length) {
        var env = findBootstrapEnvironment(); //returns ["xs", "sm", "md", "lg", "xl"];
        var container = $('.fotorama-gallery');
        var currentSize = container.attr('data-size') || "sm";

        sizeFotorama(container);

        var changeSize = function () {
            function isHidden(el) {
                return el && el.offsetWidth === 0 && el.offsetHeight === 0;
            }

            if (!isHidden(container[0])) {
                sizeFotorama(container);
            }


            /*
            env = findBootstrapEnvironment();
            currentSize = container.attr('data-size');
            if (currentSize !== env) {
                container.each(function () {
                    var fotorama = $(this).data('fotorama');
                    if (fotorama) {
                        $(this).data('fotorama').destroy();
                    }
                });
                sizeFotorama(container);
            }*/
        };

        //on resize, if enviroment has changed kill all fotorama and redraw.
        $(window).on('throttled-resize', function () {
            changeSize();
        });

        //resizing when navtab is shown to prevent bug where throttled resize is called when gallery is hidden making galley tiny. 
        $('.nav-tabs').on('shown.bs.tab', function () {
            setTimeout(function () {
                changeSize();
            }, 500);
        });
    }



    //size fotorama images based on screensize then initialize fotorama
    function sizeFotorama(container) {
        container.find('a').each(function () {
            var image = $(this);
            if (env === "lg" || env === "xl") {
                image.attr('href', image.attr('data-href-lg'));
                //set thumbnail margin and change any other atributes as required.
                container.attr('data-thumbmargin', '40');

            } else if (env === "md") {
                image.attr('href', image.attr('data-href-md'));
                container.attr('data-thumbmargin', '30');
            } else {
                image.attr('href', image.attr('data-href-sm'));
                container.attr('data-thumbmargin', '10');
            }
        });

        initFotorama();
    }

    var myFotorama = false;

    //initialises fotorama
    function initFotorama() {
        if (myFotorama) {
            myFotorama.destroy();
        }

        $('.fotorama-gallery').on('fotorama:load', function (e, fotorama) {
            var element = $(this);
            var active = element.find('.fotorama__active');
            var caption = active.find('.fotorama__caption');
            var image = active.find('img');
            image.attr('alt', ' ');
            var activeFrame = fotorama.activeFrame;

            if (caption.find('.credit').length === 0 && activeFrame.credit !== "") {

                caption.append(
                    '<div class="credit">' + activeFrame.credit + '</div>'
                );
                caption.attr('tabindex', '0');
            }

            element.find('.fotorama__img').attr('tabindex', '-1');


        }).on('fotorama:ready', function (e, fotorama) {
            myFotorama = fotorama;

            var element = $(this);
            //add text to thumbnails
            var i = 1;
            element.find('.fotorama__nav__frame--thumb').each(function () {
                $(this).attr('aria-label', 'thumbnail ' + i);
                i++;
            });

            lookForDisabledButtons(element);

            //hide video close button from screen reader/tab on mobile
            var video = element.find('.fotorama__video-close');
            video.attr('tabindex', '-1');
            video.attr('aria-hidden', 'true');

        }).on('fotorama:showend', function (e, fotorama) {
            var element = $(this);
            lookForDisabledButtons(element);

        }).fotorama();

        function lookForDisabledButtons(element) {

            element.find('.fotorama__arr--prev, .fotorama__arr--next').each(function () {

                if ($(this).hasClass('fotorama__arr--disabled')) {
                    $(this).attr("aria-disabled", "true");
                } else {
                    $(this).attr("aria-disabled", "false");
                }
            });
        }
    }
};


(function ($) {
    window.EFL.galleries.loadGalleries();
})(jQuery);;
(function() {
    'use strict';

    //fixed first column table
    if ($('table.fixed').length > 0) {

        $('table.fixed').each(function(index, value) {
            var fixedCol, element, table, caption;

            table = $(this);
            table.wrap('<div class="table-wrap clearfix"></div>');
            $(this).addClass('cloned');

            var caption = "";

            if (table.find('caption').length > 0) {
                table.find('caption').html();
            }

            $("<table class='fixed-column-clone scroll-table' aria-hidden='true'></table>").insertBefore(table);

            fixedCol = table.prev('.fixed-column-clone');
            fixedCol.append('<thead><tr></tr></thead>');

            element = table.find('thead td:first-child').clone();
            fixedCol.find('thead tr').append(element);
            
            table.find('thead td:first-child').addClass('sr-only');
            table.find('caption').addClass('sr-only');

            fixedCol.append('<tbody></tbody>');

            table.find('tbody tr td:first-child').each(function() {
                element = $(this).clone();
                $(this).addClass('sr-only');
                fixedCol.append('<tr></tr>');
                fixedCol.find('tbody tr:last-child').append(element);

            });

            if (caption.length !== "") {
                // calculate width of caption contianer so caption is always centered with table
                // width of cloned table + width of fixed column
                var width = $(this).find('tbody').width() + fixedCol.width();

                $('<div>', {
                    class: 'caption-replacement-container',
                    width: width + 'px',
                    html: '<p id="caption-replacement-' + index + '">' + caption + '</p>'
                }).insertBefore($('.table-wrap'));

                table.attr('aria-describedby', 'caption-replacement-' + index);
            }

            setMargin();

            function setMargin() {
                var colWidth = fixedCol.width();
                table.css('margin-left', colWidth);
            }

            $(window).on('throttled-resize', setMargin);

        });
    }

    //eq heights for th/td rows in fixed first colum
    if ($('table.fixed').length > 0) {
        $('table.fixed').each(function() {
            var table = $(this);
            var cloned = table.parent().find('.fixed-column-clone');
			$(window).on('throttled-resize', function(){
				eqRows(table, cloned);
			});

            if (table.is(":visible")) {
				eqRows(table, cloned);

            }
        });
    }

	function rowCheck(row, cloneRow, selector, counter) {
		setRowHeight(row, selector, 'auto');
		setRowHeight(cloneRow, selector, 'auto');
		var rowHeight = findRowHeight(row, selector);
		var cloneRowHeight = findRowHeight(cloneRow, selector);


		if (rowHeight > cloneRowHeight) {
			setRowHeight(cloneRow, selector, rowHeight);
		} else if (cloneRowHeight > rowHeight) {
			setRowHeight(row, selector, cloneRowHeight);
		}

	}

	function eqRows(table, cloned){
		table.find('thead tr').each(function(index) {
			var row = $(this);
			var cloneRow = cloned.find('thead tr').eq(index);
			rowCheck(row, cloneRow, 'td', index);
		});

		table.find('tbody tr').each(function(index) {
			var row = $(this);
			var cloneRow = cloned.find('tbody tr').eq(index);
			rowCheck(row, cloneRow, 'td', index);
		});
	}

    function findRowHeight(row, selector) {
        var height = 0;
        row.find(selector).each(function() {
            height = Math.max($(this).height(), height);
        }).height(height);

        return height;
    }

    function setRowHeight(row, selector, height) {
        row.find(selector).each(function() {
            $(this).height(height);
        });
    }

    if ($('table.flip').length > 0) {
		var table = $('table.flip');

		table.find('th').each(function (index) {
			var rowHead = $(this);
        	var cols = table.find('tr');
			var rowNumber = index;

			var rowHeight = rowHead.height();

			cols.each(function(){
				var cell = $(this).find('td').eq(rowNumber);
				if (cell.height() > rowHeight ){
					rowHeight = cell.height();
				}
			});

			rowHead.height(rowHeight);
			cols.each(function(){
				$(this).find('td').eq(rowNumber).height(rowHeight);
			});


		});

	}


}());
;
/*
This script looks for iframes in the editorial content area, interogates the url and adds apropriate wrappers.

*/

(function ($) {
	'use strict';

	var settings = {
		trigger_container: '.news-article-body, .content-block-wrapper, .commentary-container',
		trigger: 'iframe'
	};

	function update(refresh) {

		$(settings.trigger_container).find(settings.trigger).each(function () {
			var $context = $(this),
				url = $context.attr('src'),
				wrapClass = 'iframe-wrap',
				title = 'Embedded content',
				is_tweet = $context.attr('id') ? $context.attr('id').match(/twitter/) : false,
				is_facebook_post = false,
				is_facebook_video = false;

			if (url) {

				if (url.match(/(youtube\.com|youtu\.be)/)) {
					wrapClass = 'youtube-wrap';
					title = 'Embedded content from Youtube';
				}
				else if (url.match(/(google.*maps)/)) {
					wrapClass = 'google-map-wrap';
					title = 'Embedded content from Google Maps';
				}
				else if (url.match(/facebook\.com/)) {
					is_facebook_post = true;
					title = 'Embedded content from Facebook';

					if (url.match(/video\.php/)) {
						is_facebook_video = true;
						wrapClass = 'facebook-video-wrap';
						title = 'Embedded video from Facebook';
					}
				}
			}

			// if the iframe is within a Club Commentary entry, add the wrapper class to the .embed container
			if ($context.closest('.commentary-entry').length) {
				$context.closest('.embed').addClass(wrapClass);

				if (is_facebook_post && !is_facebook_video) {
					$context.addClass('facebook-iframe');
				}
			}
			else if (!$context.parent().hasClass(wrapClass) && !is_tweet) {
				// don't wrap the iframe if it is a Twitter tweet (this was causing Twitter to clear the content of the iframe on iOS and macOS Safari)
				$context.unwrap('p');
				$context.wrap('<div class="' + wrapClass + '"></div>');
			}

			$context.attr('title', title);
		});

		// attempt to refresh any existing iframes
		if (refresh) {
			try {
				window.instgrm.Embeds.process();
			}
			catch (e) {}
		}

	}

	function init() {
		window.EFL.eventDispatcher.registerHandler('update-iframes', function() {
			update(true);
		});

		update();
	}

	init();

})(window.jQuery);
;
/*
	Adition Controller
	------------------

	Functions can be accessed externally via the window.EFL.adition.functions object (see the bottom of the script for these functions).


	Example Adition element:

		<div data-adition="MPUMobile" data-adition-group="triple-mpu-mobile-1-a" class="visible-xs-inline-block"></div>


	The ad type passed in data-adition="" must be an ad type as defined in AditionAdTypes within AdHelper.cs

	data-adition-group and class attributes are optional (the latter being used to control the viewport(s) on which the ad appears)
*/

window.EFL = window.EFL || {};
window.EFL.adition = window.EFL.adition || {};

window.EFL.adition.functions = (function($, EFLAdition) {
	'use strict';

	var global_settings = {
		adfarm: 'adfarm1.adition.com',
		error_prefix: 'EFL Adition',
		triggers: {}
	};

	// Async load the Adition codebase
	var loadAdition = function() {
		(function() {
			var script = document.createElement('script');
			script.type = 'text/javascript';
			script.src = (document.location.protocol === 'https:' ? 'https:' : 'http:') + '//imagesrv.adition.com/js/srp.js';
			script.charset = 'utf-8';
			script.async = true;

			var firstScript = document.getElementsByTagName('script')[0];
			firstScript.parentNode.insertBefore(script, firstScript);
		}());

		window.adition.srq.push(function(api) {
			api.registerAdfarm(global_settings.adfarm);

			// Set section and subsection, if available
			if (window.EFL && EFLAdition && EFLAdition.profiles.section) {
				api.setProfile('section', EFLAdition.profiles.section);

				if (EFLAdition.profiles.subsection) {
					api.setProfile('subsection', EFLAdition.profiles.subsection);
				}
            }

            //set child-friendly adds if applicable
            if (window.EFL && EFLAdition && EFLAdition.profiles.childfriendly) {
                api.setProfile('excl', 'children');
            }
		});
	};

	// Determine if a DOM element is visible
	// (won't work for fixed position elements)
	var isElementHidden = function(element) {
		return ($(element)[0].offsetParent === null);
	};

	// Log an error if error logging is enabled
	var logError = function(error_message, function_name) {
		function_name = function_name ? function_name + ' - ' : '';
		console.error(global_settings.error_prefix + ' - ' + function_name + error_message);
	};

	// If a valid instance is found, render the ad
	var loadInstance = function(element_id, ad_type, group) {
		var ad_id = getAdID(ad_type),
			instanceFound = false;

		if (ad_id) {
			instanceFound = true;

            window.adition.srq.push(function (api) {

                if (group) {
                    api
                        .configureRenderSlot(element_id)
                        .setProfile('group', group)
                        .setContentunitId(ad_id);
                }
                else {
                    api
                        .configureRenderSlot(element_id)
                        .setContentunitId(ad_id);
                }

                api
                    .renderSlot(element_id);
            });
		}

		return instanceFound;
	};

	// Checks for any unprocessed ad instances on the page and processes them
	// (only ads that are visible on the page on processed, see isElementHidden() for more info)
	var checkForInstances = function() {
		var $trigger = $(global_settings.triggers.main),
			instances = [];

		if ($trigger.length && !$(global_settings.triggers.disable).length) {

			$trigger.each(function() {
				var $element = $(this),
					element_id = $element.attr('id'),
					ad_type = $element.data('adition'),
                    group = '',
					instanceLoaded = false;

				// If an ad element is visible
				if (!isElementHidden(this)) {

					// Ensure the DOM element has an ID
					if (!element_id) {
						$element.attr('id', 'adition-instance-' + Math.floor(Math.random() * 1000000));
						element_id = $element.attr('id');
					}

                    group = $element.data('adition-group');

					// Load the ad
                    instanceLoaded = loadInstance(element_id, ad_type, group);

					// If the instance loaded successfully, process it
					if (instanceLoaded) {
						instances.push(element_id);

						// Stop it from getting checked again, and add an identifying class
						$element.removeAttr('data-adition').addClass('adition-instance adition-' + ad_type);
					}
					else {
						// Otherwise, remove the Ad
						$element.remove();
					}
				}
			});

		}

		// if instances are found, load them
		if (instances.length) {
			window.adition.srq.push(function(api) {
				api.load(instances).completeRendering();
			});
		}
	};

	// Adition event handlers are registered within this function
	var registerEventHandlers = function() {
		window.adition.srq.push(function(api) {

			// Add a unique iframe class onPostRender()
			api.events.onPostRender(function(instance) {
				$(instance).find('iframe').addClass('adition-iframe');
			});
		});
	};

	// Checks to see if the passed ad_type exists within window.EFL.adition.ad_types
	var adTypeExists = function(ad_type) {
		return EFLAdition.ad_types && EFLAdition.ad_types.hasOwnProperty(ad_type);
	};

	// Returns an ad ID for the passed ad_type
	var getAdID = function(ad_type) {
		if (!adTypeExists(ad_type)) {
			return undefined;
		}

		return EFLAdition.ad_types[ad_type];
	};

	var getScreenreaderText = function(is_ad_group_block) {
		return '<p class="sr-only">' + (is_ad_group_block ? 'Advertisement block' : 'Advertisement') + '</p>';
	};

	/*
	Generate an Ad element
	(NOTE: the ad will not render until the ad has been inserted into the page and checkForInstances() is called)

	settings:
		ad_type: the ad_type defined in window.EFL.adition.ad_types (REQUIRED)
		display: 'inline' / 'inline-block' (defaults to 'block' if nothing is specified)
		visibility: 'mobile' / 'tablet' / 'tablet-desktop' / 'desktop' / 'desktop-large' (defaults to rendering on all viewports if nothing is specified)
		group: A key value used to group adverts together (e.g. grouping three MPU's to ensure duplicate ads aren't rendered)
		insert_screenreader_text: Prepends screenreader text to the containing element (disabled by default)
	*/
	var generateAd = function(settings) {
		var visibility_classes = '',
			display_class = '',
			group = '';

		if (!settings || $.isEmptyObject(settings)) {
			logError('No settings defined.', 'generateAd()');
			return;
		}

		if (!settings.hasOwnProperty('ad_type') || !settings.ad_type) {
			logError('Specify an ad type.', 'generateAd()');
			return;
		}

		if (!adTypeExists(settings.ad_type)) {
			logError('Ad type does not exist.', 'generateAd()');
			return;
		}

		if (settings.group) {
			group = ' data-adition-group="' + settings.group + '"';
		}

		if (settings.display) {
			switch (settings.display) {
				case 'inline':
				case 'inline-block':
					display_class = '-' + settings.display;
					break;
			}
		}

		if (settings.visibility) {

			switch (settings.visibility) {
				case 'mobile':
					visibility_classes = 'visible-xs' + display_class;
					break;

				case 'tablet':
					visibility_classes = 'visible-sm' + display_class;
					break;

				case 'tablet-desktop':
					visibility_classes = 'visible-sm' + display_class +' visible-md' + display_class;
					break;

				case 'desktop':
					visibility_classes = 'visible-md' + display_class +' visible-lg' + display_class;
					break;

				case 'desktop-large':
					visibility_classes = 'visible-lg' + display_class;
					break;
			}

			visibility_classes = visibility_classes ? ' class="' + visibility_classes + '"' : '';
		}

		if (settings.insert_screenreader_text) {
			return $(getScreenreaderText() + '<div data-adition="' + settings.ad_type + '"' + group + visibility_classes + ' />');
		}

		return $('<div data-adition="' + settings.ad_type + '"' + group + visibility_classes + ' />');
	};

	/*
	Generate and insert an Ad into the passed jQuery element

	settings:
		$element: The jQuery element that the ad is to be inserted into (REQUIRED)
		ad_type: Passed to the generateAd() function (see generateAd() for more information) (REQUIRED)
		position: Element insert position ('prepend', 'append', 'before', 'after' - defaults to 'append' if no position is specified)
		check_for_instances: boolean value to indicate whether or not to check for new instances after inserting the ad (DEFAULT: true)
		visibility: See generateAd() for more information
		display: See generateAd() for more information
		group: See generateAd() for more information
	*/
	var insertAd = function(settings) {
		var $advert;

		if (!settings || $.isEmptyObject(settings)) {
			logError('No settings defined.', 'insertAd()');
			return;
		}

		if (!(settings.hasOwnProperty('$element') && (settings.$element instanceof $))) {
			logError('Specify a valid element.', 'insertAd()');
			return;
		}

		if (settings.$element.length) {

			settings.check_for_instances = typeof(settings.check_for_instances) === 'boolean' ? settings.check_for_instances : true;
			settings.position = settings.position || 'append';

			$advert = generateAd({
				ad_type: settings.ad_type,
				display: settings.display,
				visibility: settings.visibility,
				group: settings.group
			});

			if ($advert.length) {

				insertElement(settings.$element, $advert, settings.position);

				// Check for new Ad instances
				if (settings.check_for_instances) {
					checkForInstances();
				}
			}
		}
	};

	/*
	Insert an array of $ads into the passed jQuery $element
	Ads are wrapped within a container of div.adition-container

	settings:
		$element: The jQuery element that the ads are to be inserted into
		$ads: An array of $ad elements generated from the generateAd() function (or window.EFL.adition.functions.generate_ad() if called externally)
		position: Element insert position ('prepend', 'append', 'before', 'after' - defaults to 'append' if no position is specified)
		check_for_instances: boolean value to indicate whether or not to check for new instances after inserting the ad (DEFAULT: true)
		insert_screenreader_text: Prepends screenreader text to the containing element (disabled by default)
		is_group: Determines whether or not a group of ads per viewport are to be inserted (affects which screenreader text is displayed) (DEFAULT: false)
	*/
	var insertAds = function(settings) {
		var $container;

		if (!settings || $.isEmptyObject(settings)) {
			logError('No settings defined.', 'insertAds()');
			return;
		}

		if (!(settings.hasOwnProperty('$element') && (settings.$element instanceof $))) {
			logError('Specify a valid element.', 'insertAds()');
			return;
		}

		if (!(settings.hasOwnProperty('$ads') && (settings.$ads instanceof Array))) {
			logError('Specify a valid $ads array.', 'insertAds()');
			return;
		}

		if (settings.$element.length && settings.$ads.length) {

			$container = $('<div class="adition-container" />');

			if (settings.insert_screenreader_text) {
				$container.prepend(getScreenreaderText(settings.is_group));
			}

			settings.check_for_instances = typeof(settings.check_for_instances) === 'boolean' ? settings.check_for_instances : true;

			$.each(settings.$ads, function(index, $ad) {

				if (!($ad instanceof $)) {
					logError('Specify a valid $ad element', 'insertAds()');
					return;
				}

				$container.append($ad);
			});

			insertElement(settings.$element, $container, settings.position);

			// Check for new Ad instances
			if (settings.check_for_instances) {
				checkForInstances();
			}
		}
	};

	// Inserts an $element_to_insert to the defined position of an $element
	// Position defaults to .append() if no position is defined
	var insertElement = function($element, $element_to_insert, position) {
		switch (position) {
			case 'prepend':
				$element.prepend($element_to_insert);
				break;

			case 'before':
				$element.before($element_to_insert);
				break;

			case 'after':
				$element.after($element_to_insert);
				break;

			default:
				$element.append($element_to_insert);
		}
	};

	// Reloads a new set of ads
	// Optionally pass an array of element ID's to target specific ads
	// ID's should be the id attribute of a div.adition-instance element (with no # prefix)
	var reloadAds = function(instances) {
		window.adition.srq.push(function(api) {
			if (instances instanceof Array) {
				api.load(instances).completeRendering();
			}
			else {
				api.load().completeRendering();
			}
		});
	};

	var init = function(main_trigger, disable_trigger) {

		if (!main_trigger || !disable_trigger) {
			logError('Triggers not defined.', 'init()');
			return;
		}

		// Setup callback queue
		window.adition = window.adition || {};
		window.adition.srq = window.adition.srq || [];

		// Set triggers
		global_settings.triggers.main = main_trigger;
		global_settings.triggers.disable = disable_trigger;

		loadAdition();
		registerEventHandlers();
	};

	return {
		initialize: init,
		check_for_instances: checkForInstances,
		generate_ad: generateAd,
		insert_ad: insertAd,
		insert_ads: insertAds,
		reload_ads: reloadAds
	};

})(
	window.jQuery,
	window.EFL.adition
);;
/*
	Adition advertising script

	Ads can be injected to the page with an attribute of data-adition="{AdType}" where {AdType} is one of the window.EFL.adition.ad_types defined within AditionInfo.cshtml

	The script also programatically injects ads to containers with block counters defined (currently used on the OneColumn and Home templates).
*/

(function($, EFLAdition) {
	'use strict';

	var settings = {
		triggers: {
			main: '[data-adition]',
			disable: '[data-adition-disable]',
			block_counter: {
				general: '[data-adition-block-counter]',
				trigger: '[data-adition-block-counter=trigger]',
				count: '[data-adition-block-counter=count]'
			}
		},
		ad_counters: {
			leaderboard: 0,
			mpu: 0
		},
		ad_frequency: 2
	};

	// Block counter ad insertion logic
	var insertBlockCountAdverts = function($container) {
		var count = 0,
			counter_mode = $container.data('adition-block-counter-mode'),
			$count_blocks,
			$invalid_count_blocks,
			$blocks;

		if (counter_mode) {
			$count_blocks = $container.find(settings.triggers.block_counter.count);
			$invalid_count_blocks = $container.find(settings.triggers.block_counter.general + ' ' + settings.triggers.block_counter.count);
			$blocks = $count_blocks.not($invalid_count_blocks);

			$blocks.each(function(index, block) {
				var $block = $(block),
					$content_block_wrapper = $block.closest('.content-block-wrapper'),
					page_layout = 'default';

				count++;

				// if the content block is within a content block wrapper, set the block's page_layout
				if ($content_block_wrapper.length && ($content_block_wrapper.hasClass('content-block-wrapper-container-left') || $content_block_wrapper.hasClass('content-block-wrapper-container-center'))) {
					if ($content_block_wrapper.hasClass('content-block-wrapper-container-left')) {
						page_layout = 'left';
					}
					else if ($content_block_wrapper.hasClass('content-block-wrapper-container-center')) {
						page_layout = 'center';
					}
				}

				switch(counter_mode) {
					case 'home-page':
						homePageBlockCountInsert($block, count, page_layout);
						break;
					case 'one-column':
						oneColumnBlockCountInsert($block, count, page_layout);
						break;
				}
			});
		}
	};

	// Ad insertion logic for the home page template
	var homePageBlockCountInsert = function($block, count, page_layout) {

		var generated_ad_settings = {};

		// Leaderboard
		if (count === (2 * settings.ad_frequency)) {
			generated_ad_settings = generateLeaderboard(page_layout);
		}
		// MPU
		else if (count % settings.ad_frequency === 0) {
			generated_ad_settings = generateMPUs(page_layout);
		}

		// Insert the Ads
		if (count % settings.ad_frequency === 0) {

			EFLAdition.functions.insert_ads({
				$element: $block,
				position: 'after',
				insert_screenreader_text: true,
				$ads: generated_ad_settings.$ads,
				is_group: generated_ad_settings.is_group
			});
		}
	};

	// Ad insertion logic for the one column template
	var oneColumnBlockCountInsert = function($block, count, page_layout) {

		var generated_ad_settings = {};

		// Leaderboard
		if ((count % (settings.ad_frequency * 2)) === 0) {
			generated_ad_settings = generateLeaderboard(page_layout);
		}
		// MPU
		else if (count % settings.ad_frequency === 0) {
			generated_ad_settings = generateMPUs(page_layout);
		}

		// Insert the Ads
		if (count % settings.ad_frequency === 0) {

			EFLAdition.functions.insert_ads({
				$element: $block,
				position: 'after',
				insert_screenreader_text: true,
				$ads: generated_ad_settings.$ads,
				is_group: generated_ad_settings.is_group
			});
		}
	};

	// Generate either groups of Leaderboards or MPUs, depending on the page layout
	// Returns an ad_settings object with an array of $ads, and an is_group boolean, depending on the passed page_layout (left/center/default)
	var generateLeaderboard = function(page_layout) {

		var ad_settings = {};

		ad_settings.$ads = [];

		switch(page_layout) {
			case 'center':
				settings.ad_counters.mpu++;
				ad_settings.$ads = generateSingleMPU('block', settings.ad_counters.mpu);
				ad_settings.is_group = false;
				break;

			case 'left':
				settings.ad_counters.mpu++;
				ad_settings.$ads = generateDoubleMPU('inline-block', settings.ad_counters.mpu);
				ad_settings.is_group = true;
				break;

			default:
				settings.ad_counters.leaderboard++;

				ad_settings.$ads.push(EFLAdition.functions.generate_ad({
					ad_type: 'LeaderboardDesktop',
					visibility: 'desktop',
					group: settings.ad_counters.leaderboard ? 'leaderboard-desktop-' + settings.ad_counters.leaderboard : false
				}));

				ad_settings.$ads.push(EFLAdition.functions.generate_ad({
					ad_type: 'LeaderboardTablet',
					visibility: 'tablet',
					group: settings.ad_counters.leaderboard ? 'leaderboard-tablet-' + settings.ad_counters.leaderboard : false
				}));

				ad_settings.$ads.push(EFLAdition.functions.generate_ad({
					ad_type: 'LeaderboardMobile',
					visibility: 'mobile',
					group: settings.ad_counters.leaderboard ? 'leaderboard-mobile-' + settings.ad_counters.leaderboard : false
				}));

				ad_settings.is_group = false;
		}

		return ad_settings;
	};

	// Generate groups of MPUs depending on the page layout
	// Returns an ad_settings object with an array of $ads, and an is_group boolean, depending on the passed page_layout (left/center/default)
	var generateMPUs = function (page_layout) {

		settings.ad_counters.mpu++;

		var ad_settings = {};

		switch(page_layout) {
			case 'center':
				ad_settings.$ads = generateSingleMPU('block', settings.ad_counters.mpu);
				ad_settings.is_group = false;
				break;

			case 'left':
				ad_settings.$ads = generateDoubleMPU('inline-block', settings.ad_counters.mpu);
				ad_settings.is_group = true;
				break;

			default:
				ad_settings.$ads = generateTripleMPU('inline-block', settings.ad_counters.mpu);
				ad_settings.is_group = true;
		}

		return ad_settings;
	};

	// Generates a block of single MPU's on desktop, tablet and mobile
	var generateSingleMPU = function(display, group) {
		var $ads = [];

		$ads.push(EFLAdition.functions.generate_ad({
			ad_type: 'MPUDesktop',
			visibility: 'desktop',
			display: display,
			group: group ? 'mpu-desktop-' + group : false
		}));

		$ads.push(EFLAdition.functions.generate_ad({
			ad_type: 'MPUTablet',
			visibility: 'tablet',
			display: display,
			group: group ? 'mpu-tablet-' + group : false
		}));

		$ads.push(EFLAdition.functions.generate_ad({
			ad_type: 'MPUMobile',
			visibility: 'mobile',
			display: display,
			group: group ? 'mpu-mobile-' + group : false
		}));

		return $ads;
	};

	// Generates a block of two MPU's on desktop (+ two on tablet, and one on mobile)
	var generateDoubleMPU = function(display, group) {
		var $ads = [];

		// Generate desktop ads
		$ads.push(EFLAdition.functions.generate_ad({
			ad_type: 'MPUDesktop',
			visibility: 'desktop-large',
			display: display,
			group: group ? 'double-mpu-desktop-' + group + '-a' : false
		}));

		$ads.push(EFLAdition.functions.generate_ad({
			ad_type: 'MPUDesktop',
			visibility: 'desktop-large',
			display: display,
			group: group ? 'double-mpu-desktop-' + group + '-b' : false
		}));

		// Generate tablet ads
		$ads.push(EFLAdition.functions.generate_ad({
			ad_type: 'MPUTablet',
			visibility: 'tablet-desktop',
			display: display,
			group: group ? 'double-mpu-tablet-' + group + '-a' : false
		}));

		// Generate mobile ads
		$ads.push(EFLAdition.functions.generate_ad({
			ad_type: 'MPUMobile',
			visibility: 'mobile',
			display: display,
			group: group ? 'double-mpu-mobile-' + group + '-a' : false
		}));

		return $ads;
	};

	// Generates a block of three MPU's on desktop (+ two on tablet, and one on mobile)
	var generateTripleMPU = function(display, group) {
		var $ads = [];

		// Generate desktop ads
		$ads.push(EFLAdition.functions.generate_ad({
			ad_type: 'MPUDesktop',
			visibility: 'desktop',
			display: display,
			group: group ? 'triple-mpu-desktop-' + group + '-a' : false
		}));

		$ads.push(EFLAdition.functions.generate_ad({
			ad_type: 'MPUDesktop',
			visibility: 'desktop',
			display: display,
			group: group ? 'triple-mpu-desktop-' + group + '-b' : false
		}));

		$ads.push(EFLAdition.functions.generate_ad({
			ad_type: 'MPUDesktop',
			visibility: 'desktop',
			display: display,
			group: group ? 'triple-mpu-desktop-' + group + '-c' : false
		}));

		// Generate tablet ads
		$ads.push(EFLAdition.functions.generate_ad({
			ad_type: 'MPUTablet',
			visibility: 'tablet',
			display: display,
			group: group ? 'triple-mpu-tablet-' + group + '-a' : false
		}));

		$ads.push(EFLAdition.functions.generate_ad({
			ad_type: 'MPUTablet',
			visibility: 'tablet',
			display: display,
			group: group ? 'triple-mpu-tablet-' + group + '-b' : false
		}));

		// Generate mobile ads
		$ads.push(EFLAdition.functions.generate_ad({
			ad_type: 'MPUMobile',
			visibility: 'mobile',
			display: display,
			group: group ? 'triple-mpu-mobile-' + group + '-a' : false
		}));

		return $ads;
	};

	var init = function() {
		var $block_counter_trigger;

		// Enable Adition advertising if the main trigger, or block counter triggers are present
		// Disable advertising on the page entirely if a 'disable' block is present anywhere on the page
		if (EFLAdition && EFLAdition.functions && ($(settings.triggers.main).length || $(settings.triggers.block_counter.trigger).length) && !$(settings.triggers.disable).length) {

			EFLAdition.functions.initialize(settings.triggers.main, settings.triggers.disable);

			// If block counter elements are present, process them
			$block_counter_trigger = $(settings.triggers.block_counter.trigger);

			if ($block_counter_trigger.length && $block_counter_trigger.find(settings.triggers.block_counter.count).length >= settings.ad_frequency) {
				insertBlockCountAdverts($block_counter_trigger);
			}

			EFLAdition.functions.check_for_instances();

			$(window).on('throttled-resize orientationchange', function() {
				EFLAdition.functions.check_for_instances();
			});
		}
	};

	if (window.EPiCookiePro != null && typeof window.EPiCookiePro !== 'undefined') {

		if (window.EPiCookiePro.IsCookieCategoryAllowed(window.EPiCookiePro.ConsentCategories.Targeting)) {
			init();
		}

	}
	else {
		init();
    }
	
})(
	window.jQuery,
	window.EFL.adition
);;
window.EFL = window.EFL || {};

(function($, EFLAdition) {
	'use strict';

	var settings = {
		trigger: '[data-adition-scroll-lock]',
		disable: '[data-adition-disable]',
		$scroll_lock_container: $('.adition-scroll-lock-container').not($('.adition-scroll-lock-container .adition-scroll-lock-container')),
		header_offset: 50,
		top_offset: 0,
		scroll_lock_on: -1,
		scroll_lock_off: -1,
		is_match_centre: false,
        ads_exceed_viewport_height: true,
        delay:1000
	};

	// Sets the on/off positions
	var setPositions = function($container) {
		settings.scroll_lock_on = getOnPosition($container);
        settings.scroll_lock_off = getOffPosition($container);
	};

	// Gets the 'on' position (to determine when to set the ad to a fixed position)
	var getOnPosition = function($container) {
		var offset = 0,
			$site_header = $('.site-header'),
			nav_wrapper_height = 0;

		if (settings.is_match_centre) {

			nav_wrapper_height = $('.nav-wrapper').outerHeight();
			settings.header_offset = 100;

			if ($('.sticky-hero').length) {
				settings.top_offset = $('.widget-match-header-mini').outerHeight() + nav_wrapper_height;
				offset = $container.find('.adition-scroll-lock-trigger').offset().top - settings.top_offset;
			}
			else {
				settings.top_offset = settings.$scroll_lock_container.offset().top;
				offset = settings.top_offset;
			}
		}
		else {
			settings.header_offset = 50;

			if ($site_header.hasClass('scroll-lock')) {
				settings.top_offset = $site_header.find('.header-wrap').outerHeight();
				offset = $container.find('.adition-scroll-lock-trigger').offset().top - settings.top_offset;
			}
			else {
				settings.top_offset = $site_header.outerHeight(true);
				offset = settings.top_offset;
			}
		}

		return offset;
	};

	// Gets the 'off' position (to determine when to pin the ad to the bottom of the container)
	var getOffPosition = function($container) {
		return Math.floor( $container.offset().top + $container.height() );
	};

	// Checks to see if the advert column's height exceed's the primary column's height
	var adColumnHeightExceedsPrimary = function($container) {

		var $primary_column = $container.find('.primary-column'),
			$secondary_column = $container.find('.secondary-column');

		// If the secondary column exceeds the height of the primary, revert to the default ad behaviour
		if ($secondary_column.outerHeight() >= $primary_column.outerHeight()) {
			return true;
		}

		return false;
	};

	// Show/hide an ad
	var showAd = function($trigger, show, type) {
		var $ad = $trigger.find('.adition-scroll-lock-' + type);

		if ($ad.length) {
			if (show) {
				$ad.removeClass('hidden');
			}
			else {
				$ad.addClass('hidden');
			}
		}
	};

	// Sets which ad types are visible depending on the viewport height
	var setVisibleAds = function($trigger, $container) {

		var viewport_height = $(window).height(),
			available_height = 0,
			ad_top_margin = 20,
			mpu_single_height = 250 + ad_top_margin,
			mpu_double_height = 600 + ad_top_margin;

		// If the secondary column exceeds the height of the primary, show the double MPU
		if (adColumnHeightExceedsPrimary($container)) {
			showAd($trigger, false, 'MPUDesktop');
			showAd($trigger, true, 'MPUDesktopDouble');
			return;
		}

		getOnPosition($container);

		available_height = viewport_height - settings.header_offset;

		if (available_height >= (mpu_double_height)) {
			showAd($trigger, false, 'MPUDesktop');
			showAd($trigger, true, 'MPUDesktopDouble');
			settings.ads_exceed_viewport_height = false;
		} else {
			showAd($trigger, false, 'MPUDesktop');
			showAd($trigger, true, 'MPUDesktopDouble');
			settings.ads_exceed_viewport_height = true;
		}

		EFLAdition.functions.check_for_instances();
	};

	// Sets the scroll lock state
	var setState = function($trigger, $container) {
		var current_scroll_position, bottom;

		setVisibleAds($trigger, $container);

		if ($trigger.is(':visible')) {

			// If the secondary column exceeds the height of the primary, revert to the default ad behaviour
			if (adColumnHeightExceedsPrimary($container) || settings.ads_exceed_viewport_height) {
				$trigger.removeClass('fixed bottom');
				return;
			}

			setPositions($container);

			// If on/off values are not set, revert to the default ad behaviour
			if (settings.scroll_lock_on < 0 && settings.scroll_lock_off < 0) {
				$trigger.removeClass('fixed bottom');
				return;
			}

            current_scroll_position = $(window).scrollTop();

            //Delay this calculation a bit on first page load to allow the ad time to appear in the iframe.
            setTimeout(function () {

                bottom = current_scroll_position + $trigger.find('.adition-container').outerHeight() + settings.top_offset;


                if (current_scroll_position > settings.scroll_lock_on && bottom <= settings.scroll_lock_off) {
                    // Set 'fixed' state
                    $trigger.addClass('fixed').removeClass('bottom');
                }
                else if (bottom > settings.scroll_lock_off) {
                    // Set 'bottom' state
                    $trigger.addClass('bottom').removeClass('fixed');
                }
                else {
                    // Set 'default' state
                    $trigger.removeClass('fixed bottom');
                }

                //We only want to delay this process on first page load, to allow time for the ad to load in.  It should
                //be immediage after that for smooth scrolling.
                settings.delay = 0;

            }, settings.delay);

           

		}
	};

	var init = function() {
		var $trigger = $(settings.trigger);

		if (EFLAdition && EFLAdition.functions && $trigger.length && settings.$scroll_lock_container.length && !$(settings.disable).length) {

			settings.is_match_centre = $('body').hasClass('match-centre');

			$trigger.each(function(index, trigger) {

				var $trigger = $(trigger),
					$container,
					$scroll_lock_container = $trigger.closest('.adition-scroll-lock-container');

				if (settings.is_match_centre) {

					if ($scroll_lock_container.closest('.tab-pane').length) {
						$container = $scroll_lock_container;
					}
					else {
						$container = $trigger.closest('.tab-pane');
					}
				}
				else {
					$container = $scroll_lock_container;
				}

				if ($container.length) {
                    setState($trigger, $container);

					$(window).on('throttled-scroll throttled-resize orientationchange', function(e) {
						setState($trigger, $container);
					});

					window.EFL.eventDispatcher.registerHandler('update-adition-scroll-lock', function() {
						setState($trigger, $container);
					});
				}
			});
		}
	};

	init();

})(
	window.jQuery,
	window.EFL.adition
);;
(function() {
	'use strict';

	var delay = 100;
	var timer;

	$(window).on('resize', function() {
		clearTimeout(timer);
		timer = setTimeout(function() {
			$(window).trigger('throttled-resize');
		}, delay);
	});
}());;
(function() {
	'use strict';

	var delay = 25,
		trigger = true;

	$(window).on('scroll', function() {
		if (trigger) {
			trigger = false;

			window.setTimeout(function() {
				$(window).trigger('throttled-scroll');
				trigger = true;
			}, delay);
		}
	});
}());;
/*
	Allows popup menus on header navigation (click or focus to activate)
	Adding the adding/removing of CSS classes on other elements when something is clicked

	Example usage

		<button data-toggleclass="hide" data-toggleclass-target=".bob">Click me</button>

		<div class="bob">Now you see me</div>
*/

(function() {
    'use strict';

    var state = {
        default: 0,
        toRemove: 1,
        added: 2
    };

    if ($('[data-addclass-target]').length) {
        $('[data-addclass-target]').each(function() {
            var _class = $(this).data('addclass');
            var target = $(this).data('addclass-target');
            target = (target === 'parent') ? $(this).parent() : $(target);
            target.data('state', state.default);

            var hasNoRemove = typeof($(this).data('removeclass')) === 'undefined';

            $(this).on('click', function() {
                if (hasNoRemove || target.data('state') === state.default) {
                    target.addClass(_class);
                    target.data('state', state.added);
                    $(window).trigger('throttled-resize');
                }
                else if(target.data('state') === state.added) {
                    target.data('state', state.toRemove);
                }

                return false;
            });
        });
    }

    if ($('[data-removeclass-target]').length) {
        $('[data-removeclass-target]').each(function() {
            var _class = $(this).data('removeclass');
            var target = $(this).data('removeclass-target');
            target = (target === 'parent') ? $(this).parent() : $(target);

            var hasNoAdd = typeof($(this).data('addclass')) === 'undefined';

            $(this).on('click', function() {
                if (hasNoAdd || target.data('state') === state.toRemove) {
                    target.removeClass(_class);
                    target.data('state', state.default);
                    $(window).trigger('throttled-resize');
                } else {
                    if (target.data('state') === state.added) {
                        target.data('state', state.toRemove);
                    }
                }
                return false;
            });
        });
    }

    if ($('[data-toggleclass-target]').length) {
        $('[data-toggleclass-target]').each(function() {
            var _class = $(this).data('toggleclass');
            var target = $(this).data('toggleclass-target');
            target = (target === 'parent') ? $(this).parent() : $(target);

            $(this).on('click', function () {
                if (target.hasClass(_class)) {
                    target.removeClass(_class);
                } else {
                    target.addClass(_class);
                }

                // Special case for when we also need to apply this class to the body
                // To prevent page scrolling whilst mobile navigation is active
                if (_class === 'navigation-active') {
                    var $body = $('body');

                    if ($body.hasClass(_class)) {
                        $body.removeClass(_class);
                    } else {
                        $body.addClass(_class);
                    }
                }

                $(window).trigger('throttled-resize');

                return false;
            });
        });
    }

    if ($('[data-setfocus-target]').length) {
 
        $('[data-setfocus-target]').each(function () {
            var target = $($(this).data('setfocus-target'));

            $(this).on('click', function () {
                target.focus();

                return false;
            });
        });
    }
}());
;
/*
	Adding the adding/removing of aria-hidden on other elements when something is clicked

	Example usage

		<button data-togglearia-target=".bob">Click me</button>

		<div class="bob" aria-hidden="false" >Now you see me</div>
*/

(function($) {
	'use strict';

	var settings = {
		trigger: 'data-togglearia-target'
	};

	var init = function() {
		var $context = $(this),
			target = $context.data('togglearia-target'),
			$target = $(target),
			isVisible = $target.is(':visible');

		$target.attr('aria-hidden', !isVisible);
		$context.attr('aria-expanded', isVisible);

		$context.on('click', function () {
			var isVisible = $target.attr('aria-hidden') === 'false',
				$siblings = $('[' + settings.trigger + '="' + target + '"]');

			$target.attr('aria-hidden', isVisible);
			$siblings.attr('aria-expanded', !isVisible); // sets the aria-expanded state of all elements with the same target
		});
	};

	$('[' + settings.trigger + ']').each(init);

})(window.jQuery);;
/*
	Programmatically match the width and height of an element to match the parent DOM element
	Required as the Neulion player needs to have a height set in px

	Example usage

		<div>
			<div data-matchparentdimensions></div>
		</div>
*/

(function() {
	'use strict';

	var settings = {
		trigger: '[data-matchparentdimensions]'
	};

	if ($(settings.trigger).length) {
		var matchParent = function() {
			$(settings.trigger).each(function() {
				var $child = $(this);
				var $parent = $child.parent();

				$child
				.width($parent.outerWidth())
				.height($parent.outerHeight());
			});
		};

		matchParent();
		$(window).on('throttled-resize', matchParent);
	}
}());
;
window.EFL = window.EFL || {};

window.EFL.DateUtils = (function () {
	"use strict";

	var MILLISECONDS_IN_HOUR = 3600000;
	var MILLISECONDS_IN_MINUTE = 60000;

	function toInteger (dirtyNumber) {
		if (dirtyNumber === null || dirtyNumber === true || dirtyNumber === false) {
			return NaN;
		}

		var number = Number(dirtyNumber)

		if (isNaN(number)) {
			return number;
		}

		return number < 0 ? Math.ceil(number) : Math.floor(number);
	}

	/**
	 * Convert the given argument to an instance of Date.
	 *
	 * @param {Date|Number} argument - the value to convert
	 * @returns {Date} the parsed date in the local time zone
	 */
	function toDate(argument) {
		var argStr = Object.prototype.toString.call(argument);

		// Clone the date
		if (
			argument instanceof Date ||
			(typeof argument === 'object' && argStr === '[object Date]')
		) {
			// Prevent the date to lose the milliseconds when passed to new Date() in IE10
			return new Date(argument.getTime());
		} else if (typeof argument === 'number' || argStr === '[object Number]') {
			return new Date(argument);
		} else {
			if (
				(typeof argument === 'string' || argStr === '[object String]') &&
				typeof console !== 'undefined'
			) {
				// eslint-disable-next-line no-console
				console.warn(
					"Doesn't accept strings as arguments. Please use `parseISO` to parse strings."
				);
				// eslint-disable-next-line no-console
				console.warn(new Error().stack);
			}
			return new Date(NaN);
		}
	}

	/**
	 * Get the milliseconds timestamp of the given date.
	 *
	 * @param {Date|Number} date - the given date
	 * @returns {Number} the timestamp
	 */
	function getTime(dirtyDate) {
		var date = toDate(dirtyDate);
		var timestamp = date.getTime();

		return timestamp;
	}

	/**
	 * Compare the two dates and return 1 if the first date is after the second,
	 * -1 if the first date is before the second or 0 if dates are equal.
	 *
	 * @param {Date|Number} dateLeft - the first date to compare
	 * @param {Date|Number} dateRight - the second date to compare
	 * @returns {Number} the result of the comparison
	 */
	function compareAsc(dirtyDateLeft, dirtyDateRight) {
		var dateLeft = toDate(dirtyDateLeft);
		var dateRight = toDate(dirtyDateRight);

		var diff = dateLeft.getTime() - dateRight.getTime();

		if (diff < 0) {
			return -1;
		} else if (diff > 0) {
			return 1;
			// Return 0 if diff is 0; return NaN if diff is NaN
		} else {
			return diff;
		}
	}

	/**
	 * Get the seconds timestamp of the given date.
	 *
	 * @param {Date|Number} date - the given date
	 * @returns {Number} the timestamp
	 *
	 * @example
 	 * // Get the timestamp of 29 February 2020 11:45:05 CET:
 	 * var result = getUnixTime(new Date(2020, 1, 29, 11, 45, 5))
 	 * //=> 1330512305
	 */
	function getUnixTime(dirtyDate) {
		return Math.floor(getTime(dirtyDate) / 1000);
	}

	/**
	 * Get the ms timestamp of the given date.
	 *
	 * @param {Date|Number} date - the given date
	 * @returns {Number} the timestamp
	 *
	 * @example
 	 * // Get the timestamp of 29 February 2020 11:45:05 CET:
 	 * var result = getUnixTime(new Date(2020, 1, 29, 11, 45, 5))
 	 * //=> 1330512305000
	 */
	function getUnixTimeInMs(dirtyDate) {
		return Math.floor(getTime(dirtyDate));
	}

	/**
	 * Add the specified number of milliseconds to the given date.
	 *
	 * @param {Date|Number} date - the date to be changed
 	 * @param {Number} amount - the amount of milliseconds to be added.
	 * @returns {Date} the new date with the milliseconds added
	 */
	function addMilliseconds(dirtyDate, dirtyAmount) {
		var timestamp = toDate(dirtyDate).getTime();
		var amount = toInteger(dirtyAmount);

		return new Date(timestamp + amount);
	}

	/**
	 * Add the specified number of seconds to the given date.
	 *
	 * @param {Date|Number} date - the date to be changed
	 * @param {Number} amount - the amount of seconds to be added. Positive decimals will be rounded using `Math.floor`, decimals less than zero will be rounded using `Math.ceil`.
	 * @returns {Date} the new date with the seconds added
	 *
	 * @example
	 * // Add 30 seconds to 10 July 2014 12:45:00:
	 * var result = addSeconds(new Date(2014, 6, 10, 12, 45, 0), 30)
	 * //=> Thu Jul 10 2014 12:45:30
	 */
	function addSeconds(dirtyDate, dirtyAmount) {
		var amount = toInteger(dirtyAmount);

		return addMilliseconds(dirtyDate, amount * 1000);
	}

	/**
	 * Add the specified number of minutes to the given date.
	 *
	 * @param {Date|Number} date - the date to be changed
 	 * @param {Number} amount - the amount of minutes to be added.
 	 * @returns {Date} the new date with the minutes added
	 */
	function addMinutes(dirtyDate, dirtyAmount) {
		var amount = toInteger(dirtyAmount);

		return addMilliseconds(dirtyDate, amount * MILLISECONDS_IN_MINUTE);
	}

	/**
	 * Subtract the specified number of minutes from the given date.
	 *
	 * @param {Date|Number} date - the date to be changed
 	 * @param {Number} amount - the amount of minutes to be subtracted
 	 * @returns {Date} the new date with the minutes subtracted
	 */
	function subMinutes(dirtyDate, dirtyAmount) {
		var amount = toInteger(dirtyAmount);

		return addMinutes(dirtyDate, -amount);
	}

	/**
	 * Add the specified number of hours to the given date.
	 *
	 * @param {Date|Number} date - the date to be changed
     * @param {Number} amount - the amount of hours to be added.
	 * @returns {Date} the new date with the hours added
	 */
	function addHours(dirtyDate, dirtyAmount) {
		var amount = toInteger(dirtyAmount);

		return addMilliseconds(dirtyDate, amount * MILLISECONDS_IN_HOUR);
	}

	/**
	 * Subtract the specified number of hours from the given date.
	 *
	 * @param {Date|Number} date - the date to be changed
	 * @param {Number} amount - the amount of hours to be subtracted
	 * @returns {Date} the new date with the hours subtracted
	 */
	function subHours(dirtyDate, dirtyAmount) {
		var amount = toInteger(dirtyAmount);
		return addHours(dirtyDate, -amount);
	}

	/**
	 * Return the start of a day for the given date.
	 * The result will be in the local timezone.
	 *
	 * @param {Date|Number} date - the original date
	 * @returns {Date} the start of a day
	 *
	 * @example
	 * // The end of a day for 2 September 2014 11:55:00:
	 * var result = startOfDay(new Date(2014, 8, 2, 11, 55, 0))
	 * //=> Tue Sep 02 2014 00:00:00
	 */
	function startOfDay(dirtyDate) {
		var date = toDate(dirtyDate);

		date.setHours(0, 0, 0, 0);
		return date;
	}

	/**
	 * Return the end of a day for the given date.
	 * The result will be in the local timezone.
	 *
	 * @param {Date|Number} date - the original date
	 * @returns {Date} the end of a day
	 *
	 * @example
	 * // The end of a day for 2 September 2014 11:55:00:
	 * var result = endOfDay(new Date(2014, 8, 2, 11, 55, 0))
	 * //=> Tue Sep 02 2014 23:59:59.999
	 */
	function endOfDay(dirtyDate) {
		var date = toDate(dirtyDate);

		date.setHours(23, 59, 59, 999);
		return date;
	}

	return {
		toDate: toDate,
		getTime: getTime,
		compareAsc: compareAsc,
		getUnixTime: getUnixTime,
		getUnixTimeInMs: getUnixTimeInMs,
		addMilliseconds: addMilliseconds,
		addSeconds: addSeconds,
		addMinutes: addMinutes,
		subMinutes: subMinutes,
		addHours: addHours,
		subHours: subHours,
		startOfDay: startOfDay,
		endOfDay: endOfDay
	};
})();
;
window.EFL = window.EFL || {};

window.EFL.HTTPCache = (function() {
	'use strict';

	var cacheStore = {
		cacheStorageKey: '@@efl@@::http::cache',
		addNewKey: function(key) {
			var httpCacheData = this.getStoreData();

			httpCacheData.allKeys.push(key);

			// TODO: This could be expensive if lots are running. We should batch the actual store.
			this.updateStore(httpCacheData);
		},
		removeKey: function(key) {
			var httpCacheData = this.getStoreData();

			httpCacheData.allKeys.indexOf(key) > -1 ? httpCacheData.allKeys.splice(httpCacheData.allKeys.indexOf(key), 1) : false;

			// TODO: This could be expensive if lots are running. We should batch the actual store.
			this.updateStore(httpCacheData);
		},
		getStoreData: function() {
			var defaultObj = { allKeys: [] };
			var httpCacheStr = localStorage.getItem(this.cacheStorageKey);
			var httpCacheData = httpCacheStr !== null ? JSON.parse(httpCacheStr) : defaultObj;

			return httpCacheData;
		},
		updateStore: function(data) {
			localStorage.setItem(this.cacheStorageKey, JSON.stringify(data));
		},
		clearAll: function() {
			var httpCacheData = this.getStoreData();

			httpCacheData.allKeys.forEach(function(key) {
				localStorage.removeItem(key);
			});

			httpCacheData.allKeys = [];

			// TODO: This could be expensive if lots are running. We should batch the actual store.
			this.updateStore(httpCacheData);
		}
	};

	return {
		getItem: function(key) {
			return localStorage.getItem(key);
		},
		removeItem: function(key) {
			cacheStore.removeKey(key);
			localStorage.removeItem(key);
		},
		setItem: function(key, data) {
			cacheStore.addNewKey(key);
			localStorage.setItem(key, data);
		},
		clearAll: function() {
			cacheStore.clearAll();
		}
	};
})();
;
window.EFL = window.EFL || {};

// TODO: Line 76ish
window.EFL.HTTP = (function($, EFL) {
	'use strict';

	var addSeconds = EFL.DateUtils.addSeconds;
	var getUnixTimeInMs = EFL.DateUtils.getUnixTimeInMs;

	var hashstr = function(s) {
		var hash = 0;
		if (s.length == 0) return hash;
		for (var i = 0; i < s.length; i++) {
			var char = s.charCodeAt(i);
			hash = ((hash << 5) - hash) + char;
			hash = hash & hash; // Convert to 32bit integer
		}
		return hash;
	};

	// eslint-disable-next-line complexity
	var normalizeUrl = function(urlString) {
		var testParameter = function testParameter(name, filters) {
			return filters.some(function (filter) {
				return filter instanceof RegExp ? filter.test(name) : filter === name;
			});
		};

		var options = {
			defaultProtocol: 'http:',
			normalizeProtocol: true,
			forceHttp: false,
			forceHttps: true,
			stripAuthentication: true,
			stripHash: false,
			stripWWW: true,
			stripProtocol: false,
			removeQueryParameters: [/^utm_\w+/i],
			removeTrailingSlash: true,
			removeDirectoryIndex: false,
			sortQueryParameters: true
		};

		urlString = urlString.trim();

		var hasRelativeProtocol = urlString.startsWith('//');
		var isRelativeUrl = !hasRelativeProtocol && /^\.*\//.test(urlString);

		// Prepend protocol
		if (!isRelativeUrl) {
			urlString = urlString.replace(/^(?!(?:\w+:)?\/\/)|^\/\//, options.defaultProtocol);
		}

		var urlObj = new URL(!urlString.startsWith('/') ? urlString : window.location.origin + urlString);

		if (options.forceHttp && urlObj.protocol === 'https:') {
			urlObj.protocol = 'http:';
		}

		if (options.forceHttps && urlObj.protocol === 'http:') {
			urlObj.protocol = 'https:';
		}

		// Remove auth
		// EDGE doesn't like this, and will fire a security issue.
		// if (options.stripAuthentication) {
		// 	urlObj.username = '';
		// 	urlObj.password = '';
		// }

		// Remove hash
		if (options.stripHash) {
			urlObj.hash = '';
		}

		// Remove duplicate slashes if not preceded by a protocol
		if (urlObj.pathname) {
			// TODO: Can we add this back?
			// urlObj.pathname = urlObj.pathname.replace(/(?<!https?:)\/{2,}/g, '/');
		}

		// Decode URI octets
		if (urlObj.pathname) {
			try {
				urlObj.pathname = decodeURI(urlObj.pathname);
			// eslint-disable-next-line no-empty
			} catch (_) {}
		}

		// Remove directory index
		if (options.removeDirectoryIndex === true) {
			options.removeDirectoryIndex = [/^index\.[a-z]+$/];
		}

		if (Array.isArray(options.removeDirectoryIndex) && options.removeDirectoryIndex.length > 0) {
			var pathComponents = urlObj.pathname.split('/');
			var lastComponent = pathComponents[pathComponents.length - 1];

			if (testParameter(lastComponent, options.removeDirectoryIndex)) {
				pathComponents = pathComponents.slice(0, pathComponents.length - 1);
				urlObj.pathname = pathComponents.slice(1).join('/') + '/';
			}
		}

		if (urlObj.hostname) {
			// Remove trailing dot
			urlObj.hostname = urlObj.hostname.replace(/\.$/, '');

			// Remove `www.`
			if (options.stripWWW && /^www\.(?:[a-z\-\d]{2,63})\.(?:[a-z.]{2,5})$/.test(urlObj.hostname)) {
				// Each label should be max 63 at length (min: 2).
				// The extension should be max 5 at length (min: 2).
				// Source: https://en.wikipedia.org/wiki/Hostname#Restrictions_on_valid_host_names
				urlObj.hostname = urlObj.hostname.replace(/^www\./, '');
			}
		}

		// Remove query unwanted parameters
		if (Array.isArray(options.removeQueryParameters)) {
			for (var _i = 0, _arr = [].concat(urlObj.searchParams.keys()); _i < _arr.length; _i++) {
				var key = _arr[_i];

				if (testParameter(key, options.removeQueryParameters)) {
					// eslint-disable-next-line dot-notation
					urlObj.searchParams["delete"](key);
				}
			}
		}

		// Sort query parameters
		if (options.sortQueryParameters) {
			urlObj.searchParams.sort();
		}

		if (options.removeTrailingSlash) {
			urlObj.pathname = urlObj.pathname.replace(/\/$/, '');
		}

		urlString = urlObj.toString();

		// Remove ending `/`
		if ((options.removeTrailingSlash || urlObj.pathname === '/') && urlObj.hash === '') {
			urlString = urlString.replace(/\/$/, '');
		}

		// Restore relative protocol, if applicable
		if (hasRelativeProtocol && !options.normalizeProtocol) {
			urlString = urlString.replace(/^http:\/\//, '//');
		}

		// Remove http/https
		if (options.stripProtocol) {
			urlString = urlString.replace(/^(?:https?:)?\/\//, '');
		}

		return urlString;
	};

	var serializeQuery = function(url) {
		var normalizedURL = normalizeUrl(url);

		return normalizedURL;
	};

	var useQuery = function(url, fetcherFn, options) {
		var expiry = 30;//5 * 60; // 5 min default
		var skipCache = false;

		if (typeof options === 'number') {
			expiry = options;
			options = undefined;
		} else if (typeof options === 'object') {
			// I hope you didn't set it to 0 seconds
			expiry = options.seconds || expiry;
			skipCache = options.skipCache || false;
		}

		//Limit cache time to 30s to avoid unusual behaviour
		if (expiry > 30) {
			expiry = 30;
		}

		if (skipCache) {
			return fetcherFn.call();
		}

		// Use the URL as the cache key to sessionStorage
		var cacheKey = hashstr(serializeQuery(url));
		var cached = EFL.HTTPCache.getItem(cacheKey);
		var whenCached = EFL.HTTPCache.getItem(cacheKey + ':ts');

		if (cached !== null && whenCached !== null) {
			// it was in sessionStorage! Yay!
			// Even though 'whenCached' is a string, this operation
			// works because the minus sign tries to convert the
			// string to an integer and it will work.
			var age = (Date.now() - whenCached);

			if (age < 0) {
				var response = JSON.parse(cached);
				return Promise.resolve(response);
			} else {
				// We need to clean up this old key
				EFL.HTTPCache.removeItem(cacheKey);
				EFL.HTTPCache.removeItem(cacheKey + ':ts');
			}
		}

		var fetcher = fetcherFn.call();

		fetcher.then(function (content) {
			EFL.HTTPCache.setItem(cacheKey, JSON.stringify(content));
			EFL.HTTPCache.setItem(cacheKey + ':ts', getUnixTimeInMs(addSeconds(Date.now(), expiry)));
		}).catch(function (error) {
			console.error(error);
		});

		return fetcher;
	};

	return {
		get: function(url, settings, options) {
			var defaultSettings = {
				'method': 'GET',
				'url': url,
			};

			var fetcherFn = function() {
				return new Promise(function (resolve, reject) {
					var fetch = $.ajax(_.assign(defaultSettings, settings));

					fetch.done(function( data, textStatus, jqXHR ) {
						resolve({
							data: data,
							statusText: textStatus,
							status: jqXHR.status,
							response: jqXHR.responseJSON
						});
					});

					fetch.fail(function(jqXHR, textStatus, errorThrown) {
						reject({
							errorThrown: errorThrown,
							statusText: textStatus,
							status: jqXHR.status,
							response: jqXHR.responseJSON
						});
					});
				});
			};

			return useQuery(url, fetcherFn, options);
		},

		post: function(url, settings) {
			var defaultSettings = {
				'method': 'POST',
				'url': url,
			};

			return new Promise(function (resolve, reject) {
				var fetch = $.ajax(_.assign(defaultSettings, settings));

				fetch.done(function (data, textStatus, jqXHR) {
					//item changed clear cache
					EFL.HTTPCache.clearAll();

					resolve({
						data: data,
						statusText: textStatus,
						status: jqXHR.status,
						response: jqXHR.responseJSON
					});
				});

				fetch.fail(function(jqXHR, textStatus, errorThrown) {
					reject({
						errorThrown: errorThrown,
						statusText: textStatus,
						status: jqXHR.status,
						response: jqXHR.responseJSON
					});
				});
			});
		},

		put: function(url, settings) {
			var defaultSettings = {
				'method': 'PUT',
				'url': url,
			};

			return new Promise(function (resolve, reject) {
				var fetch = $.ajax(_.assign(defaultSettings, settings));

				fetch.done(function (data, textStatus, jqXHR) {

					//item changed clear cache
					EFL.HTTPCache.clearAll();

					resolve({
						data: data,
						statusText: textStatus,
						status: jqXHR.status,
						response: jqXHR.responseJSON
					});
				});

				fetch.fail(function(jqXHR, textStatus, errorThrown) {
					reject({
						errorThrown: errorThrown,
						statusText: textStatus,
						status: jqXHR.status,
						response: jqXHR.responseJSON
					});
				});
			});
		},

		delete: function (url, settings) {
			var defaultSettings = {
				'method': 'DELETE',
				'url': url,
			};

			return new Promise(function (resolve, reject) {
				var fetch = $.ajax(_.assign(defaultSettings, settings));

				fetch.done(function (data, textStatus, jqXHR) {
					//item changed clear cache
					EFL.HTTPCache.clearAll();

					resolve({
						data: data,
						statusText: textStatus,
						status: jqXHR.status,
						response: jqXHR.responseJSON
					});
				});

				fetch.fail(function (jqXHR, textStatus, errorThrown) {
					reject({
						errorThrown: errorThrown,
						statusText: textStatus,
						status: jqXHR.status,
						response: jqXHR.responseJSON
					});
				});
			});
		}
	};
})(
	window.jQuery,
	window.EFL
);
;
!function a(b,c,d){function e(g,h){if(!c[g]){if(!b[g]){var i="function"==typeof require&&require;if(!h&&i)return i(g,!0);if(f)return f(g,!0);var j=new Error("Cannot find module '"+g+"'");throw j.code="MODULE_NOT_FOUND",j}var k=c[g]={exports:{}};b[g][0].call(k.exports,function(a){var c=b[g][1][a];return e(c?c:a)},k,k.exports,a,b,c,d)}return c[g].exports}for(var f="function"==typeof require&&require,g=0;g<d.length;g++)e(d[g]);return e}({1:[function(a,b,c){function d(a){this.message=a}function e(a){var b=String(a).replace(/=+$/,"");if(b.length%4==1)throw new d("'atob' failed: The string to be decoded is not correctly encoded.");for(var c,e,g=0,h=0,i="";e=b.charAt(h++);~e&&(c=g%4?64*c+e:e,g++%4)?i+=String.fromCharCode(255&c>>(-2*g&6)):0)e=f.indexOf(e);return i}var f="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";d.prototype=new Error,d.prototype.name="InvalidCharacterError",b.exports="undefined"!=typeof window&&window.atob&&window.atob.bind(window)||e},{}],2:[function(a,b,c){function d(a){return decodeURIComponent(e(a).replace(/(.)/g,function(a,b){var c=b.charCodeAt(0).toString(16).toUpperCase();return c.length<2&&(c="0"+c),"%"+c}))}var e=a("./atob");b.exports=function(a){var b=a.replace(/-/g,"+").replace(/_/g,"/");switch(b.length%4){case 0:break;case 2:b+="==";break;case 3:b+="=";break;default:throw"Illegal base64url string!"}try{return d(b)}catch(c){return e(b)}}},{"./atob":1}],3:[function(a,b,c){"use strict";function d(a){this.message=a}var e=a("./base64_url_decode");d.prototype=new Error,d.prototype.name="InvalidTokenError",b.exports=function(a,b){if("string"!=typeof a)throw new d("Invalid token specified");b=b||{};var c=b.header===!0?0:1;try{return JSON.parse(e(a.split(".")[c]))}catch(f){throw new d("Invalid token specified: "+f.message)}},b.exports.InvalidTokenError=d},{"./base64_url_decode":2}],4:[function(a,b,c){(function(b){var c=a("./lib/index");"function"==typeof b.window.define&&b.window.define.amd?b.window.define("jwt_decode",function(){return c}):b.window&&(b.window.jwt_decode=c)}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{"./lib/index":3}]},{},[4]);;
window.EFL = window.EFL || {};

window.EFL.FormValidation = (function () {
	'use strict';

	function FormValidation() {
		var parsleyOptions = {
			successClass: 'is-valid',
			errorClass: 'is-invalid',
			errorsWrapper: '<ul class="invalid-feedback-client"></ul>',
			errorTemplate: '<li></li>',
			classHandler: function (ParsleyField) { return ParsleyField.$element; }
		};
	
		Object.keys(parsleyOptions).forEach(function (key) {
			Parsley.options[key] = parsleyOptions[key];
		});
	}

	FormValidation.prototype.initDateValidation = function () {

		window.Parsley.addValidator('age', {
			validate: function (value, requirement) {
				return !this.hasAllParts(value) || this.isOverAge(parseInt(requirement), value)
			}.bind(this),
			messages: {
				en: 'Unfortunately you need to be at least 18 to access this service',
			}
		});
	
		window.Parsley.addValidator('validdate', {
			validate: function (value) {
				return !this.hasAllParts(value) || this.isDateValid(value)
			}.bind(this),
			messages: {
				en: 'Please enter a valid date',
			}
		});
	
		window.Parsley.addValidator("fulldatevalid", {
			requirementType: "string",
			validateString: function(_, requirement) {
				return $(requirement).parsley().isValid()
			}
		});
	
		$('[data-day]').change(function () {
			var fullDateElement = $($(this).attr('data-parsley-fulldatevalid'))[0];
			var parts = fullDateElement.value.split('-');
			fullDateElement.value = parts[0] + '-' + parts[1] + '-' + this.value;
		})
	
		$('[data-month]').change(function () {
			var fullDateElement = $($(this).attr('data-parsley-fulldatevalid'))[0];
			var parts = fullDateElement.value.split('-');
			fullDateElement.value = parts[0] + '-' + this.value + '-' + parts[2];
		})
	
		$('[data-year]').change(function () {
			var fullDateElement = $($(this).attr('data-parsley-fulldatevalid'))[0];
			var parts = fullDateElement.value.split('-');
			fullDateElement.value = this.value + '-' + parts[1] + '-' + parts[2];
		})
	}

	FormValidation.prototype.hasAllParts = function(date) {
		var dateParts =  date.split('-');

		for(var i = 0; i < dateParts.length; i++){
			if(!dateParts[i]) {
				return false;
			}
		}

		return true;
	}

	FormValidation.prototype.getAge = function (birthDateString) {
		var today = new Date();
		var birthDate = new Date(birthDateString);
		var age = today.getFullYear() - birthDate.getFullYear();
		var m = today.getMonth() - birthDate.getMonth();
		if (m < 0 || (m === 0 && today.getDate() < birthDate.getDate())) {
			age--;
		}
		return age;
	}

	FormValidation.prototype.isOverAge = function (age, date) {
		var isCorrectAge = this.getAge(date) >= parseInt(age);
		return isCorrectAge;
	}

	FormValidation.prototype.isDateValid = function (date) {
		var dateRegex = /^(?:(?:31(\/|-|\.)(?:0?[13578]|1[02]))\1|(?:(?:29|30)(\/|-|\.)(?:0?[13-9]|1[0-2])\2))(?:(?:1[6-9]|[2-9]\d)?\d{2})$|^(?:29(\/|-|\.)0?2\3(?:(?:(?:1[6-9]|[2-9]\d)?(?:0[48]|[2468][048]|[13579][26])|(?:(?:16|[2468][048]|[3579][26])00))))$|^(?:0?[1-9]|1\d|2[0-8])(\/|-|\.)(?:(?:0?[1-9])|(?:1[0-2]))\4(?:(?:1[6-9]|[2-9]\d)?\d{2})$/

		var isValidDate = !!date.split('-').reverse().join('-').match(dateRegex);
		return isValidDate;
	}

	return new FormValidation();

}());
;
window.EFL = window.EFL || {};

window.EFL.Tooltips = (function ($) {
	'use strict';

	return {
		init: function () {
			$(document).click(function () {
				$('.tooltip').hide();
			});

			$('.tooltip').on('click', function (event) {
				event.preventDefault();
				event.stopPropagation();
			});

			$('.tooltip-btn').on('click', function (event) {
				event.preventDefault();
				event.stopPropagation();
				$(this).siblings('.tooltip').toggle();
			});
		}
	};
})(window.jQuery);
;
(function () {
	'use strict';

	var hasOwn = {}.hasOwnProperty;

	function classNames() {
		var classes = [];

		for (var i = 0; i < arguments.length; i++) {
			var arg = arguments[i];
			if (!arg) continue;

			var argType = typeof arg;

			if (argType === 'string' || argType === 'number') {
				classes.push(arg);
			} else if (Array.isArray(arg)) {
				if(arg.length) {
					var inner = classNames.apply(null, arg);
					if (inner) {
						classes.push(inner);
					}
				}
			} else if (argType === 'object') {
				if (arg.toString !== Object.prototype.toString) {
					classes.push(arg.toString());
				} else {
					for (var key in arg) {
						if (hasOwn.call(arg, key) && arg[key]) {
							classes.push(key);
						}
					}
				}
			}
		}

		return classes.join(' ');
	}

	if (typeof module !== 'undefined' && module.exports) {
		classNames.default = classNames;
		module.exports = classNames;
	} else if (typeof define === 'function' && typeof define.amd === 'object' && define.amd) {
		// register as 'classnames', consistent with npm package name
		define('classnames', [], function () {
			return classNames;
		});
	} else {
		window.classNames = classNames;
	}
}());
;
/*
	Scrolls to a DOM element on the page

	Example usage

		<button data-scrollto="#scroll-here">Click me</button>

        ...

		<div id="scroll-here">Here I am!</div>
*/

(function() {
    'use strict';

    var settings = {
        trigger: 'scrollto',
        offset: 50,    
        speed: 500
    };

    if ($('[data-' + settings.trigger + '], [data-video-packages-special]').length) {
        $('body').on('click', '[data-' + settings.trigger + ']', function () {
            var $target = $($(this).data(settings.trigger));

            setTimeout(function () {
                $('html, body').animate({
                    scrollTop: $target.offset().top - settings.offset
                }, settings.speed);
            }, 400);
        });
    }
}());
;
window.EFL.DiceRealmLabelsLocalCache = (function() {
	'use strict';

	// Last update: 01/05/2019 10:00
	return {
		"default_locale": "en_GB",
		"selected_locale": "en_GB",
		"translations": {
			"en_GB": {
				"accessibility": "Accessibility",
				"account": "Account",
				"activateAccountInstructions": "We've emailed you with instructions on how to active your account",
				"addedToFavourites": "Added to Favourites",
				"addToFavourites": "Add to Favourites",
				"airplay": "AirPlay",
				"airplayAvailable": "This video is playing on Apple TV",
				"alreadyHaveAnAccount": "Already have an account?",
				"amount": "Amount",
				"browse": "Browse",
				"cancel": "Cancel",
				"changeLanguageTo": "Change to",
				"changePassword": "Change Password",
				"checkYourInbox": "Check your inbox",
				"confirmPassword": "Confirm Password",
				"connectionError": "Connection error",
				"contentNotAvailable": "This content is not available",
				"contentUnavailableOffline": "This content is not available while you're offline. Please connect to the internet and try again",
				"continueWatching": "Continue Watching",
				"createPassword": "Create Password",
				"date": "Date",
				"description": "Description",
				"dontHaveAnAccount": "Don't have an account ?",
				"duration": "Duration",
				"email": "Email",
				"emailErrorCode": "This email address is already in use",
				"favourites": "Favourites",
				"filterByTag": "Filter by tag",
				"forgotPassword": "Forgot Password?",
				"goToPreferences": "Go to Preferences",
				"history": "History",
				"home": "Home",
				"january": "Jan",
				"language": "Language",
				"legal": "Legal",
				"live": "Live",
				"liveeventupdates": "Live Event Updates",
				"liveNow": "Live Now",
				"loading": "Loading",
				"login": "Login",
				"loginError": "Email or password is incorrect",
				"loginFormError": "There are some problems with the form below",
				"logOut": "Logout",
				"new": "New",
				"newsletter": "Newsletter",
				"noAccount": "Don't have an account?",
				"noDevicesAvailable": "No devices available",
				"noInternetAvailable": "No internet connection detected",
				"nonotifications": "You have no pending notifications",
				"noResults": "No Results were found",
				"notConnectedToWifi": "Not connected to wifi",
				"notifications": "Notifications",
				"offline": "Offline",
				"password": "Password",
				"passwordErrorCode": "Password does not meet requirements",
				"passwordMatchError": "Both passwords must match",
				"passwordRequired": "Password is required",
				"passwordRequirementRules": "Your password must be between 6 and 24 characters.",
				"passwordRequirements": "Password Requirements",
				"paymentHistory": "Payment History",
				"paymentMethods": "Payment Methods",
				"popularEvents": "Popular Events",
				"popularVods": "Popular Videos",
				"preferences": "Preferences",
				"profile": "Profile",
				"pushToEmail": "Push to Email",
				"pushToPhone": "Push to Phone",
				"registerConfirm": "Something went wrong please try again",
				"removedFromFavourites": "Removed from Favourites",
				"resetPasswordInstructions": "We've emailed you with instructions on how to reset your password.",
				"restart": "Restart",
				"resume": "Resume",
				"schedule": "Schedule",
				"search": "Search",
				"signUp": "Sign Up",
				"signUpNow": "Sign Up Now",
				"social": "Social",
				"sortBy": "Sort by",
				"subscription": "Subscription Started",
				"subTitleLanguage": "Sub Title Language",
				"subTitles": "Sub Titles",
				"suggestedPlaylists": "Suggested Playlists",
				"suggestedResults": "Suggested Results",
				"suggestedVideos": "Suggested Videos",
				"support": "Support",
				"tapToRetry": "Tap to retry",
				"upcoming": "Upcoming",
				"updatePreferences": "Update your preferences to view this content over mobile data",
				"upNext": "Up Next",
				"useMobileData": "Use Mobile Data",
				"validEmailRequired": "A valid email address is required",
				"video": "Video",
				"vod": "VOD",
				"watchingNow": "Watching now",
				"watchNow": "Watch Now",
				"submit": "Submit",
				"airPlayPlaying": "This video is playing on Apple TV",
				"close": "Close",
				"yesCancel": "Yes, Cancel",
				"yourDetails": "Your details",
				"myAccount": "My account",
				"planCancellationPrompt": "Are you sure you want to cancel this plan?",
				"billingAddress": "Billing address",
				"addressLine1": "Address line 1",
				"addressLine2": "Address line 2",
				"city": "City",
				"state": "State",
				"country": "Country",
				"name": "Name",
				"yourCurrentPlan": "Your current plan",
				"noContentHere": "No content here",
				"totalVideos": "total videos",
				"addVideoAsFavouriteToSee": "Add videos as a Favourite to see them here",
				"noContentAvailable: No content available": "No content available",
				"welcomeTo": "Welcome to",
				"loginNow": "Login now",
				"invalidToken": "Invalid token",
				"sorrySomethingWentWrong": "Sorry, something went wrong.",
				"positiveEmailConfirmation": "Thanks for confirming your email",
				"yourAllSetGoEnjoy": "You're all set - go and enjoy",
				"signIn": "Sign in",
				"welcomeBack": "Welcome back",
				"signInBelow": "Sign in below",
				"pleaseTypeYourEmailAddressBelow": "Please type your email address below",
				"typeYourEmailAddress": "Type your email address",
				"resetPasswordEmailSent: Reset Password Email Sent": "Reset Password Email Sent",
				"wantToLoginClickHere": "Want to login? Click here",
				"forgottenPassword": "Forgotten password",
				"pleaseResetPasswordBelow": "Please reset your password below",
				"addressCountryState": "State",
				"addressCountryPostCode": "Post Code",
				"setPassword": "Set password",
				"pleaseSetNewPasswordBelow": "Please set your new password below",
				"typeNewPassword": "Type New Password",
				"retypeNewPassword": "Retype New Password",
				"dontHaveAccountSignUp": "Don't have an account? SIGN UP",
				"forgotYourPasswordClickHere": "Forgot your password? Click here",
				"pleaseProvideAValue": "Please provide a value",
				"incorrectValue": "Incorrect value",
				"pleaseConfirmYourPassword": "Please confirm your password",
				"passwordsDontMatch": "Passwords don't match",
				"pleaseProvideYourPassword": "Please provide your password",
				"yourPasswordRules": "Your password must be between 6 and 24 characters",
				"passwordSuccessfullyReset": "Password Successfully Reset",
				"passwordNotMatching": "Password not matching",
				"passwordSuccessfullyCreated": "Password Successfully Created",
				"startYourFreeTrialToday": "Start Your Free Trial Today",
				"signUpToStartFreeTrial": "Sign up now for exclusive access!",
				"firstName": "First name",
				"surname": "Surname",
				"signUpStep1Title": "Step 1 - create a login",
				"signUpStep2Title": "Step 2 - select a package for when your trial ends",
				"pleaseProvideValidEmail": "Please provide a valid email address",
				"pleaseProvideYourEmail": "Please provide your email address",
				"pleaseProvideFirstName": "Please provide your first name",
				"pleaseProvideSurname": "Please provide your surname",
				"pleaseSelectACountry": "Please select a country",
				"noNewsAvailable": "No news available",
				"notFound404": "404 Not Found",
				"liveThisWeek": "Live this week",
				"nextWeek": "Next Week",
				"later": "Later",
				"loadMore": "Load More",
				"eventStartsIn": "Event starts in",
				"daysD": "d",
				"hoursH": "h",
				"minutesM": "m",
				"secondsS": "s",
				"notImplemented": "Not implemented",
				"settings": "Settings",
				"back": "Back",
				"removeFromFavourites": "Remove from Favourites",
				"primary": "primary",
				"addNewPaymentMethod": "Add a new payment method",
				"buyNow": "Buy now",
				"upgradeYourPlanToWatch": "Choose your plan to watch this video",
				"yourCurrentPlanIs": "Your Current Plan is",
				"selectPlan": "Select Plan",
				"payment": "Payment",
				"inOrderToWatchUpgrade": "In order to watch this video, you'll need to upgrade your subscription plan.",
				"refresh": "Refresh",
				"liveAndUpcoming": "Live and upcoming",
				"videos": "Videos",
				"playlists": "Playlists",
				"trendingLive": "Trending Live",
				"trendingVideos": "Trending Videos",
				"noResultsFoundForSearchTerm": "Unfortunately there are no results for \"{{searchTerm}}\", but here are a few things you might be interested in",
				"refine": "Refine",
				"Reset": "Reset",
				"dateAdded": "Date added",
				"popularity": "Popularity",
				"tag": "Tag",
				"minutes4Min": "4 Min",
				"minutes20Min": "20 Min",
				"purchase": "Purchase",
				"readNow": "Read Now",
				"authenticationError": "Authentication error",
				"signUpComplete": "Sign up complete",
				"errorAccessingVideo": "There was an error accessing this video",
				"watchingVideoOnAnotherDevice": "You are currently watching a video on another device",
				"eventNotAvailableInTerritory": "Event is not available in your territory",
				"errorOccurredPleaseTryAgain": "An error occurred. Please try again",
				"refreshPageNoAuthCode": "Please refresh the page. If this message persists please contact our support team",
				"youreAllSetAccountCreated": "You're all set - Please click the button below to login into your account",
				"pleaseTryAgainVideoError": "Please try again shortly - if the issue persists please contact our support team",
				"youreNotAllowedVideoForbidden": "You are not allowed to access this video. Please check your licences",
				"toWatchHereCloseOtherSessions": "To watch here please close your other session and refresh your page",
				"perMonth": "per Month",
				"month": "Month",
				"year": "Year",
				"subTotalPerPeriod": "Sub Total",
				"totalPerPeriod": "Total",
				"taxIncludedPerPeriod": "Tax Included",
				"taxExcludedPerPeriod": "Tax",
				"free": "Free",
				"cancelPlan": "Cancel plan",
				"promptToUseChrome": "Please use a more modern browser to enjoy the full experience",
				"outOfDateBrowser": "Apologies - you're currently trying to access this website using an unsupported browser ({{browserName}}, {{browserVersion}})",
				"noResultsFoundForSearchTermPrefix": "Unfortunately there are no results for \"",
				"noResultsFoundForSearchTermSuffix": "\", but here are a few things you might be interested in",
				"subTotalPerPeriodPrefix": "Sub Total (Per",
				"subTotalPerPeriodSuffix": ")",
				"totalPerPeriodPrefix": "Total (Per",
				"totalPerPeriodSuffix": ")",
				"taxIncludedPerPeriodPrefix": "Tax Included (Per",
				"taxIncludedPerPeriodSuffix": ")",
				"taxExcludedPerPeriodPrefix": "Tax (Per",
				"taxExcludedPerPeriodSuffix": ")",
				"timeAndDate": "Time and Date",
				"resetPasswordEmailSent": "Reset Password Email Sent. If you have an account, you will receive an email shortly. Please check your Spam/Junk if you cannot see the email in your inbox.",
				"consentAgree": "I agree",
				"goBack": "Go back",
				"currentPlan": "Current plan",
				"checkoutSuccessful": "Checkout Successful",
				"orderSummary": "Order Summary",
				"secureCheckout": "Secure Checkout",
				"trialInfoPrefix": "This package comes with a",
				"trialInfoSuffix": "",
				"alreadyHaveProviderLoginPrefix": "Do you have a",
				"alreadyHaveProviderLoginSuffix": "login?",
				"createLogin": "Create Login",
				"yes": "Yes",
				"no": "No",
				"planCancellationSuccess": "Please note that if you are on a recurring subscription, your membership will expire at the end of your current subscription period. You will receive an email shortly with confirmation of the exact timing of when your subscription ends.",
				"planCancellationError": "There was an error cancelling this licence",
				"planCancellationUnmanaged": "Because you bought this license on an app store, you cannot cancel it here. Please visit your account on your device to cancel.",
				"planCancellationTitleSuccess": "Cancellation confirmed",
				"planCancellationTitleUnmanaged": "Cancellation information",
				"planCancellationTryAgain": "Try again",
				"paymentId": "Payment ID",
				"subscriptionAlertErrorRestoreFailed": "Please ensure you are using the correct iTunes account and try again.",
				"subscriptionAlertErrorRestoreUserCanceled": "Please ensure you have not canceled the subscription and try again.",
				"subscriptionAlertErrorRestoreEmpty": "There are no active subscriptions to restore.",
				"subscriptionAlertRestoreTitleError": "Restore Failed",
				"subscriptionAlertRestoreTitleSuccess": "Restore Success",
				"subscriptionAlertRestoreMessageSuccess": "Your plan should be unlocked.",
				"subscriptionRestoreMessage": "If you have previously paid for one of these plans but don't see it unlocked above, please press okay to restore your purchase.",
				"ok": "OK",
				"restorePurchase": "Restore Purchase",
				"watchHere": "Watch Here",
				"currentlyWatching": "Currently watching",
				"buttonText": "Button Text",
				"wouldYouLikeToChangeLabel": "Would you like to change the language to",
				"newNotification": "New notification",
				"unavailableLicence": "Licenses unavailable",
				"unavailable": "Unavailable",
				"iosPolicyPart1": "Payment will be charged to your iTunes Account at confirmation of purchase",
				"iosPolicyPart2": "Your Subscription will automatically renew at the same price within 24 hours of the end of the selected period - if you wish to cancel you must do so more than 24 hours prior to the renewal date",
				"iosPolicyPart3": "You can manage your subscription, and turn off auto-renewal, in your Account Settings after purchase. If you are within a free-trial period and select a subscription that covers the same content package, the trial period will immediately expire.",
				"forMoreInformations": "For more information please see our",
				"privacyPolicyAndTerms": "Privacy Policy and Terms of Use.",
				"toWatchHereCloseOtherSessionsMobileVersion": "To watch here please close your other session",
				"chromeCast": "Chromecast",
				"information": "information",
				"playNext": "Play next",
				"goLive": "Go live",
				"connectingToChromecast": "Connecting to Chromecast...",
				"castingVideo": "Casting video.",
				"loadingVideo": "Loading video...",
				"connectedToChromecast": "Connected to Chromecast",
				"unfortunatelyYouAreOffline": "Unfortunately, you are offline. Please connect to the internet and try again",
				"unfortunatelyEventNotAvailableInLocation": "Unfortunately this event is not available in your current location",
				"anErrorOccured": "An error occured. Please try again.",
				"checkYourSubscriptions": "Please check your subscriptions",
				"selectAPlan": "Select a plan",
				"expires": "Expires",
				"expired": "Expired",
				"goToSettingsToChangePreferences": "Go to settings to change your preferences",
				"zipPostCode": "zip / post code",
				"zipPostCodeError": "please provide a zip / post code",
				"provideYourStateOfResidence": "please provide your state of residence",
				"outOfDateBrowserPrefix": "Apologies - you're currently trying to access this website using an unsupported browser (",
				"outOfDateBrowserSuffix": ")",
				"noContentAvailable": "No content available",
				"signUpStep3Title": "Step 3 - Complete Your Checkout",
				"cardNumber": "Card Number",
				"singUoComplete": "Sign up complete",
				"paySecurely": "Pay securely",
				"firstNameFieldIsRequired": "Please provide your first name",
				"lastNameFieldIsRequired": "Please provide your surname",
				"lastName": "Last name",
				"addressFieldIsRequired": "Please select a country",
				"administrativeLevel1FieldIsRequired": "Please provide your state of residence",
				"postalCode": "Please provide a zip / post code",
				"phoneNumberField": "Phone Number",
				"phoneNumberFieldIsRequired": "Phone number",
				"line1FieldIsRequired": "Please provide your address",
				"termsOfUse": "Consents",
				"contentLocked": "Content locked",
				"postalCodeFieldIsRequired": "Please provide a zip / post code",
				"phoneNumber": "Phone Number",
				"secretTooCommon": "Password is too common",
				"subscriptions": "Subscriptions",
				"toManageYourSubscriptions": "To manage your subscriptions please visit the",
				"website": "website",
				"perYear": "per Year",
				"paymentCardError": "There has been an error charging your card",
				"paymentInvalidExpiryMonth": "Invalid card expiry month",
				"paymentInvalidExpiryYear": "Invalid card expiry year",
				"paymentInvalidCardNumber": "Incorrect card number",
				"paymentExpiredCard": "This card has expired",
				"paymentInvalidCvc": "Incorrect card security code",
				"paymentInvalidZip": "Card zip code has failed validation",
				"paymentCardDeclined": "This card has been declined",
				"paymentCardMissing": "There was an error using this card",
				"paymentError": "An error has occurred",
				"paymentInvalidExpiry": "Invalid card expiration",
				"secretLength": "Secret length must be between 4 and 256 characters",
				"waitFor": "Wait for",
				"seconds": "seconds",
				"failed": "Failed",
				"cancelled": "Cancelled",
				"view": "View",
				"print": "Print",
				"total": "Total",
				"singUpComplete": "Sign up complete",
				"addCard": "Add Card",
				"invoice": "Invoice",
				"newsList": "Latest news",
				"resetPassword": "Reset password",
				"paymentFailed": "Payment Failed",
				"deleteCard": "Remove a payment method",
				"cardDeletionPrompt": "Are you sure you want to remove this card?",
				"delete": "Remove",
				"skipThisStep": "Skip this step",
				"planCancellationClose": "Close",
				"paySecurelySuffix": "",
				"firstNameField": "First name",
				"lastNameField": "Last name",
				"addressField": "Country",
				"billingAddressRequired": "Billing address required",
				"preferedLanguage": "Preferred Language",
				"iOSPrivacyPolicy": "",
				"next": "Next",
				"play": "Play",
				"passwordResetRequestSent": "If you have an account, you will receive an email shortly. Please check your Spam/Junk if you cannot see the email in your inbox.",
				"passwordResetReceived": "Password reset request received",
				"info": "Info",
				"pressUp": "Press up for info",
				"forgottenPasswordEmailRequest": "Please enter your email address to receive a forgotten password link",
				"playFromBeginning": "Play from beginning",
				"invalidUsernamePasswordCombination": "Email or password is incorrect",
				"passwordMustBeBetween4And256Characters": "Your password must be between 6 and 24 characters.",
				"signupGeoRestriction": "Unfortunately this service is not available in your region.",
				"passwordTooCommon": "Password is too common",
				"noContentAvailableSchedule": "No events are currently scheduled",
				"passwordIsTooCommon": "Password is too common",
				"failedAuthentication": "Authentication failed",
				"emailMustBeInAscii": "Please provide a valid email address",
				"proceed": "Proceed",
				"devicesAvailable": "Devices Available",
				"connectToDevice": "Connect to a device",
				"fullName": "Full name",
				"preferredName": "Preferred name",
				"fullNameFieldIsRequired": "Full name is required",
				"fullNameFieldIsInvalid": "Full name is invalid",
				"preferredNameFieldIsRequired": "Preferred name",
				"preferredNameFieldIsInvalid": "Preferred name is invalid",
				"phoneNumberFieldIsInvalid": "Phone number is invalid",
				"cardAlias": "Alias",
				"Cvc": "CVC",
				"payNowWith": "Pay now with",
				"nowPlaying": "Now Playing",
				"invalidResetPasswordToken": "Password reset failed",
				"videoPlayingOnYourDevice": "This video is playing on another device",
				"loginWithPhone": "Phone",
				"cardExpiryPlaceholder": "MM / YY",
				"disconnectFromAirplay": "Disconnect from Airplay",
				"disconnect": "Disconnect",
				"chromeCastContentNotAvailable": "There was an error Chromecasting this video",
				"billingDetailsRequired": "Billing details required",
				"iosPPVPolicyPart2": "Access to content will be expire after the event has concluded, as per the date on the your payment invoice receipt. If you choose to cancel before the event period has expired, you will lose access to the content immediately.",
				"iosPPVPolicyPart1": "This package is a one time purchase, providing access to all content outlined within the description above.",
				"signupWith": "Sign up with",
				"doyouwanttoexit": "Do you want to exit?",
				"taxPerYear": "(per year)",
				"taxPerMonth": "(per month)",
				"taxPerDay": "(per day)",
				"testLabel": "Test Label - editing on realm. Making the label extra long to see how the text box behaves. Making the label extra long to see how the text box behaves.Making the label extra long to see how the text box behaves.",
				"logoutConfirmation": "Are you sure you want to logout?",
				"paidOrder": "Payment Successful",
				"licenceDowngrade": "Downgrade",
				"payWithCreditCard": "Pay with credit card",
				"cancelSubscription": "Customer Cancellation",
				"disputeCreated": "Payment Disputed",
				"videoIsNotLive": "This event is not live",
				"payWithBrowser": "Pay with browser",
				"licenceRevoked": "License Revoked",
				"failedPayment": "Payment Failed",
				"trialStarted": "Trial Started",
				"disputeUpdated": "Payment Dispute Updated",
				"licenceUpgrade": "Upgrade",
				"paidInvoice": "Payment Successful",
				"licenceRenewal": "Subscription Renewed",
				"licenceCancelled": "License Cancelled",
				"disputeClosed": "Payment Dispute Closed",
				"Play": "Oynat",
				"Are you sure you want to exit?": "��k�_ Yapmak _stedi_inizden Emin Misiniz?",
				"Play from beginning�": "Ba_tan Oynat",
				"Video Annotations": "Video Annotations",
				"Set primary": "�Ana Kart",
				"videoAnnotations": "Video Annotations",
				"manageCards": "Manage cards",
				"setPrimary": "Set primary",
				"trialInfo": "This package comes with a {{trialDuration}} day free trial and you can cancel at any time. The payment will not be processed until",
				"alreadyHaveProviderLogin": "Do you have a {{providerName}} login?",
				"addCardDescription": "Enter your new card details below",
				"requiredField": "*Required",
				"fullNameField": "Full Name",
				"annotationsOff": "Off",
				"annotationsOn": "On",
				"annotations": "Annotations",
				"dismiss": "Dismiss",
				"save": "Save",
				"selectLanguage": "Select a Language",
				"applicationText": "Application Text",
				"confirmLanguageSelection": "Are you sure you would like to change your app language?",
				"addressIsInvalid": "Invalid address",
				"appBannerStoreAndroid": "In Google Play",
				"generalCardError": "General Card Error",
				"tvContentPadlockedDescription": "Please visit the {{appName}} Website or Mobile application to manage your subscription and unlock this content.",
				"preferencesUpdated": "Your preferences have been updated",
				"minute": "minute",
				"appBannerView": "View",
				"vpnErrorMessage": "Unfortunately this service is unavailable if you are using a VPN",
				"morevods": "More Videos",
				"playingInPictureInPicture": "This video is playing in the pop out player",
				"apply": "Apply",
				"sessionExpiredMessage": "rid: {{requestId}}",
				"licenceWillExpire": "Cancels {{expiry}}",
				"marketingPreferences": "Marketing Preferences",
				"postalCodeIsInvalidSize": "Postal code is too long",
				"watch": "Watch",
				"cardCVCPlaceholder": "0",
				"more": "More",
				"appBannerOpen": "Open",
				"appVersionOutdated": "This version of {{appName}} is no longer supported.",
				"signUpSplashSub": "",
				"rokuConsentFormdceseriea": "",
				"rokuConsentFormdcefivb": "",
				"hours": "hours",
				"useVoucher": "Promo code?",
				"day": "day",
				"noResultsSubtitle": "Please refine your search and try again.",
				"chromecastErrorHelp": "Please disconnect and reconnect your Chromecast device and try again.",
				"rokuConsentFormdcessport": "",
				"invalidVoucher": "Invalid voucher",
				"viewAll": "View All",
				"noLanguageSelected": "None",
				"invalidVideoSource": "Invalid video source",
				"appBannerStoreIos": "On the App Store",
				"discountedTotal": "Discounted total",
				"payWithPaypal": "Pay with Paypal",
				"exitConfirmation": "Are you sure you want to exit?",
				"descriptionUnavailable": "Description unavailable",
				"updatePreferencesButton": "Update Preferences",
				"moreInfo": "More Info",
				"days": "days",
				"emailIsInvalid": "Please provide a valid email address",
				"exitPictureInPicture": "Close Player",
				"payWithKnownBrowser": "Pay with",
				"minutes": "minutes",
				"thisEventHasEnded": "This event has ended",
				"chooseSubtitleLanguage": "Choose Subtitle Language",
				"errorCode": "Error code:",
				"emptyVoucher": "Voucher code cannot be empty.",
				"username": "Username",
				"tvContentPadlocked": "This content is not available",
				"appVersionUpdate": "Please visit the {{platform}} to update to the latest version",
				"searchFilterLiveEvent": "Live Event",
				"rokuConsentForm": "Lorem ipsum is a pseudo-Latin text used in web design, typography, layout, and printing in place of English to emphasise design elements over content. It's also called placeholder (or filler) text. It's a convenient tool for mock-ups. It helps to outline the visual elements of a document or presentation, eg typography, font, or layout. Lorem ipsum is mostly a part of a Latin text by the classical author and philosopher Cicero. Its words and letters have been changed by addition or removal, so to deliberately render its content nonsensical; it's not genuine, correct, or comprehensible Latin anymore.",
				"videoDeletionPrompt": "Are you sure you want to delete this video?",
				"rokuConsentFormdceimgott": "",
				"cardBeingAdded": "Your card is being added and will be available as a payment method shortly.",
				"sessionExpired": "Session invalid or expired",
				"autoplay": "Autoplay",
				"tryAgain": "Try again",
				"bankUnreachable": "We're unable to contact your bank. Please try again in a few minutes. If the issue persists, please contact your bank directly.",
				"chooseLanguage": "Choose Language",
				"searchFilterVoD": "Video On Demand",
				"rokuConsentFormdcesandbox": "",
				"addCardPaycell": "Add card",
				"appBannerPrice": "Free",
				"licenceWillRenew": "Will renew {{renewal}}",
				"appleTvErrorMessage": "Unfortunately we've been unable to load this page",
				"searchFilterPlaylist": "Video Playlist",
				"autoplayExplanation": "Autoplay next video once your video stops playing",
				"appUpdate": "Update {{appName}}",
				"rokuConsentFormdcepbr": "",
				"subtitlesOff": "Subtitles Off",
				"playbackRate": "Playback Rate",
				"redirectingToBankPage": "Redirecting to Bank Page",
				"preferredLanguage": "Preferred Language",
				"oneMoreVod": "More Video",
				"hour": "hour",
				"cardBeingAddedError": "Card Being Added Error",
				"signUpSplashHero": "",
				"second": "second",
				"voucherPlaceholder": "Voucher Code",
				"idIsInvalid": "Invalid email address",
				"rossLabel": "Ross",
				"downloading": "Downloading",
				"passwordIsRequired": "Password is required",
				"localeIsInvalid": "Invalid locale",
				"suspendedUser": "Suspended user",
				"noDownloadTitle": "No videos downloaded yet...",
				"upcomingJoinButton": "Watch now",
				"annotationsToggle": "Toggle annotations",
				"newLabelTest": "new label test",
				"accessibility1": "Accessibility",
				"searchFilterType": "Type",
				"offlineDownloadSuggestion": "Alternatively, if you have downloaded content you can watch it from the Downloads page.",
				"upcomingWatchButton": "Watch now",
				"upcomingLoadMore": "Show more",
				"cancelledSubscription": "Subscription Cancelled",
				"confirmedUserAlreadyExists": "User already exists",
				"downloads": "Downloads",
				"undefined": "Support",
				"rossTestKey": "label value 1",
				"test2": "test",
				"test1": "test",
				"goToDownloads": "Go to Downloads",
				"contactEmailField": "Contact Email",
				"pleaseProvideYourName": "Please provide your name",
				"downloaded": "Downloaded",
				"testLabel2": "lang 12345",
				"myNewKey": "myNewValues",
				"townIsRequired": "Town is required",
				"viewall": "View All",
				"events": "Fight Card",
				"countryCodeIsInvalid": "Invalid country code",
				"noCat": "no cat",
				"csvUploadedLabel": "Test Label 1 1",
				"deleteDownloadAlertDescription": "After deletion, this video will only be available to play online.",
				"newtest": "newtest test",
				"userTypeIsInvalid": "User type is invalid",
				"rossTest": "test value",
				"noDownloadDescription": "Downloading videos is a great way to make sure you always have something to watch.",
				"testNewLabel": "ross test new label",
				"rossTestLabel1": "Ross",
				"loginWithFacebook": "Facebook",
				"addressIsRequired": "Address is required",
				"yoyoyo": "yo yo yo",
				"addressTypeIsRequired": "Address type is required",
				"rossLabelTest2019": "Hello",
				"postCodeIsTooLong": "Post code is too long",
				"anotherNewLabel": "newLabelTests",
				"deleteDownloadAlertTitle": "Are you sure you'd like to delete this video?",
				"stateIsInvalid": "Invalid state",
				"view-all": "View All",
				"download": "Download",
				"moreVodsPlaylist": "More Videos",
				"secretIsInvalid": "Invalid secret",
				"secretIsRequired": "Secret is required",
				"fightCardsTab": "Fight card",
				"upcomingHideDetails": "Hide event details",
				"doesNotLackConfirmation": "Does not lack confirmation",
				"mobileConnectTextMobile": "Fast Login",
				"upcomingViewDetails": "View event details",
				"labelIsRequired": "Label is required",
				"newTestLabel": "test value",
				"danUpload": "Upload a label 2",
				"athletes": "Athletes",
				"faq": "FAQ",
				"audio": "Audio",
				"fightPass": "Fight Pass",
				"termsOfService": "Terms of Service",
				"privacyPolicy": "Privacy Policy",
				"muteIcon": "Mute (m)",
				"alreadyPurchasedSubscription": "Already purchased a subscription?",
				"pauseIcon": "Pause (k)",
				"playIcon": "Play (k)",
				"fullScreenOnIcon": "Full screen (f)",
				"licenceWillExpireTomorrow": "Will expire tomorrow",
				"fullScreenOffIcon": "Exit full screen (f)",
				"informationIcon": "Information",
				"previousIcon": "Previous",
				"chromeCastIcon": "Chromecast",
				"done": "Done",
				"autoPlayOff": "Off",
				"reactivatePlan": "Reactivate Plan",
				"licenceWillExpireOn": "Will expire on {{expiry}}",
				"searchIcon": "Search",
				"licenceWillRenewTomorrow": "Will renew tomorrow",
				"licenceWillRenewOn": "Will renew on {{renewal}}",
				"unMuteIcon": "Unmute (m)",
				"off": "Off",
				"settingsIcon": "Settings",
				"nextIcon": "Next",
				"miniPlayerIcon": "Mini player",
				"reload": "Reload",
				"active": "Active",
				"autoPlayOn": "On",
				"subtitles": "Subtitles "
			}
		}
	};
}());
;
window.EFL = window.EFL || {};

window.EFL.DiceRealmLabels = (function(EFL) {
	'use strict';

	function DiceRealmLabels() {
	}

	DiceRealmLabels.prototype.getLabel = function(labelCode) {
		return window.EFL.Dice.labels[labelCode];
	}

	DiceRealmLabels.prototype.getLabels = function (labelCodes) {
		var labels = [];

		if (!labelCodes || !labelCodes.length) {
			return labels;
		}

		for (var i = 0; i < labelCodes.length; i++) {
			var label = this.getLabel(labelCodes[i]);
			labels.push(label);
		}

		return labels;
	}


	// eslint-disable-next-line vars-on-top
	var instance;
	// eslint-disable-next-line vars-on-top
	var _static = {
		name: "DiceRealmLabels",

		getInstance: function(options) {
			if ( instance === undefined ) {
				instance = new DiceRealmLabels(options);
			}

			return instance;
		}
	};

	return _static;
}(window.EFL));
;
window.EFL = window.EFL || {};

window.EFL.DiceStorage = (function() {
	'use strict';

	var keyPrefix = '@@dice@@';
	var createKey = function (e) {
		return (keyPrefix + "::" + e.app + "::" + e.realm);
	};

	/**
	 * Wraps the specified cache entry and returns the payload
	 * @param entry The cache entry to wrap
	 */
	var wrapCacheEntry = function (entry) {
		//var expirySeconds = entry.decodedToken.exp - 60; // take off a small leeway
		var expirySeconds = (Math.floor(Date.now() / 1000) + (entry.decodedToken.exp - entry.decodedToken.iat)) - 60;
		return {
			body: entry,
			expiresAt: expirySeconds
		};
	};

	function DiceStorageCache() {
		// ES5 version of `function DiceStorageCache(options = { cacheLocation: 'session' }) {`
		var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {
			cacheLocation: 'session'
		};

		this.storage = undefined;
		this.setStorageLocation(options.cacheLocation);
	}

	DiceStorageCache.createKey = function (entry) {
		return createKey(entry);
	};

	/**
	 * Sets the storage location.
	 * Possible options:
	 *  - localStorage
	 *  - sessionStorage (default)
	 */
	DiceStorageCache.prototype.setStorageLocation = function (location) {
		if (location === 'session') {
			this.storage = window.sessionStorage;
		} else if (location === 'local') {
			this.storage = window.localStorage;
		} else {
			this.storage = window.sessionStorage;
		}

		console.info('[DiceStorageCache] cache location: ' +  location);
	};
	
	DiceStorageCache.prototype.save = function (entry) {
		var cacheKey = createKey(entry);
		var payload = wrapCacheEntry(entry);
		
		this.storage.setItem(cacheKey, JSON.stringify(payload));
	};

	DiceStorageCache.prototype.saveFull = function (entry, payload) {
		var cacheKey = createKey(entry);
		
		this.storage.setItem(cacheKey, JSON.stringify(payload));
	};

	DiceStorageCache.prototype.getItem = function (key) {
		var cacheKey = createKey(key);
		var payload = this.readJson(cacheKey);
		var nowSeconds = Math.floor(Date.now() / 1000);

		if (!payload)
			return;
		if (payload.expiresAt < nowSeconds) {
			if (payload.body.refresh_token) {
				var newPayload = this.stripData(payload);
				this.writeJson(cacheKey, newPayload);
				return newPayload.body;
			}
			this.storage.removeItem(cacheKey);
			return;
		}
		return payload.body;
	};

	// Get the full object from storage.
	// This was added to support the user state refresh. Ex: when you update your phonenumber, the user data needs to be updated.
	DiceStorageCache.prototype.getItemFull = function (key) {
		var cacheKey = createKey(key);
		var payload = this.readJson(cacheKey);

		if (!payload) {
			return;
		}
		
		return payload;
	};

	DiceStorageCache.prototype.clear = function () {
		for (var i = localStorage.length - 1; i >= 0; i--) {
			if (localStorage.key(i).startsWith(keyPrefix)) {
				localStorage.removeItem(localStorage.key(i));
			}
		}

		for (var j = sessionStorage.length - 1; j >= 0; j--) {
			if (sessionStorage.key(j).startsWith(keyPrefix)) {
				sessionStorage.removeItem(sessionStorage.key(j));
			}
		}
	};

	/**
	 * Retrieves data from local storage and parses it into the correct format
	 * @param cacheKey The cache key
	 */
	DiceStorageCache.prototype.readJson = function (cacheKey) {
		var json = this.storage.getItem(cacheKey);
		var payload;
		if (!json) {
			return;
		}
		payload = JSON.parse(json);
		if (!payload) {
			return;
		}
		// eslint-disable-next-line consistent-return
		return payload;
	};

	/**
	 * Writes the payload as JSON to localstorage
	 * @param cacheKey The cache key
	 * @param payload The payload to write as JSON
	 */
	DiceStorageCache.prototype.writeJson = function (cacheKey, payload) {
		this.storage.setItem(cacheKey, JSON.stringify(payload));
	};

	/**
	 * Produce a copy of the payload with everything removed except the refresh token
	 * @param payload The payload
	 */
	DiceStorageCache.prototype.stripData = function (payload) {
		var refresh_token = payload.body.refresh_token;
		var authorisationToken = payload.body.access_token || payload.body.authorisationToken; // 1. This will be needed to call the refresh API (┛◉Д◉)┛彡┻━┻
		var cachedUser = payload.body.user; // 2. This will be needed to store back the user data on token refresh
		var strippedPayload = {
			body: {
				refresh_token: refresh_token,
				authorisationToken: authorisationToken, // 1. Renamed it to fix any func that will search for it.
				user:  cachedUser // 2. Renamed it to fix any func that will search for it.
			},
			expiresAt: payload.expiresAt
		};

		return strippedPayload;
	};

	return DiceStorageCache;
})();

;
window.EFL = window.EFL || {};

/**
 * A simple, lightweight module for handling cookies and storing Dice related information
 * 
 * getAllKeys: Returns an object containing all visible cookies.
 * get:    Returns a single cookie with the specified name, or undefined if the cookie doesn't exist.
 * save:   Creates a new cookie.
 *         The first parameter is for the name, and the second for the value.
 *         The third parameter is optional and allows you to modify attributes for the new cookie.
 * remove: Deletes a single cookie by name.
 */
window.EFL.DiceClientStorage = (function() {
	"use strict";

	// eslint-disable-next-line max-len
	function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; }
	// eslint-disable-next-line max-len
	function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
	// eslint-disable-next-line max-len
	function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }

	function stringifyAttribute(name, value) {
		if (!value) {
			return '';
		}

		var stringified = '; ' + name;

		if (value === true) {
			return stringified; // boolean attributes shouldn't have a value
		}

		return stringified + '=' + value;
	}

	function stringifyAttributes(attributes) {
		if (typeof attributes.expires === 'number') {
			var expires = new Date();
			expires.setMilliseconds(expires.getMilliseconds() + attributes.expires * 864e+5);
			attributes.expires = expires;
		}

		return stringifyAttribute('Expires', attributes.expires ? attributes.expires.toUTCString() : '') + stringifyAttribute('Domain', attributes.domain) + stringifyAttribute('Path', attributes.path) + stringifyAttribute('Secure', attributes.secure) + stringifyAttribute('SameSite', attributes.sameSite);
	}

	function encode(name, value, attributes) {
		return encodeURIComponent(name).replace(/%(23|24|26|2B|5E|60|7C)/g, decodeURIComponent) // allowed special characters
			.replace(/\(/g, '%28').replace(/\)/g, '%29') // replace opening and closing parens
		+ '=' + encodeURIComponent(value) // allowed special characters
			.replace(/%(23|24|26|2B|3A|3C|3E|3D|2F|3F|40|5B|5D|5E|60|7B|7D|7C)/g, decodeURIComponent) + stringifyAttributes(attributes);
	}

	function parse(cookieString) {
		var result = {};
		var cookies = cookieString ? cookieString.split('; ') : [];
		var rdecode = /(%[\dA-F]{2})+/gi;

		for (var i = 0; i < cookies.length; i++) {
			var parts = cookies[i].split('=');
			var cookie = parts.slice(1).join('=');

			if (cookie.charAt(0) === '"') {
				cookie = cookie.slice(1, -1);
			}

			try {
				var name = parts[0].replace(rdecode, decodeURIComponent);
				result[name] = cookie.replace(rdecode, decodeURIComponent);
			} catch (e) {// ignore cookies with invalid name/value encoding
			}
		}

		return result;
	}

	function getAll() {
		return parse(document.cookie);
	}

	function get(name) {
		return getAll()[name];
	}

	function set(name, value, attributes) {
		document.cookie = encode(name, value, _objectSpread({
			path: '/'
		}, attributes));
	}

	function remove(name, attributes) {
		set(name, '', _objectSpread({}, attributes, {
			expires: -1
		}));
	}

	function CSGetAllKeys() {
		return Object.keys(getAll() || {});
	}

	function CSGet(key) {
		var value = get(key);
		if (typeof value === 'undefined') {
			return;
		}
		return JSON.parse(value);
	}

	function CSSet(key, value, options) {
		set(key, JSON.stringify(value), {
			expires: options.daysUntilExpire
		});
	}

	function CSRemove(key) {
		remove(key);
	}

	return {
		getAllKeys: CSGetAllKeys,
		get: CSGet,
		save: CSSet,
		remove: CSRemove,
	};
})();
;
window.EFL = window.EFL || {};

window.EFL.DiceAuth = (function(EFL) {
	'use strict';

	/**
	 * Dice auth library. Used for logining in, tokern refresh and so on.
	 */
	function DiceAuth() {
		var cacheFactory;

		this.API_URL_BASE = EFL.Dice.apiUrl + '/api/v2/';
		this.API_HEADERS = {
			"realm": EFL.Dice.realm,
			"x-api-key": EFL.Dice.key,
			"app": EFL.Dice.app,
			"Content-Type": "application/json"
		};
		this.HTTP = EFL.HTTP;

		cacheFactory = this._cacheFactory();
		this.cache = cacheFactory.cache;
		this.cacheLocation = cacheFactory.cacheLocation;
		this.requestCache = {};

		this.ClientStorage = EFL.DiceClientStorage;

		this.tokensKeys = {
			'access_token': 'authorisationToken',
			'refresh_token': 'refreshToken'
		};
		this.storageKey = 'auth_token_default';
		this.authCookieName = 'dice.is.authenticated';
		this.authenticated = null;
		this.partitionCode = EFL.Dice.partitionCode;
	}

	DiceAuth.prototype._cacheFactory = function() {
		var key = EFL.DiceStorage.createKey({
			app: EFL.Dice.app,
			realm: EFL.Dice.realm
		});

		if (window.localStorage.getItem(key)) {
			return {
				cache: new EFL.DiceStorage({ cacheLocation: 'local' }),
				cacheLocation: 'local'
			};
		}

		return {
			cache: new EFL.DiceStorage(),
			cacheLocation: 'session'
		};
	};

	DiceAuth.prototype._setCache = function(cacheLocation) {
		if (this.cacheLocation !== cacheLocation) {
			this.cache = new EFL.DiceStorage({ cacheLocation: cacheLocation });
			this.cacheLocation = cacheLocation;
		}
	};

	DiceAuth.prototype._setAuthenticated = function(authenticated) {
		this.authenticated = authenticated;
	};

	DiceAuth.prototype._parseLoginData = function(data) {
		return data.data || {};
	};

	DiceAuth.prototype._getTokenUsingRefreshToken = function() {
		var options = {
			app: EFL.Dice.app,
			realm: EFL.Dice.realm
		};
		var cache = this.cache.getItem(options);
		var tokenRefreshPromise;

		// If access tokens are missing (user never logged in, or cleared data)
		// get new tokens via gues login to be able to call APIs.
		if (!cache || !cache.refresh_token) {
			return new Promise(function(resolve, reject) {
				this.guestLogin()
					.then(function(guestLoginResult) {
						var decodedToken = jwt_decode(guestLoginResult.response[this.tokensKeys.access_token]);
						resolve({
							access_token: guestLoginResult.response[this.tokensKeys.access_token],
							refresh_token: guestLoginResult.response[this.tokensKeys.refresh_token],
							decodedToken: decodedToken
						});
					}.bind(this))
					.catch(function(error) {
						reject(error);
					});
			}.bind(this));
		}

		// eslint-disable-next-line vars-on-top
		var tokenRefreshPromiseCache = this.requestCache['token/refresh'];
		if (tokenRefreshPromiseCache && tokenRefreshPromiseCache.state === 'pending') {
			console.info('[DiceAuth] `token/refresh` pending reuqest hit, returning current pending promise.');

			return tokenRefreshPromiseCache.promise;
		}

		// User has a refresh token, logged in or guest login was used at least once.
		tokenRefreshPromise = new Promise(function(resolve, reject) {
			var API_HEADERS = _.clone(this.API_HEADERS);
			API_HEADERS['Authorization'] = 'Bearer ' + cache.authorisationToken;

			this.HTTP.post(this.API_URL_BASE + 'token/refresh', {
				headers: API_HEADERS,
				data: JSON.stringify({ 'refreshToken': cache.refresh_token }),
			})
				.then(function(refreshResult) {
					var decodedToken = jwt_decode(refreshResult.response[this.tokensKeys.access_token]);

					this.requestCache['token/refresh'].state = 'fulfilled';

					resolve({
						access_token: refreshResult.response[this.tokensKeys.access_token],
						refresh_token: refreshResult.response[this.tokensKeys.refresh_token],
						decodedToken: decodedToken
					});
				}.bind(this))
				.catch(function (error) {
					var isErrorRefreshingToken = error.response.messages.findIndex(function(errCode) { return errCode === 'errorRefreshingToken'; });

					this.requestCache['token/refresh'].state = 'rejected';

					// If there is an error with the refresh token (probably expired - has a TTL of 2 months)
					if (isErrorRefreshingToken > -1 || error.status === 404) {
						this.cache.clear();
						this.ClientStorage.remove(this.authCookieName);

						reject({
							error: 'login_required'
						});
					} else {
						reject(error);
					}
				}.bind(this));
		}.bind(this));

		this.requestCache['token/refresh'] = {
			promise: tokenRefreshPromise,
			state: 'pending',
		};

		return tokenRefreshPromise;
	};

	DiceAuth.prototype.getAPIHeaders = function() {
		return this.API_HEADERS;
	};

	DiceAuth.prototype.getUser = function() {
		var options = {
			app: EFL.Dice.app,
			realm: EFL.Dice.realm
		};
		var cache = this.cache.getItem(options);

		// If tokens expire logged in cookie is not cleared. This ensures that.
		if (!cache || !cache.user) {
			if (this.ClientStorage.get(this.authCookieName)) {
				this.ClientStorage.remove(this.authCookieName);
				if (window.location.pathname.includes("/my-account/")) {
					window.location.replace("/my-account/");
				}
				
			}
		}

		return cache && cache.user;
	};

	// Because we are storing user info into local/session storage, we need a mechanism to update the info
	// when the user updates it via account mgmt page. This function here has been built for that purpose.
	// It retreives the full obj from storage and updates the parts it needs then saves it back.
	// TODO: Not sure this is an OK aproach. Worth exploring more resilient and abstract ways.
	DiceAuth.prototype.refreshUserData = function(data) {
		var cacheEntry = {
			app: EFL.Dice.app,
			realm: EFL.Dice.realm
		};
		var cachedData = this.cache.getItemFull(cacheEntry);
		if (!cachedData.body.user.name) {
			cachedData.body.user.name = {};
		}

		cachedData.body.user.phoneNumber = data.phoneNumber;
		cachedData.body.user.name.fullName = data.fullName;
		cachedData.body.user.name.preferredName = data.preferredName;

		this.cache.saveFull(cacheEntry, cachedData);
	};

	DiceAuth.prototype.getDecodedTokenData = function() {
		var options = {
			app: EFL.Dice.app,
			realm: EFL.Dice.realm
		};
		var cache = this.cache.getItem(options);

		return cache && cache.decodedToken;
	};

	DiceAuth.prototype.getProfile = function(access_token) {
		return new Promise(function(resolve, reject) {
			var API_HEADERS = _.clone(this.API_HEADERS);
			API_HEADERS['Authorization'] = 'Bearer ' + access_token;

			this.HTTP.get(this.API_URL_BASE + 'user/profile', {
				headers: API_HEADERS
			})
				.then(function(response) {
					resolve(response);
				})
				.catch(function(error) {
					reject(error);
				});
		}.bind(this));
	};

	DiceAuth.prototype.isAuthenticated = function() {
		var user;

		if (this.authenticated) {
			return true;
		} else {
			user = this.getUser();
		}

		return !!user;
	};

	/**
	 * If there's a valid token stored, return it. Otherwise, the token endpoint
	 * is called directly with the 'refreshToken' grant.
	 */
	DiceAuth.prototype.getTokenSilently = function() {
		var cache = this.cache.getItem({
			app: EFL.Dice.app,
			realm: EFL.Dice.realm
		});

		// User logged in / guest logged in and access token is not expired
		if (cache && cache.access_token) {
			return Promise.resolve({
				access_token: cache.access_token,
				refresh_token: cache.refresh_token
			});
		}

		return new Promise(function(resolve, reject) {
			this._getTokenUsingRefreshToken()
				.then(function (authResult) {
					// Update refresh token with new one to allow access to be perpetually extended.
					var cacheEntry = {
						app: EFL.Dice.app,
						realm: EFL.Dice.realm,
						decodedToken: authResult.decodedToken,
						access_token: authResult.access_token,
						refresh_token: authResult.refresh_token ? authResult.refresh_token : _.get(cache, 'refresh_token', authResult.refresh_token),
						user: cache ? cache.user : undefined
					};

					this.cache.save(cacheEntry);

					resolve({
						access_token: cacheEntry.access_token,
						refresh_token: cacheEntry.refresh_token
					});
				}.bind(this))
				.catch(function(error) {
					if (error.error === 'login_required') {
						this.getTokenSilently()
							.then(function(tokens) {
								resolve(tokens);
							})
							.catch(function(err) {
								reject(err);
							});
					} else {
						reject(error);
					}
				}.bind(this));
		}.bind(this));
	};

	DiceAuth.prototype.processUser = function(profile, access_token, refresh_token, useSessionStorage) {
		var decodedToken = jwt_decode(access_token);
		var cacheEntry = {
			app: EFL.Dice.app,
			realm: EFL.Dice.realm,
			decodedToken: decodedToken,
			access_token: access_token,
			refresh_token: refresh_token,
			user: profile
		};

		// Default behaviour has been reveserd so store into local storage but if checkbox is ticked use session storage
		if (!useSessionStorage) {
			this.cache.clear(); // If we have anything in session cleanup first.
			this._setCache('local');
		}

		this.cache.save(cacheEntry);
		this.ClientStorage.save(this.authCookieName, true, { daysUntilExpire: 1 });
		this._setAuthenticated(true);
	};

	DiceAuth.prototype.login = function(formData) {
		return new Promise(function(resolve, reject) {
			this.HTTP.post(this.API_URL_BASE + 'login', {
				headers: this.API_HEADERS,
				data: JSON.stringify({ "id": formData.username, "secret": formData.password }),
			})
				.then(function(response) {
					var reponseData = this._parseLoginData(response);
					var access_token = reponseData[this.tokensKeys.access_token];
					var refresh_token = reponseData[this.tokensKeys.refresh_token];

					//Check if signing to the same partition as the account registered with
					var accountPartition = unescape(jwt_decode(access_token).rpd).split(":")[1];
					if (accountPartition !== this.partitionCode && accountPartition !== undefined) {
						this._setAuthenticated(false);
						return reject(_.assignIn(new Error('Partition does not match'), { authenticated: this.authenticated })); //TODO: add a realm lable for this.
					}

					//create profile object from form

					return this.getProfile(access_token)
						.then(function(profile) {
							// Creates and stores the user data (profile, access tokens)
							this.processUser(profile.data, access_token, refresh_token, formData.rememberLogin);
							resolve(_.assignIn(response, { authenticated: this.authenticated }));
						}.bind(this));
				}.bind(this))
				.catch(function(error) {
					this._setAuthenticated(false);
					reject(_.assignIn(error, { authenticated: this.authenticated }));
				}.bind(this));
		}.bind(this));
	};

	DiceAuth.prototype.register = function(formData, token, additionalFields) {
		return new Promise(function(resolve, reject) {
			var API_HEADERS = _.clone(this.API_HEADERS);
			var payload = {
				email: formData.email,
				secret: formData.secret,
				meta_fields: {
					address: {
						countryCode: formData.country,
						postalCode: formData.postcode
					},
					firstName: formData.firstName,
					lastName: formData.lastName,
					birthDate: formData.birthDate,
					phoneNumber: formData.phoneNumber
				},
				partitionData: {
					clb: [this.partitionCode],
				}
			};
			_.assign(payload.meta_fields.address, additionalFields)


			this.HTTP.post(this.API_URL_BASE + 'user', {
				headers: token ? _.assignIn(API_HEADERS, { 'Authorization': 'Bearer ' + token }) : API_HEADERS,
				data: JSON.stringify(payload),
			})
				.then(function(response) {
					var reponseData = this._parseLoginData(response);
					var access_token = reponseData[this.tokensKeys.access_token];
					var refresh_token = reponseData[this.tokensKeys.refresh_token];

					// profile should be the same format as data retuned by getProfile()
					// can't call getProfile() yet as account is not always created instantly
					var profile = {
						birthDate: formData.birthDate,
						contactEmail: formData.email,
						createdDate: Date.now(),
						id: formData.email,
						name: {
							fullName: formData.firstName + " " + formData.lastName,
							preferredName: formData.firstName + " " + formData.lastName
						},
						phoneNumber: formData.phoneNumber
					};
					this.processUser(profile, access_token, refresh_token);
					resolve(_.assignIn(response, { authenticated: this.authenticated }));

				}.bind(this))
				.catch(function(error) {
					reject(_.assignIn(error, { authenticated: this.authenticated }));
				}.bind(this));
		}.bind(this));
	};

	// Used for logging out for example.
	DiceAuth.prototype.revokeUserToken = function() {
		var options = {
			app: EFL.Dice.app,
			realm: EFL.Dice.realm
		};
		var cache = this.cache.getItem(options);

		if (!cache || !cache.refresh_token) {
			return Promise.reject({
				error: 'login_required'
			});
		}

		this.cache.clear();
		this.ClientStorage.remove(this.authCookieName);

		return new Promise(function(resolve, reject) {
			var API_HEADERS = _.clone(this.API_HEADERS);
			API_HEADERS['Authorization'] = 'Bearer ' + cache.access_token || cache.authorisationToken;

			this.HTTP.post(this.API_URL_BASE + 'user/token/delete', {
				headers: API_HEADERS,
				data: JSON.stringify({ "token": cache.refresh_token }),
			})
				.then(function(response) {
					resolve(response);
				})
				.catch(function(error) {
					reject(error);
				});
		}.bind(this));
	};

	DiceAuth.prototype.guestLogin = function() {
		var guestCheckinPromise;
		var guestCheckinPromiseCache = this.requestCache['login/guest/checkin'];
		if (guestCheckinPromiseCache && guestCheckinPromiseCache.state === 'pending') {
			console.info('[DiceAuth] `login/guest/checkin` pending reuqest hit, returning current pending promise.');
			return guestCheckinPromiseCache.promise;
		}

		// User has a refresh token, logged in or guest login was used at least once.
		guestCheckinPromise =  new Promise(function(resolve, reject) {
			this.HTTP.post(this.API_URL_BASE + 'login/guest/checkin', {
				headers: this.API_HEADERS,
				data: JSON.stringify({ "partitionData": { "clb": [this.partitionCode] } })
			})
				.then(function(response) {
					var reponseData = this._parseLoginData(response);
					var access_token = reponseData[this.tokensKeys.access_token];
					var refresh_token = reponseData[this.tokensKeys.refresh_token];

					var decodedToken = jwt_decode(access_token);
					var cacheEntry = {
						app: EFL.Dice.app,
						realm: EFL.Dice.realm,
						decodedToken: decodedToken,
						access_token: access_token,
						refresh_token: refresh_token
					};

					this.cache.save(cacheEntry);
					this.requestCache['login/guest/checkin'].state = 'fulfilled';

					resolve(response);
				}.bind(this))
				.catch(function(error) {
					this.requestCache['login/guest/checkin'].state = 'rejected';

					reject(error);
				}.bind(this));
		}.bind(this));

		this.requestCache['login/guest/checkin'] = {
			promise: guestCheckinPromise,
			state: 'pending',
		};

		return guestCheckinPromise;
	};

	// This will generate and send a reset password email
	DiceAuth.prototype.createResetPasswordEmail = function(email) {
		return new Promise(function(resolve, reject) {
			this.HTTP.post(this.API_URL_BASE + 'reset-password/create', {
				headers: this.API_HEADERS,
				data: JSON.stringify({ "id": email, "provider": "ID" }),
			})
				.then(function(response) {
					resolve(response);
				})
				.catch(function(error) {
					reject(error);
				});
		}.bind(this));
	};

	// Change the password for a user
	DiceAuth.prototype.resetPassword = function(newPassword, resetToken) {
		return new Promise(function(resolve, reject) {
			this.HTTP.put(this.API_URL_BASE + 'reset-password', {
				headers: this.API_HEADERS,
				data: JSON.stringify({
					"secret": newPassword,
					"provider": "ID",
					"token": resetToken
				}),
			})
				.then(function(response) {
					resolve(response);
				})
				.catch(function(error) {
					reject(error);
				});
		}.bind(this));
	};

	DiceAuth.prototype.withToken = function(func) {
		return function() {
			var _this = this;
			var args = new Array(arguments.length);

			for (var _len = arguments.length, _key = 0; _key < _len; _key++) {
				args[_key] = arguments[_key];
			}

			return EFL.DiceAuth.getTokenSilently()
				.then(function(tokens) {
					args.push(tokens);
				}).catch(function() {
					args.push(null);
				}).finally(function() {
					func.apply(_this, args);
				});
		};
	};

	return new DiceAuth();
})(
	window.EFL
);
;
window.EFL = window.EFL || {};

window.EFL.DiceVideoPlayer = (function ($, EFL) {
	"use strict";

	function DiceVideoPlayer() {
		this.apiHeaders = EFL.DiceAuth.getAPIHeaders();
	}

	DiceVideoPlayer.prototype.initPlayer = function(videoId, isLive, isAudio, mountPoint) {
		var _this = this;
		
		return EFL.DiceAuth.getTokenSilently()
			.then(function(tokens) {
				var config = {
					"apiKey": _this.apiHeaders['x-api-key'],
					"authToken": tokens.access_token,
					"baseUrl": EFL.Dice.apiUrl,
					"beaconUrl": EFL.Dice.beaconUrl,
					"mountPoint": mountPoint, // TODO: Terrible name for a ID selector
					"realm": _this.apiHeaders['realm'],
					"refreshToken": tokens.refresh_token
				};
		
				function loadVideo() {
					var loadRequest = {
						"id": Number(videoId),
						"isLive": isLive
					};
					//var userInterfaceConfig = isAudio ? 'audioOnly' : 'default';
					var userInterfaceConfig = isAudio ? { type: "audioOnly" } : { type: "default" };
		
					doris.load(loadRequest, userInterfaceConfig);
				}
				
		
				doris.init(config, function(error) {
					if (error) {
						_this.showError(mountPoint);
						console.error(error);
					} else {
						loadVideo();
					}
				});

				_this.trackEvents(videoId, isLive, isAudio);
			});
	};

	DiceVideoPlayer.prototype.showError = function(mountPoint) {
		var errorLabel = EFL.DiceRealmLabels.getInstance().getLabel('errorAccessingVideo');
		var errorTpl = '<div style="display:flex;justify-content:center;align-items:center;height:100%;">' + errorLabel + '</div>';
		
		$('#' + mountPoint).html(errorTpl);
	};

	DiceVideoPlayer.prototype.trackEvents = function (videoId, isLive, isAudio) {
		var _this = this;
		this._getVideoName(videoId, isLive).then(function (data) {
			var eventID = isAudio ? "audio-engagement" : "video-engagement";
			var eventCat = isAudio ? "Audio Engagement" : "Video Engagement";
			var eventName = data.title; // Video name
			var type = data.type; // VOD / Live
			var accessLevel = _this.getAccessLevelForVideo(videoId, isLive); // (Premium | Freemium | Free)
			
			function track(label) {
				window.EFL.analyticsController.track({
					event: eventID,
					category: eventCat,
					action: eventName,
					label: label,
					'video-type': accessLevel,
					'video-version': type
				});
			}

			var trackOnceList = [];
			function trackOnce(value) {
				if (!trackOnceList.includes(value)) {
					track(value);
					trackOnceList.push(value);
				}
			}
			
			doris.on('playing', function () {
				//only track play on inital play not on seeking or resume play
				trackOnce('Play');
			});

			doris.on('pause', function () {
				track('Pause');
			});

			//only track progress on vods
			if (!isAudio && !isLive) {
				//timeupdate runs multiple times per second, to stop 25% being fired 5+ times keep a list of tracked percentages so only 1 is sent.
				doris.on('timeupdate', function (data) {
					var percentage = Math.floor(data.progress * 100);
					if (percentage === 25) {
						trackOnce('25%');
					} else if (percentage === 50) {
						trackOnce('50%');
					} else if (percentage === 75) {
						trackOnce('75%');
					}
				});

				doris.on('ended', function () {
					track('100%');
				});
			}
		});
	};

	DiceVideoPlayer.prototype._getVideoName = function (videoId, isLive) {
		var _this = this;
		return new Promise(function (resolve, reject) {
			if (isLive) {
				_this._getEventById(videoId).then(function (data) {
					resolve(data.data);
				});
			} else {
				_this._getVodById(videoId).then(function (data) {
					resolve(data.data);
				});
			}
		});
	};

	DiceVideoPlayer.prototype._getEventById = function (eventId) {
		var _this = this;
		return new Promise(function (resolve, reject) {
			EFL.DiceAuth.getTokenSilently()
				.then(function (tokens) {
					var API_HEADERS = EFL.DiceAuth.getAPIHeaders();
					API_HEADERS['Authorization'] = 'Bearer ' + tokens.access_token;

					EFL.HTTP.get(EFL.DiceAuth.API_URL_BASE + 'event/' + eventId, {
						headers: API_HEADERS
					})
						.then(function (response) {
							resolve(response);
						})
						.catch(function (error) {
							console.error(error);
							reject(error);
						});
				})
				.catch(function (error) {
					console.error(error);
					reject(error);
				});
		});
	};

	DiceVideoPlayer.prototype._getVodById = function (vodId) {
		var _this = this;
		return new Promise(function (resolve, reject) {
			EFL.DiceAuth.getTokenSilently()
				.then(function (tokens) {
					var API_HEADERS = EFL.DiceAuth.getAPIHeaders();
					API_HEADERS['Authorization'] = 'Bearer ' + tokens.access_token;

					EFL.HTTP.get(EFL.DiceAuth.API_URL_BASE + 'vod/' + vodId, {
						headers: API_HEADERS
					})
						.then(function (response) {
							resolve(response);
						})
						.catch(function (error) {
							console.error(error);
							reject(error);
						});
				})
				.catch(function (error) {
					console.error(error);
					reject(error);
				});
		});
	};

	DiceVideoPlayer.prototype.getAccessLevelForVideo = function (videoId, isLive) {
		// Access level will be (Premium | Freemium | Free). This data is not available from an API so will have to get it from DOM / infer it.
		// All live events will be Premium.
		// Match replays will be Premium.
		// For VODs it should be set on the block and will be added to its element.
		var accessType;
		if (isLive) {
			accessType = "Premium";
		} else {
			if ($('[data-replay-vod=' + videoId + ']')) {
				accessType = "Premium";
			} else {
				accessType = $('.video[data-playvideo-id=' + videoId + ']').data('accessType'); // Standard VOD in grid
			}
		}
		return accessType;
	};

	return new DiceVideoPlayer();

}(window.jQuery, window.EFL));
;
window.EFL = window.EFL || {};

window.EFL.EFLLogin = (function($, EFL) {
	'use strict';

	/**
	 * Main EFLLogin function constructor function.
	 * @method EFLLogin
	 * @return {void}
	 */
	function EFLLogin() {
		this.elements = {
			'$loginFormContainer': '.c-login-form',
			'$loginForm': '#cLoginForm',
			'cLoginForm': 'cLoginForm',
			'$cForgotPasswordForm': '#cForgotPasswordForm',
			'cForgotPasswordForm': 'cForgotPasswordForm',
			'cForgotPasswordConfirm': 'cForgotPasswordConfirm',
			'$loginBtn': '.c-login-form .js-submit',
			'$forgotBtn': '.c-login-form .js-forgot',
			'$resetPassBtn': '.c-login-form .js-reset',
			'$resetPassCancelBtn': '.c-login-form .js-cancel',
			'$returnLoginBtn': '.c-login-form .js-return'
		};
		this.classes = {
			'formControlErrorLabelElClass': '.invalid-feedback'
		};
	}

	/**
	 * Initialize the module by calling all init functions, like events and so on.
	 */
	EFLLogin.prototype.init = function() {
		this.initEvents();
	};

	/**
	 * Initialise UI events related to login, forgot password.
	 * @method initEvents
	 * @return {void}
	 */
	EFLLogin.prototype.initEvents = function() {
		// Init Parsley validation on forms.
		var loginValidationInstance = $(this.elements.$loginForm).parsley();
		var forgotValidationInstance = $(this.elements.$cForgotPasswordForm).parsley();

		// Login event
		$(this.elements.$loginForm).on('submit', function() {
			if (loginValidationInstance.isValid()) {
				var loginFormElements = document.getElementById(this.elements.cLoginForm).elements;

				this.triggerUserLogin({
					diceInputEmail: loginFormElements.diceInputEmail.value,
					diceInputPassword: loginFormElements.diceInputPassword.value,
					diceRememberLogin: loginFormElements.diceInputRemember.checked
				});
			}

			// This prevents the form from auto submitting. Same as `event.preventDefault();`.
			return false;
		}.bind(this));

		// Forgot password event
		$(this.elements.$forgotBtn).on('click', function () {
			this.switchLoginForgotForms('forgotBtn');
		}.bind(this));
		// Forgot password event
		$(this.elements.$returnLoginBtn).on('click', function () {
			this.switchLoginForgotForms('returnLoginBtn');
		}.bind(this));

		$(this.elements.$resetPassCancelBtn).on('click', function () {
			this.switchLoginForgotForms();
		}.bind(this));

		$(this.elements.$cForgotPasswordForm).on('submit', function () {
			var forgotFormElements = document.getElementById(this.elements.cForgotPasswordForm).elements;

			if (forgotValidationInstance.isValid()) {
				this.triggerUserPasswordReset({ diceInputForgotEmail: forgotFormElements.diceInputForgotEmail.value }, forgotFormElements.diceInputForgotEmail);
			}

			// This prevents the form from auto submitting. Same as `event.preventDefault();`.
			return false;
		}.bind(this));
	};

	/**
	 * Show login form custom errors. The error will be displayed under the email field.
	 * @method showLoginError
	 * @param {String} errLabel - Error label to show
	 * @return {void}
	 */
	EFLLogin.prototype.showLoginError = function(errLabel) {
		var $emailInput = $(document.getElementById(this.elements.cLoginForm).elements.diceInputEmail);
		var $passwordInput = $(document.getElementById(this.elements.cLoginForm).elements.diceInputPassword);

		$emailInput.addClass('is-invalid');
		$passwordInput.addClass('is-invalid');
		$emailInput.siblings(this.classes.formControlErrorLabelElClass).text(errLabel);
	};

	EFLLogin.prototype.showPasswordResetError = function(errLabel) {
		var $emailInput = $(document.getElementById(this.elements.cForgotPasswordForm).elements.diceInputForgotEmail);

		$emailInput.addClass('is-invalid');
		$emailInput.siblings(this.classes.formControlErrorLabelElClass).text(errLabel);
	};

	/**
	 * Redirect user on login to the url passed by the backend on the data-redirect-uri attr.
	 * @method redirectLoggedIn
	 * @return {void}
	 */
	EFLLogin.prototype.redirectLoggedIn = function () {
		var searchParams = new URLSearchParams(window.location.search);
		var redirectUrl = $(this.elements.$loginFormContainer).data('redirect-uri');
		var sku = searchParams.get('sku');
		if (sku) {
			window.location.href = '/my-account/pay?sku=' + escape(sku) + '&redirect=' + escape(redirectUrl);
		} else if (redirectUrl && redirectUrl.replace(window.location.origin, '').indexOf("://") === -1) { //ensure redirect isn't another site
			window.location.replace(redirectUrl);
		} else {
			window.location.href = window.EFL.Dice.subscribeUrl;
		}
	};

	/**
	 * Process login error, invalid credentials, other server errors.
	 * @method processLoginError
	 * @param {object} errorResponse - Error response object coming from API.
	 * @param {number} status - Status code (4XX, 5XX)
	 * @return {void}
	 */
	EFLLogin.prototype.processLoginError = function(errorResponse, status) {
		if (status === 404) {
			// {status: 404, code: "NOT_FOUND", messages: ['failedAuthentication'], requestId: "c34eb1fb227c3682"}
			// TODO: Instead of hardcoding the error message code we should use the one provided in the error array.
			// Like in the example above where code: "NOT_FOUND" comes back with `failedAuthentication`.
			if (errorResponse.status === 404) {
				// eslint-disable-next-line vars-on-top
				var errLabel = EFL.DiceRealmLabels.getInstance().getLabel('failedAuthentication');
				this.showLoginError(errLabel);
			}
		} else {
			// There should always be an ELSE to process JS errors included not just APi errors.
			// eslint-disable-next-line vars-on-top
			var genricErrLabel = EFL.DiceRealmLabels.getInstance().getLabel('errorOccurredPleaseTryAgain');
			this.showLoginError(genricErrLabel);
		}
	};

	EFLLogin.prototype.processUserPasswordResetError = function(errorResponse, status) {
		var genricErrLabel = EFL.DiceRealmLabels.getInstance().getLabel('invalidResetPasswordToken');
		this.showPasswordResetError(genricErrLabel);
	}

	/**
	 * Trigger user login function called on click event on the Login button.
	 * This will run the input forms validations and will use DiceAuth to trigger the login API.
	 * @method triggerUserLogin
	 * @param {object} formData
	 * @return {void}
	 */
	EFLLogin.prototype.triggerUserLogin = function(formData) {
		// Disable login button to prevent multiple clicks.
		$(this.elements.$loginBtn).prop('disabled', true);

		EFL.DiceAuth.login({
			username: formData.diceInputEmail,
			password: formData.diceInputPassword,
			rememberLogin: formData.diceRememberLogin
		})
			.then(function(data) {
				$(this.elements.$loginBtn).prop('disabled', false);

				if (data.authenticated === true) {
					EFL.analyticsController.pageView('sign-in/success', 'Sign-in-Form-Success', 'content-view');

					// Make sure we clear all HTTP cache to invalidate current cache state.
					// This makes sure that there won't be access data stored from guest login.
					EFL.HTTPCache.clearAll();

					this.redirectLoggedIn();
				}
			}.bind(this))
			.catch(function(error) {
				$(this.elements.$loginBtn).prop('disabled', false);

				// Don't swallow JS errors.
				if (!error.response && !error.status) {
					console.error(error);
				}

				this.processLoginError(error.response, error.status);
			}.bind(this));
	};

	EFLLogin.prototype.triggerUserPasswordReset = function(formData, field) {
		EFL.DiceAuth.createResetPasswordEmail(formData.diceInputForgotEmail)
			.then(function() {
				field.value = '';
				this.switchLoginForgotForms('resetBtn');
			}.bind(this))
			.catch(function(error) {
				// Don't swallow JS errors.
				if (!error.response && !error.status) {
					console.error(error);
				}

				this.processUserPasswordResetError(error.response, error.status)
			}.bind(this));
	}

	EFLLogin.prototype.switchLoginForgotForms = function (source) {
		var $loginForm = $('#' + this.elements.cLoginForm);
		var $forgotPasswordForm = $('#' + this.elements.cForgotPasswordForm);
		var $forgotPasswordConfirmBlock = $('#' + this.elements.cForgotPasswordConfirm);

		$loginForm.addClass('hide');
		$forgotPasswordForm.addClass('hide');
		$forgotPasswordConfirmBlock.addClass('hide');

		if (source === 'forgotBtn') {
			//Show password Reset
			$('#diceInputForgotEmail').val($('#diceInputEmail').val());
			$forgotPasswordForm.removeClass('hide');
		}
		else if (source === 'resetBtn') {
			//Show password Reset Confirmation
			$forgotPasswordConfirmBlock.removeClass('hide');
		}
		else {
			//Show Login
			$loginForm.removeClass('hide');
		}
	};

	// our instance holder
	// eslint-disable-next-line vars-on-top
	var instance;

	// an emulation of static variables and methods
	// eslint-disable-next-line vars-on-top
	var _static = {
		name: "EFLLogin",

		// Method for getting an instance. It returns
		// a singleton instance of a singleton object
		getInstance: function(options) {
			if ( instance === undefined ) {
				instance = new EFLLogin(options);
			}

			return instance;
		}
	};

	return _static;
}(window.jQuery, window.EFL));
;
/* eslint-disable vars-on-top */
window.EFL = window.EFL || {};

window.EFL.EFLRegister = (function ($, EFL) {
	'use strict';

	function EFLRegister() {
		this.registerMessageButtonSelector = '#registerMessage .btn';
		this.elements = {
			'$registerForm': $('#cRegisterForm'),
			'registerForm': document.getElementById('cRegisterForm'),
			'$submitButton': $('.c-register-form__submit-btn'),
			'$registerFormErrorMessage': $('#register-form-error-message'),
			'$birthDateErrorMessage': $('#birthdate-error-container'),
			'$registerMessage': $('#registerMessage'),
			'$registerMessageButton': $(this.registerMessageButtonSelector),
			'mobileInput': document.getElementById('diceInputMobile'),
			'$countryDropdown': $('#diceInputCountry'),
			'customFields': $('.js-custom-address-fields')
		};
		this.trackBasicPass;
	}

	EFLRegister.prototype.init = function () {
		//Skip registration for authenticated users
		if (EFL.DiceAuth.isAuthenticated() && this.elements.registerForm) {
			this.redirect();
		} else {
			this.initEvents();
		}

	};

	EFLRegister.prototype.initEvents = function () {

		if (!this.elements.registerForm) {
			return;
		}

		this.showForm();

		//override parsley to exclude hidden fields
		var validationInstance = this.elements.$registerForm.parsley({
			excluded: "input[type=button], input[type=submit], input[type=reset], input[type=hidden], [disabled], :hidden"
		});

		this.elements.$registerForm.on('submit', function (event) {
			event.preventDefault();
			this.submit(validationInstance);
		}.bind(this));

		window.EFL.Tooltips.init();

		EFL.FormValidation.initDateValidation();

		$(".c-register-form__comms-preferences input[type=checkbox]").change( function(){
			if($(".c-register-form__comms-preferences input[type=checkbox]:checked").length) {
				$(".c-register-form__comms-method").show();
			} else {
				$(".c-register-form__comms-method").hide();
				$(".c-register-form__comms-method input[type=checkbox]").prop('checked', false);
			}
		});

		this.initValidationMessages();

		//Check whether registration message is shown
		if (this.elements.$registerMessage.length) {
			if (!this.elements.$registerMessageButton.length) {
				//message but no button - add one
				this.elements.$registerMessage.append('<p><button type="button" class="btn btn-primary">Register now</button></p>');
				this.elements.$registerMessageButton = $(this.registerMessageButtonSelector);
			}
			this.elements.$registerMessageButton.on("click", function (e) {
				e.preventDefault();
				$('.initial').hide();
				$('#registerContainer').removeClass('hidden');
				document.body.scrollTop = 0; // For Safari
				document.documentElement.scrollTop = 0; // For Chrome, Firefox, IE and Opera
			});
		}
		this.intlTelInput = window.intlTelInput(this.elements.mobileInput, {
			utilsScript: "/Static/scripts/vendor/intlTelInput-utils.js",
			autoPlaceholder: "aggressive",
			initialCountry: "gb",
			preferredCountries: ["gb"],
			separateDialCode: true
		});
		this.track();

		this.elements.$countryDropdown.on('change', function () {
			//show hidden fields matching selected country
			var selected = this.elements.$countryDropdown.val();
			
			this.elements.customFields.each(function () {
				if ($(this).attr("data-for-country") == selected) {
					$(this).show();
				} else {
					$(this).hide();
				}
			});

		}.bind(this));
	};

	EFLRegister.prototype.showForm = function () {
		// Hide bouncing ball and show login/register forms
		$("#loader").hide();
		$("#login-register").show();
	};

	// Todo: Remove when validation messages are rendered on the server
	EFLRegister.prototype.initValidationMessages = function () {
		var registerFormElements = this.elements.registerForm.elements;

		registerFormElements.diceInputTitle.setAttribute(
			'data-parsley-required-message',
			'Title is required.' // TODO: Replace with realm label
		);
		registerFormElements.diceInputFirstName.setAttribute(
			'data-parsley-required-message',
			'First name is required.'  // TODO: Replace with realm label
		);
		registerFormElements.diceInputLastName.setAttribute(
			'data-parsley-required-message',
			'Last name is required.'  // TODO: Replace with realm label
		);
		registerFormElements.diceInputMobile.setAttribute(
			'data-parsley-required-message',
			'Phone number is required.'  // TODO: Replace with realm label
		);
		registerFormElements.diceInputMobile.setAttribute(
			'data-parsley-pattern-message',
			'Please enter a valid Phone Number.' // TODO: Replace with realm label
		);
		registerFormElements.diceInputRegisterEmail.setAttribute(
			'data-parsley-required-message',
			'Email is required.'  // TODO: Replace with realm label
		);
		registerFormElements.diceInputRegisterEmail.setAttribute(
			'data-parsley-type-message',
			'Please enter a valid email (eg: user@domain.com).'  // TODO: Replace with realm label
		);
		registerFormElements.diceInputConfirmEmail.setAttribute(
			'data-parsley-equalto-message',
			'Please confirm your email address.'  // TODO: Replace with realm label
		);
		registerFormElements.diceInputConfirmEmail.setAttribute(
			'data-parsley-required-message',
			'Please confirm your email address.'  // TODO: Replace with realm label
		);
		registerFormElements.diceInputPostcode.setAttribute(
			'data-parsley-required-message',
			'Post code is required.' // TODO: Replace with realm label
		);
		registerFormElements.diceInputPostcode.setAttribute(
			'data-parsley-pattern-message',
			'Please enter a valid Post Code.' // TODO: Replace with realm label
		);
		registerFormElements.diceInputCountry.setAttribute(
			'data-parsley-required-message',
			'Country is required.' // TODO: Replace with realm label
		);
		registerFormElements.diceInputPassword.setAttribute(
			'data-parsley-required-message',
			'Password is required.'  // TODO: Replace with realm label
		);
		registerFormElements.diceInputConfirmPassword.setAttribute(
			'data-parsley-required-message',
			'Password confirmation is required.' // TODO: Replace with realm label
		);
		registerFormElements.diceInputConfirmPassword.setAttribute(
			'data-parsley-equalto-message',
			'Password and confirmation do not match.' // TODO: Replace with realm label
		);
		registerFormElements.diceInputBirthDateDay.setAttribute(
			'data-parsley-required-message',
			'Date of birth day is required.' // TODO: Replace with realm label
		);
		registerFormElements.diceInputBirthDateMonth.setAttribute(
			'data-parsley-required-message',
			'Date of birth month is required.' // TODO: Replace with realm label
		);
		registerFormElements.diceInputBirthDateYear.setAttribute(
			'data-parsley-required-message',
			'Date of birth year is required.' // TODO: Replace with realm label
		);
		registerFormElements.diceInputAcceptedTermsConditions.setAttribute(
			'data-parsley-required-message',
			'You must read and accept the terms and conditions, then check the "Terms and Conditions" checkbox.' // TODO: Replace with realm label
		);
	};

	EFLRegister.prototype.submit = function (validationInstance) {
		var registerFormElements = this.elements.registerForm.elements;

		//filter visible additional fields and pass their values to the registration api
		var additionalFields = {};
		this.elements.customFields.filter(":visible").each(function () {
			var fieldName = $(this).attr("data-fieldName");
			var fieldValue = $(this).find('.form-control').val();
			additionalFields[fieldName] = fieldValue;
		});

		this.hideAllErrors();

		if (validationInstance.isValid()) {

			this.elements.$submitButton.attr('disabled', true);

			var guest_token;

			EFL.DiceAuth.getTokenSilently().then(function (tokens) {
				guest_token = tokens.access_token;
			}).catch(function () {
				guest_token = null;
			}).finally(function () {
				this.register(registerFormElements, guest_token, additionalFields);
			}.bind(this));
		}

		return false;
	};

	EFLRegister.prototype.sendToGoodForm = function (registerFormElements) {
		return EFL.HTTP.post('/api/preferences/save', {
			data: {
				Title: registerFormElements.diceInputTitle.value,
				CustomerForename: registerFormElements.diceInputFirstName.value,
				CustomerSurname: registerFormElements.diceInputLastName.value,
				CustomerPostcode: registerFormElements.diceInputPostcode.value,
				Country: registerFormElements.diceInputCountry.value,
				EmailAddress: registerFormElements.diceInputRegisterEmail.value,
				DobDay: registerFormElements.diceInputBirthDateDay.value,
				DobMonth: registerFormElements.diceInputBirthDateMonth.value,
				DobYear: registerFormElements.diceInputBirthDateYear.value,
				MobilePhoneNumber: this.intlTelInput.getNumber(),
				ClubSupportedID: EFL.partitionCode,
				OptIntoClubNews: registerFormElements.diceInputOptInClub.checked,
				OptIntoEFLNews: registerFormElements.diceInputOptInEFL.checked,
				OptIntoClubOfficialPartners: registerFormElements.diceInputOptInClubPartners.checked,
				OptIntoEFLOfficialPartners: registerFormElements.diceInputOptInEFLPartners.checked,
				OptIntoIfollow: registerFormElements.diceInputOptInCommunications.checked,
				ContactMethodEmail: registerFormElements.diceInputContactMethodEmail.checked,
				ContactMethodSMS: registerFormElements.diceInputContactMethodSMS.checked,
				ContactMethodPhone: registerFormElements.diceInputContactMethodPhone.checked,
				ContactMethodOnline: registerFormElements.diceInputContactMethodOnline.checked,
			}
		}).catch(function (error) {
			// Suppress errors and continue anyway
			console.error(error);
		});
	};

	EFLRegister.prototype.register = function (registerFormElements, token, additionalFields) {
		this.elements.$submitButton.attr('disabled', true);

		EFL.DiceAuth.register({
			email: registerFormElements.diceInputRegisterEmail.value,
			secret: registerFormElements.diceInputPassword.value,
			firstName: registerFormElements.diceInputFirstName.value,
			lastName: registerFormElements.diceInputLastName.value,
			postcode: registerFormElements.diceInputPostcode.value,
			country: registerFormElements.diceInputCountry.value,
			phoneNumber: this.intlTelInput.getNumber(),
			birthDate: registerFormElements.diceInputBirthDateYear.value + '-' +
				registerFormElements.diceInputBirthDateMonth.value + '-' +
				registerFormElements.diceInputBirthDateDay.value
		}, token, additionalFields)
			.then(function () {
				this.sendToGoodForm(registerFormElements).finally(function () {
					this.trackComplete();
					this.redirect();
				}.bind(this));
			}.bind(this))
			.catch(function (error) {

				console.error(error);

				this.processError(error);
				this.elements.$submitButton.removeAttr('disabled');
			}.bind(this));
	};


	EFLRegister.prototype.processError = function (error) {

		if (!error || typeof error !== 'object') {
			var registerErrorLabel = EFL.DiceRealmLabels.getInstance().getLabel('registerConfirm');
			this.showError(registerErrorLabel);
			return;
		}

		if (error.status === 409) {
			var duplicateEmailLabel = EFL.DiceRealmLabels.getInstance().getLabel('emailErrorCode');
			this.showError(duplicateEmailLabel);
			return;
		}

		var labels = [];

		if (error.response) {
			labels = EFL.DiceRealmLabels.getInstance().getLabels(error.response.messages);
		}

		if (!labels.length) {
			var registerErrorLabel = EFL.DiceRealmLabels.getInstance().getLabel('registerConfirm');
			this.showError(registerErrorLabel);
			return;
		}

		this.showError(labels.join('<br />'));
	};

	EFLRegister.prototype.showError = function (errorMessage) {
		this.elements.$registerFormErrorMessage.css('display', 'block');
		this.elements.$registerFormErrorMessage.text(errorMessage);
	};

	EFLRegister.prototype.hideAllErrors = function () {
		this.elements.$registerFormErrorMessage.css('display', 'none');
		this.elements.$registerFormErrorMessage.text('');
	};

	EFLRegister.prototype.redirect = function () {
		var searchParams = new URLSearchParams(window.location.search);
		var redirectUrl = searchParams.get('redirect');
		var sku = searchParams.get('sku');
		if (sku) {
			window.location.href = '/my-account/pay?sku=' + escape(sku) + '&redirect=' + escape(redirectUrl);
		} else if (redirectUrl && redirectUrl.replace(window.location.origin, '').indexOf("://") === -1) { //ensure redirect isn't another site
			window.location.replace(redirectUrl);
		} else {
			window.location.href = window.EFL.Dice.subscribeUrl;
		}
	};

	EFLRegister.prototype.track = function () {
		/* taken from tracking spec spreadsheet.
		 * dataLayer.push({
			"event": "checkout",
			"gift-status-hit": "$gift-status-hit",
			"ecommerce": {
				"currencyCode": "$currencyCode",
				"checkout": {
					"actionField": {
						"step": 1,
						"stepName": "create-account"
					},
					"products": [{
						"brand": "football-team",
						"category": "Domestic | International | Premier National | Global Free",
						"id": "BASIC | ((SEASON|MONTHLY|DAY|WEEK|)(D|E|I|PN)) | (MATCH(XD|XE|XI|A))",
						"name": "Free Pass | Match Pass | Day Pass | Week Pass | Month Pass | Season Pass | Audio Match Pass",
						"price": "$price",
						"quantity": 1,
						"variant": "$matchId"(match passes only),
						"dimension12": "$dimension12"
					}]
				}
			}
		});*/

		//add in data for free pass data:
		var checkout = {
			"event": "checkout",
			"gift-status-hit": "(not set)",
			"ecommerce": {
				"currencyCode": "",
				"checkout": {
					"actionField": {
						"step": 1,
						"stepName": "create-account"
					},
					"products": [{ //only ever one sku in a basket
						"brand": EFL.video.thisClubName,
						"category": "Global Free",
						"id": "BASIC",
						"name": "Free Pass",
						"price": "0",
						"quantity": 1,
						"dimension12": "(not set)"
					}]
				}
			}
		};


		//check buying a package and add package/user data to checkout object
		var urlParams = new URLSearchParams(window.location.search);
		var sku = urlParams.get('sku');
		if (!sku) {
			// Track chcekout obj
			this.trackBasicPass = checkout;
			var trackUrl = '/club-tv/packages/free/global-free/step-1/create-account/';
			window.EFL.analyticsController.pageView(trackUrl, 'Registration');
			dataLayer.push(checkout);
		} else {
			//if sku then find the teams if its a match pass
			EFL.EFLDiceLib.getTeamsFromSku(sku).then(function (eventString) {
				// eslint-disable-next-line complexity
				var trackLicense = function (data) {
					var licenceData;
					for (var i = 0; i < data.length; i++) {
						if (data[i].licence.amounts) {
							var licencesLength = data[i].licence.amounts.length;
							for (var j = 0; j < licencesLength; j++) {
								if (data[i].licence.amounts && data[i].licence.amounts[j].sku === sku) {
									//found sku
									licenceData = data[i].licence;
								}
							}
						}
					}
					if (!licenceData) {
						dataLayer.push(checkout);
						var trackUrl = '/club-tv/packages/free/global-free/step-1/create-account/';
						window.EFL.analyticsController.pageView(trackUrl, 'Registration');
					} else {
						EFL.EFLDiceLib.parseTrackingData(
							licenceData,
							licenceData.purchaseStrategy.type,
							licenceData.purchaseStrategy.subscriptionPeriod,
							eventString,
							{
								price: licenceData.amounts[0].amount,
								currency: licenceData.amounts[0].currency,
								scale: licenceData.amounts[0].scale
							})
							.then(function (data) {
								checkout['gift-status-hit'] = data.giftingStatus;
								checkout.ecommerce.checkout.products[0].id = data.id;
								checkout.ecommerce.checkout.products[0].category = data.category;
								checkout.ecommerce.currencyCode = data.currency;
								checkout.ecommerce.checkout.products[0].name = data.name;
								checkout.ecommerce.checkout.products[0].price = data.price;
								checkout.ecommerce.checkout.products[0].variant = data.variant;
								checkout.ecommerce.checkout.products[0].dimension12 = data.dimension12;

								//track chcekout obj
								window.dataLayer.push(checkout);
								window.EFL.analyticsController.pageView(data.url + 'step-1/create-account/', 'Registration');
							});

					}
				};
				var getLicenses = function (tokens) {
					var API_HEADERS = EFL.DiceAuth.getAPIHeaders();
					var apiUrl = EFL.DiceAuth.API_URL_BASE + 'licence';
					API_HEADERS['Authorization'] = 'Bearer ' + tokens.access_token;

					window.EFL.HTTP.get(apiUrl, {
						headers: API_HEADERS
					})
						.then(function (response) {
							trackLicense(response.data);
						})
						.catch(function (error) {
							console.log(error);
						});
				};
				window.EFL.DiceAuth.withToken(getLicenses)();
			}).catch(function (error) {
				console.error(error);
			});
		}
	};

	EFLRegister.prototype.trackComplete = function () {
		// only track basic registration conversions. other purchases will be tracked on the payment capture page.

		if (this.trackBasicPass) {
			this.trackBasicPass.ecommerce.checkout.actionField.step = 3;
			this.trackBasicPass.ecommerce.checkout.actionField.stepName = "success";
			dataLayer.push(this.trackBasicPass);

			var trackUrl = '/club-tv/packages/free/global-free/step-3/success/';
			window.EFL.analyticsController.pageView(trackUrl, 'Registration');
		}
	};

	// our instance holder
	// eslint-disable-next-line vars-on-top
	var instance;

	// an emulation of static variables and methods
	// eslint-disable-next-line vars-on-top
	var _static = {
		name: "EFLRegister",

		// Method for getting an instance. It returns
		// a singleton instance of a singleton object
		getInstance: function (options) {
			if (instance === undefined) {
				instance = new EFLRegister(options);
			}

			return instance;
		}
	};

	return _static;
}(window.jQuery, window.EFL));
;
window.EFL = window.EFL || {};

window.EFL.EFLPasswordReset = (function($, EFL) {
	'use strict';

	/**
	 * Main EFLPasswordReset function constructor function.
	 * @method EFLPasswordReset
	 * @return {void}
	 */
	function EFLPasswordReset() {
		this.elements = {
			'$resetPassForm': $('.c-resetpass-form #cResetPassForm'),
			'cResetPassForm': document.getElementById('cResetPassForm'),
			'$resetPassBtn': $('.c-resetpass-form .js-submit'),
			'$resetPassCancelBtn': $('.c-resetpass-form .js-cancel'),
			'$formErrorMessage': $('.c-resetpass-form__error-message')
		};
		this.classes = {
			'formControlErrorLabelElClass': '.invalid-feedback'
		};
	}

	/**
	 * Initialize the module by calling all init functions, like events and so on.
	 */
	EFLPasswordReset.prototype.init = function() {
		this.initEvents();
	};
	
	/**
	 * Initialise UI events related to reset password, forgot password.
	 * @method initEvents
	 * @return {void}
	 */
	EFLPasswordReset.prototype.initEvents = function() {
		// Init Parsley validation on forms.
		var passResetValidationInstance = this.elements.$resetPassForm.parsley();

		// Reset password event
		this.elements.$resetPassForm.on('submit', function() {
			this.hidePasswordResetError();
			if (passResetValidationInstance.isValid()) {
				passResetValidationInstance.reset();
				// eslint-disable-next-line vars-on-top
				var resetPassFormElements = this.elements.cResetPassForm.elements;

				var urlParams = new URLSearchParams(window.location.search);
				var resetToken = urlParams.get('token')
				this.triggerUserResetPassword(resetPassFormElements.diceInputPassword.value, resetToken);
			}

			// This prevents the form from auto submitting. Same as `event.preventDefault();`.
			return false;
		}.bind(this));

		
		this.elements.$resetPassCancelBtn.on('click', function() {
			this.redirectUser();
		}.bind(this));
	};

	EFLPasswordReset.prototype.redirectUser = function() {
		window.location.replace('/my-account');
	};

	EFLPasswordReset.prototype.hidePasswordResetError = function() {
		this.elements.$formErrorMessage.text('');
		this.elements.$formErrorMessage.css('display', 'none');
	};

	EFLPasswordReset.prototype.showPasswordResetError = function(errLabel) {
		this.elements.$formErrorMessage.text(errLabel);
		this.elements.$formErrorMessage.css('display', 'block');
	};

	EFLPasswordReset.prototype.processPasswordResetError = function(error) {

		if (error.response && error.response.messages && error.response.messages.length) {
			var errLabelId = error.response.messages[0];
			var errLabel = EFL.DiceRealmLabels.getInstance().getLabel(errLabelId);
	
			if(errLabel) {
				this.showPasswordResetError(errLabel);
				return;
			}
		}

		console.error(error); 
		var defaultErrLabelId = 'invalidResetPasswordToken';
		var defaultErrLabel = EFL.DiceRealmLabels.getInstance().getLabel(defaultErrLabelId);
		this.showPasswordResetError(defaultErrLabel);
	}

	EFLPasswordReset.prototype.triggerUserResetPassword = function(newPassword, resetToken) {
		// Disable update button to prevent multiple clicks.
		this.elements.$resetPassBtn.prop('disabled', true);

		EFL.DiceAuth.resetPassword(newPassword, resetToken)
			.then(function(data) {
				this.elements.$resetPassBtn.prop('disabled', false);

				// TODO: Not sure I need this check here, we can just redirect
				if (data.status === 202) {
					this.redirectUser();
				}
			}.bind(this))
			.catch(function(error) {
				this.elements.$resetPassBtn.prop('disabled', false);
				this.processPasswordResetError(error);
			}.bind(this));
	};
	
	// our instance holder
	// eslint-disable-next-line vars-on-top
	var instance;

	// an emulation of static variables and methods
	// eslint-disable-next-line vars-on-top
	var _static = {
		name: "EFLPasswordReset",

		// Method for getting an instance. It returns
		// a singleton instance of a singleton object
		getInstance: function(options) {
			if ( instance === undefined ) {
				instance = new EFLPasswordReset(options);
			}

			return instance;
		}
	};

	return _static;
}(window.jQuery, window.EFL));
;
window.EFL = window.EFL || {};

window.EFL.EFLProfile = (function($, EFL) {
	'use strict';

	function EFLProfile() {
		this.isAuthenticated = false;
	}

	EFLProfile.prototype.init = function() {
		this.setUserState();
	};

	EFLProfile.prototype.setUserState = function() {
		var user;
		
		this.isAuthenticated = EFL.DiceAuth.isAuthenticated();

		if (this.isAuthenticated) {
			user = EFL.DiceAuth.getUser();
			if (user) {

				var displayName = user.id;

				if (user.name && user.name.preferredName) {
					displayName = user.name.preferredName;
				} else if (user.name && user.name.fullName) {
					displayName = user.name.fullName;
				}

				this.updateUserNameUI(displayName);

				// if logged in but auth cookie has expired then set it again 
				if (!EFL.DiceClientStorage.get(EFL.DiceAuth.authCookieName)) {
					EFL.DiceClientStorage.save(EFL.DiceAuth.authCookieName, true, { daysUntilExpire: 1 });

					// if on /my-account page then refresh the page so don't see the login form as cookie is checked on the backend
					if (window.location.pathname.includes("/my-account/")) {
						window.location.replace(window.location.pathname);
					}
				}	

				// TODO: What is this for and is it still needed for Dice integration?
				$(window).trigger('throttled-resize'); // Recalculate page layout
			}
		}
	};

	EFLProfile.prototype.refreshUserState = function(data) {
		EFL.DiceAuth.refreshUserData(data);
		this.updateUserNameUI(data.preferredName);
	};

	EFLProfile.prototype.updateUserNameUI = function(userName) {
		$('.my-account-link a').html(userName);
	};
	
	// eslint-disable-next-line vars-on-top
	var instance;
	// eslint-disable-next-line vars-on-top
	var _static = {
		name: "EFLProfile",

		getInstance: function(options) {
			if ( instance === undefined ) {
				instance = new EFLProfile(options);
			}

			return instance;
		}
	};

	return _static;
}(window.jQuery, window.EFL));
;
window.EFL = window.EFL || {};

window.EFL.EFLMyAccount = (function ($, EFL) {
	'use strict';

	function EFLMyAccount() {
		this.elements = {
			$component: $('.c-account-mgmt')
		};

		this.selectors = {
			sectionActionsEdit: '.js-edit-button',
			sectionActionsBack: '.js-edit-back-button',
			sectionPane: '.c-account-mgmt__section',
			sectionContentPane: '.c-account-mgmt__section-content',
			sectionEditPane: '.c-account-mgmt__section-edit',
			sectionChangePassPane: '.c-account-mgmt__section-change-pass',
			sectionUserBilling: '.c-account-mgmt__section-billing',
			editActionsButtons: '.c-account-mgmt__edit-actions .js-button',
			editButtons: '.c-account-mgmt__edit-actions .js-edit-button',
			visibilityButton: '.c-account-mgmt__edit-actions .js-section-expand-button',
			editBackButtons: '.c-account-mgmt__edit-actions .js-edit-back-button',
			changePassButton: '.c-account-mgmt__section-edit .js-change-password-button',

			deleteCardButton: '.c-account-mgmt__section-edit-actions .js-delete-card-button',
			updatePaymentButton: '.c-account-mgmt__section-edit-actions .js-update-payment-method',
			changePaymentButton: '.c-account-mgmt__section-edit-actions .js-change-payment-method',

			changePaymentConfirmationLabel: '.c-account-mgmt__success',

			updateForms: '.c-account-mgmt__form.js-mgmt-form',
			genericErrorContainer: '.c-account-mgmt__form-error',
			sectionTpl: {
				myInfo: 'script#c-account-mgmt__section-content[data-section-id="my-info"]',
				userBilling: 'script#c-account-mgmt__section-content[data-section-id="user-billing"]',
				userBillingAddress: 'script#c-account-mgmt__section-content[data-section-id="user-billing-address"]',
				userLicence: 'script#c-account-mgmt__section-content[data-section-id="user-licence"]',
				paymentHistory: 'script#c-account-mgmt__section-content[data-section-id="payment-history"]'
			},
			countryDropdown: '#diceInputCountry',
			customFields: '.js-custom-address-fields'
		};

		this.updateFunctions = {
			'my-info': {
				'info-update': this.triggerMyInfoUpdate.bind(this),
				'pass-update': this.triggerPassUpdate.bind(this)
			},
			'user-billing-address': {
				'address-update': this.triggerBillingAddressUpdate.bind(this)
			},
			'update-payment-method': {
				'update-card': this.triggerUpdatePaymentMethod.bind(this)
			}

		};

		this.myInfoIntlTelInput;

		this.address = {};
	}

	EFLMyAccount.prototype.init = function() {
		document.querySelector('[data-trigger-logout]') && EFL.EFLLogout.getInstance().init();
		
		this.initMyAccountPage();
	};

	EFLMyAccount.prototype.initMyAccountPage = function() {
		var _this = this;

		Promise.all([
			this.renderSectionMyInfo(),
			this.renderSectionUserBilling(),
			this.renderSectionUserBillingAddress(),
			this.renderSectionUserLicence(),
			this.renderSectionPaymentHistory()
		])
			.then(function() {
				_this.sectionInitEditEvents();
				_this.sectionInitVisibilityEvents();
			})
			.catch(this.handleRenderErrors.bind(this));
	};

	EFLMyAccount.prototype.renderSectionMyInfo = function() {
		var $tplEl = this.elements.$component.find(this.selectors.sectionTpl.myInfo);
		var tpl = $tplEl.text();
		var userData = EFL.DiceAuth.getUser();
		var tplData = {
			email: '',
			name: '',
			mobile: ''
		};

		if (userData) {
			tplData = {
				sectionId: 'my-info',
				email: userData.contactEmail,
				name: (userData.name ? userData.name.fullName : ""),
				mobile: (userData.phoneNumber ? userData.phoneNumber : "")
			};
		}

		var html = Sqrl.render(tpl, tplData);
		$tplEl.replaceWith(html);
		
		this.myInfoIntlTelInput = window.intlTelInput(document.getElementById('diceInputPhoneNumber'), {
			utilsScript: "/Static/scripts/vendor/intlTelInput-utils.js",
			autoPlaceholder: "aggressive",
			initialCountry: "gb",
			preferredCountries: ["gb"],
			separateDialCode: true
		});

		return Promise.resolve('renderSectionMyInfo');
	};

	EFLMyAccount.prototype.renderSectionUserBilling = function() {
		var _this = this;
		var withToken = window.EFL.DiceAuth.withToken;
		var renderSection = function(data) {
			var $tplEl = _this.elements.$component.find(_this.selectors.sectionTpl.userBilling);
			var tpl = $tplEl.text();
			var tplData = {
				sectionId: 'user-billing',
				cards: data.data
			};
			var html = Sqrl.render(tpl, tplData);
			$(_this.selectors.sectionUserBilling).html(html);

			// TODO: I think this should be part of the template as an improvement.
			if (tplData.cards.cards.length === 0) {
				$('[data-section-id="user-billing"].js-section-state-label').html('No payment information stored.');
			}

			//card events
			$(this.selectors.deleteCardButton).on('click', function(event) {
				$(this).prop('disabled', true);
				var cardId = $(event.currentTarget).data('card-id');
				_this.triggerDeleteCard(cardId);
			});
		}.bind(this);

		return new Promise(function(resolve, reject) {
			var getUserLicence = function (token) {
				var API_HEADERS = EFL.DiceAuth.getAPIHeaders();
				//TODO: revisit when paging has been documented
				var apiUrl = EFL.DiceAuth.API_URL_BASE + 'customer/cards?rpp=25';
				API_HEADERS['Authorization'] = 'Bearer ' + token.access_token;

				window.EFL.HTTP.get(apiUrl, {
					headers: API_HEADERS
				}) 
					.then(function (response) {
						renderSection(response);

						resolve(response.data);
					})
					.catch(function (error) {
						reject(error);
					});
			};
			withToken(getUserLicence)();
		});
	};

	EFLMyAccount.prototype.renderSectionUserBillingAddress = function() {
		var _this = this;
		var withToken = window.EFL.DiceAuth.withToken;
		var renderSection = function (data) {
			var address = _.filter(data.data, function (item) { return item.addressType === 'BILLING' && item.default; })[0] || {};
			var $tplEl = _this.elements.$component.find(_this.selectors.sectionTpl.userBillingAddress);
			var tpl = $tplEl.text();
			var tplData = {
				sectionId: 'user-billing-address',
				errorOccurredPleaseTryAgain: EFL.DiceRealmLabels.getInstance().getLabel('errorOccurredPleaseTryAgain'),
				address: address
			};
			var html = Sqrl.render(tpl, tplData);
			$tplEl.replaceWith(html);
			document.getElementById("diceInputCountry").value = address.countryCode;
			_this.address = address;
		};
		
		return new Promise(function(resolve, reject) {
			var listUserAddress = function (token) {
				var API_HEADERS = EFL.DiceAuth.getAPIHeaders();
				var apiUrl = EFL.DiceAuth.API_URL_BASE + 'user/address';
				API_HEADERS['Authorization'] = 'Bearer ' + token.access_token;
	
				window.EFL.HTTP.get(apiUrl, {
					headers: API_HEADERS
				})
					.then(function (response) {
						renderSection(response);

						resolve(response.data);
					})
					.catch(function (error) {
						reject(error);
					});
			};
	
			withToken(listUserAddress)();
		});
	};

	EFLMyAccount.prototype.renderSectionUserLicence = function() {
		var _this = this;
		var withToken = window.EFL.DiceAuth.withToken;
		var renderSection = function (data) {
			var cardData = data[0].cards;
			var licenceData = data[1];

			//add card details to licence data
			for (var _licenceGroup in licenceData) {
				var licenceGroup = licenceData[_licenceGroup].licences;
				for (var _licence in licenceGroup) {
					var licence = licenceGroup[_licence];
					if (licence.licenceStatus && licence.licenceStatus.licencePayment) {
						var licenceCard = licence.licenceStatus.licencePayment.cardId;
						//find matching card details
						var cardDetails = cardData.find(function (card) {
							return card.cardId === licenceCard;
						});
						//already set to be switched so don't switch again
						if (cardDetails && cardDetails.licences && cardDetails.licences.filter(function (e) { return e.status === 'SWITCH_FROM'; }).length > 0) {
							cardDetails.pending = true;
						}
						licence.licenceStatus.licencePayment.cardDetails = cardDetails;
					}
				}
			}

			var $tplEl = _this.elements.$component.find(_this.selectors.sectionTpl.userLicence);
			var tpl = $tplEl.text();
			var tplData = {
				sectionId: 'user-licence',
				licence: licenceData
			};
			var html = Sqrl.render(tpl, tplData);
			$tplEl.replaceWith(html);

			$(_this.selectors.updatePaymentButton).on('click', function (event) {
				//enable other buttons
				$(_this.selectors.updatePaymentButton).each(function () {
					$(this).prop('disabled', false);
				});
				//disable this button
				$(this).prop('disabled', true);

				var currentCardId = $(event.currentTarget).data('current-card-id');
				var licenceId = $(event.currentTarget).data('licence-id');
				_this.renderUpdatePayMethod(currentCardId, licenceId, cardData, licenceData);
			});
		};
		
		var getUserLicences = new Promise(function (resolve, reject) {
			var getUserLicence = function (token) {
				var API_HEADERS = EFL.DiceAuth.getAPIHeaders();
				var apiUrl = EFL.DiceAuth.API_URL_BASE + 'user/licence';
				API_HEADERS['Authorization'] = 'Bearer ' + token.access_token;

				window.EFL.HTTP.get(apiUrl, {
					headers: API_HEADERS
				}, { skipCache: true })
					.then(function (response) {
						resolve(response.data);
					})
					.catch(function (error) {
						reject(error);
					});
			};

			withToken(getUserLicence)();
		});

		var getUserCards = new Promise(function (resolve, reject) {
			var getUserCards = function (token) {
				var API_HEADERS = EFL.DiceAuth.getAPIHeaders();
				//TODO: revisit when paging has been documented
				var apiUrl = EFL.DiceAuth.API_URL_BASE + 'customer/cards?rpp=25';
				API_HEADERS['Authorization'] = 'Bearer ' + token.access_token;

				window.EFL.HTTP.get(apiUrl, {
					headers: API_HEADERS
				}, { skipCache: true }) 
					.then(function (response) {
						resolve(response.data);
					})
					.catch(function (error) {
						reject(error);
					});
			};
			withToken(getUserCards)();
		});

		return Promise.all([getUserCards,getUserLicences])
			.then(renderSection)
			.catch(function (error) { console.log(error) });
	};

	EFLMyAccount.prototype.renderUpdatePayMethod = function (currentCardId, licenceId, cardData, licenceData) {
		var _this = this;
		var selectedLicence;
		var cardsToChoose;
		var currentCard;

		//get licence object
		for (var _licenceGroup in licenceData) {
			var licenceGroup = licenceData[_licenceGroup].licences;
			for (var _licence in licenceGroup) {
				var licence = licenceGroup[_licence];
				if (licence.licence.id == licenceId) {
					selectedLicence = licence.licence;
				}
			}
		}

		//get current card object
		currentCard = cardData.filter(function (e) {
			return e.cardId === currentCardId;
		});


		//get all cards except current with the same payment type because card switching not supported across payment methods
		cardsToChoose = cardData.filter(function (e) {
			return e.cardId != currentCardId && e.paymentProviderId === currentCard[0].paymentProviderId;
		});


		var $updatePayMethodElement = $('#c-account-mgmt__update-payment-method');
		var tpl = $('#update-payment-method').text();

		var tplData = {
			sectionId: 'update-payment-method',
			cardsToChoose: cardsToChoose,
			currentCard: currentCard[0],
			selectedLicence: selectedLicence
		};

		var html = Sqrl.render(tpl, tplData);
		$updatePayMethodElement.html(html);
	};

	EFLMyAccount.prototype.renderSectionPaymentHistory = function() {
		var _this = this;
		var withToken = window.EFL.DiceAuth.withToken;
		var renderSection = function(data) {
			var $tplEl = _this.elements.$component.find(_this.selectors.sectionTpl.paymentHistory);
			var tpl = $tplEl.text();
			var tplData = {
				data: data.data
			};
			var html = Sqrl.render(tpl, tplData);
			$tplEl.replaceWith(html);
		};
		
		return new Promise(function(resolve, reject) {
			var getPaymentHistory = function (token) {
				var API_HEADERS = EFL.DiceAuth.getAPIHeaders();
				var apiUrl = EFL.DiceAuth.API_URL_BASE + 'customer/history/payment';
				API_HEADERS['Authorization'] = 'Bearer ' + token.access_token;
	
				window.EFL.HTTP.post(apiUrl, {
					headers: API_HEADERS
				})
					.then(function (response) {
						renderSection(response);

						resolve(response.data);
					})
					.catch(function (error) {
						reject(error);
					});
			};
	
			withToken(getPaymentHistory)();
		});
	};

	EFLMyAccount.prototype.sectionInitEditEvents = function() {
		// Section edit pane events
		$(this.selectors.editButtons).on('click', function(event) {
			var sectionId = $(event.currentTarget).data('section-id');

			this.toggleSectionEditPane(sectionId);
		}.bind(this));

		$(this.selectors.editBackButtons).on('click', function(event) {
			var sectionId = $(event.currentTarget).data('section-id');

			this.toggleSectionEditPane(sectionId);
		}.bind(this));

		// Update form events including dynamic ones
		$(document).on('submit', this.selectors.updateForms, function(event) {
			var sectionId = $(event.currentTarget).data('section-id');
			var stepId = $(event.currentTarget).data('step');

			this.triggerFormUpdateAction(sectionId, stepId, event.currentTarget);

			return false;
		}.bind(this));

		// Password change events
		$(this.selectors.changePassButton).on('click', function(event) {
			var sectionId = $(event.currentTarget).data('section-id');
			this.togglePasswordUpdatePane(sectionId);
		}.bind(this));
		$(this.selectors.sectionChangePassPane + ' .js-cancel-button').on('click', function(event) {
			var sectionId = $(event.currentTarget).data('section-id');
			this.togglePasswordUpdatePane(sectionId);
		}.bind(this));

		var $countryDropdown = $(this.selectors.countryDropdown)
		$countryDropdown.on('change', function () {
			//show hidden fields matching selected country
			var selected = $countryDropdown.val();
			var _this = this;
			$(this.selectors.customFields).each(function () {
				if ($(this).attr("data-for-country") == selected) {
					$(this).show();
					//set value of state dropdown if already has value
					var fieldName = $(this).attr("data-fieldname");
					var customValue = _this.address[fieldName];
					if (customValue) {
						$(this).find('.form-control').val(customValue);
					}
				} else {
					$(this).hide();
				}
			});
		}.bind(this));
		$countryDropdown.trigger('change');

	};

	EFLMyAccount.prototype.sectionInitVisibilityEvents = function() {
		$(this.selectors.visibilityButton).on('click', function(event) {
			var sectionId = $(event.currentTarget).data('section-id');

			this.toggleSectionVisibility(sectionId, event.currentTarget);
		}.bind(this));
	};
	
	EFLMyAccount.prototype.toggleSectionEditPane = function(sectionId) {
		$(this.selectors.sectionPane + '[data-section-id="' + sectionId + '"] ' + this.selectors.editActionsButtons).toggleClass('hide');
		$(this.selectors.sectionContentPane + '[data-section-id="' + sectionId + '"]').toggleClass('hide');
		$(this.selectors.sectionEditPane + '[data-section-id="' + sectionId + '"]').toggleClass('hide');
	};

	EFLMyAccount.prototype.togglePasswordUpdatePane = function(sectionId) {
		$(this.selectors.sectionEditPane + '[data-section-id="' + sectionId + '"]').toggleClass('hide');
		$(this.selectors.sectionChangePassPane).toggleClass('hide');
	};

	EFLMyAccount.prototype.toggleSectionVisibility = function(sectionId, el) {
		$(el).toggleClass('is-collapsed');
	};

	EFLMyAccount.prototype.handleRenderErrors = function(error) {
		console.error(error);
	};

	EFLMyAccount.prototype.triggerFormUpdateAction = function(sectionId, stepId, triggerForm) {
		var $form = $(triggerForm);
		//override parsley to exclude hidden fields
		var formValidationInstance = $form.parsley({
			excluded: "input[type=button], input[type=submit], input[type=reset], input[type=hidden], [disabled], :hidden"
		});
	

		this.hideServerSideFormErrors();
		
		formValidationInstance.validate();

		if (formValidationInstance.isValid() && this.updateFunctions[sectionId]) {
			this.updateFunctions[sectionId][stepId](triggerForm.elements, sectionId, formValidationInstance);
		}
	};

	EFLMyAccount.prototype.triggerMyInfoUpdate = function(elements, sectionId) {
		var _this = this;
		var withToken = window.EFL.DiceAuth.withToken;
		var userPhoneNumber = this.myInfoIntlTelInput.getNumber();

		var userData = EFL.DiceAuth.getUser();
		var payload = userData || {};

		var newPayload  = {
			"contactEmail" : elements.diceInputContactEmail.value,
			"preferredLocale" : "en_US",
			"phoneNumber" : userPhoneNumber,
			"name" : {
				"fullName" : elements.diceInputFullName.value,
				"preferredName" : elements.diceInputFullName.value
			}
		};

		_.assignIn(payload, newPayload);

		var updateUserProfile = function (token) {
			var API_HEADERS = EFL.DiceAuth.getAPIHeaders();
			var apiUrl = EFL.DiceAuth.API_URL_BASE + 'user/profile';
			API_HEADERS['Authorization'] = 'Bearer ' + token.access_token;

			window.EFL.HTTP.put(apiUrl, {
				headers: API_HEADERS,
				data: JSON.stringify(payload),
			})
				.then(function (response) {
					EFL.EFLProfile.getInstance().refreshUserState({
						"fullName" : elements.diceInputFullName.value,
						"preferredName" : elements.diceInputFullName.value,
						"phoneNumber" : userPhoneNumber
					});

					// Update my account section labels
					$(_this.selectors.sectionContentPane + '[data-section-id="' + sectionId + '"] .js-fullname-label').text(elements.diceInputFullName.value);
					$(_this.selectors.sectionContentPane + '[data-section-id="' + sectionId + '"] .js-mobile-label').text(userPhoneNumber);

					_this.toggleSectionEditPane(sectionId);
				})
				.catch(function (error) {
					console.error(error);
					// fail silently and do not show any access labels
				});
		};

		withToken(updateUserProfile)();
	};

	EFLMyAccount.prototype.triggerPassUpdate = function(elements, sectionId, formValidationInstance) {
		var _this = this;
		var withToken = window.EFL.DiceAuth.withToken;
		var payload  = {
			"secret" : elements.diceInputNewPassword.value
		};

		var updateUserProfile = function (token) {
			var API_HEADERS = EFL.DiceAuth.getAPIHeaders();
			var apiUrl = EFL.DiceAuth.API_URL_BASE + 'user/update-password';
			API_HEADERS['Authorization'] = 'Bearer ' + token.access_token;

			window.EFL.HTTP.put(apiUrl, {
				headers: API_HEADERS,
				data: JSON.stringify(payload),
			})
				.then(function (response) {
					_this.togglePasswordUpdatePane(sectionId);
				})
				.catch(function (error) {
					_this.updatePasswordError(error);
				});
		};

		formValidationInstance.reset();

		withToken(updateUserProfile)();
	};

	EFLMyAccount.prototype.updatePasswordError = function(error) {
		console.error(error);

		if (error.response && error.response.messages && error.response.messages.length) {
			var errLabelId = error.response.messages[0];
			var errLabel = EFL.DiceRealmLabels.getInstance().getLabel(errLabelId);
	
			if(errLabel) {
				this.showPasswordResetError(errLabel);
				return;
			}
		}
		var defaultErrLabelId = 'invalidResetPasswordToken';
		var defaultErrLabel = EFL.DiceRealmLabels.getInstance().getLabel(defaultErrLabelId);
		this.showPasswordResetError(defaultErrLabel);
	}

	EFLMyAccount.prototype.hideServerSideFormErrors = function() {
		$('.c-account-mgmt__update-password-error-message').text('');
		$('.c-account-mgmt__update-password-error-message').hide();
	};

	EFLMyAccount.prototype.showPasswordResetError = function(errLabel) {
		$('.c-account-mgmt__update-password-error-message').text(errLabel);
		$('.c-account-mgmt__update-password-error-message').show();
	};

	EFLMyAccount.prototype.triggerBillingAddressUpdate = function(elements, sectionId) {
		var _this = this;
		var withToken = window.EFL.DiceAuth.withToken;
		var payload  = {
			"address" : {
				"countryCode" : elements.diceInputCountry.value,
				"postalCode" : elements.diceInputPostcode.value
			}
		};

		if (elements.diceInputFirstLineAddress.value.length > 0) payload.address.line1 = elements.diceInputFirstLineAddress.value;
		if (elements.diceInputSecondLineAddress.value.length > 0) payload.address.line2 = elements.diceInputSecondLineAddress.value;
		if (elements.diceInputTownAddress.value.length > 0) payload.address.town = elements.diceInputTownAddress.value;
		//if (elements.diceInputStateAddress.value.length > 0) payload.address.administrativeLevel1 = elements.diceInputStateAddress.value;

		var additionalFields = {};
		$(this.selectors.customFields).filter(":visible").each(function () {
			var fieldName = $(this).attr("data-fieldName");
			var fieldValue = $(this).find('.form-control').val();
			additionalFields[fieldName] = fieldValue;
		});
		_.assign(payload.address, additionalFields)


		// eslint-disable-next-line vars-on-top
		var updateUserAddress = function(token) {
			var API_HEADERS = EFL.DiceAuth.getAPIHeaders();
			var apiUrl = EFL.DiceAuth.API_URL_BASE + 'user/billing-details';
			API_HEADERS['Authorization'] = 'Bearer ' + token.access_token;

			window.EFL.HTTP.put(apiUrl, {
				headers: API_HEADERS,
				data: JSON.stringify(payload),
			})
				.then(function (response) {
					_this.toggleSectionEditPane(sectionId);
					// eslint-disable-next-line vars-on-top
					var addressLabel = '';
					if (payload.address.line1) addressLabel += payload.address.line1 + ', ';
					if (payload.address.line2) addressLabel += payload.address.line2 + ', ';
					if (payload.address.town) addressLabel += payload.address.town + ', ';
					if (payload.address.administrativeLevel1) addressLabel += payload.address.administrativeLevel1 + ', ';
					if (payload.address.postalCode) addressLabel += payload.address.postalCode + ', ';
					if (payload.address.countryCode) addressLabel += payload.address.countryCode;

					$(_this.selectors.sectionContentPane + '[data-section-id="' + sectionId + '"] .js-address-label').text(addressLabel);
					$(_this.selectors.genericErrorContainer + '[data-section-id="' + sectionId + '"]').addClass('hide');
				})
				.catch(function (error) {
					$(_this.selectors.genericErrorContainer + '[data-section-id="' + sectionId + '"]').removeClass('hide');
					console.error(error);
				});
		};

		withToken(updateUserAddress)();
	};

	EFLMyAccount.prototype.triggerDeleteCard = function(cardID) {
		var _this = this;
		var withToken = window.EFL.DiceAuth.withToken;

		// eslint-disable-next-line vars-on-top
		var updateUserProfile = function (token) {
			var API_HEADERS = EFL.DiceAuth.getAPIHeaders();
			var apiUrl = EFL.DiceAuth.API_URL_BASE + 'customer/cards/' + cardID;
			API_HEADERS['Authorization'] = 'Bearer ' + token.access_token;

			window.EFL.HTTP.delete(apiUrl, {
				headers: API_HEADERS
			})
				.then(function (response) {
					
					_this.renderSectionUserBilling();
				})
				.catch(function (error) {
					console.error(error);
					// fail silently
				});
		};

		withToken(updateUserProfile)();
	};

	EFLMyAccount.prototype.triggerUpdatePaymentMethod = function (elements, sectionId) {
		$(elements.diceUpdatePaymentMethod).prop('disabled', true);

		var _this = this;
		var withToken = window.EFL.DiceAuth.withToken;

		var licenceId = elements.diceUpdatePaymentMethod.attributes.getNamedItem('data-licence-id').nodeValue;
		var chosenCard = elements.dicePaymentMethod.value;

		var updateUserProfile = function (token) {
			var API_HEADERS = EFL.DiceAuth.getAPIHeaders();
			var apiUrl = EFL.DiceAuth.API_URL_BASE + 'licence/' + licenceId + '/card/' + chosenCard;
			API_HEADERS['Authorization'] = 'Bearer ' + token.access_token;

			window.EFL.HTTP.post(apiUrl, {
				headers: API_HEADERS
			})
				.then(function (response) {

					//hide this form
					$('#c-account-mgmt__update-payment-method').html('');

					//update button with Pending
					$(_this.selectors.updatePaymentButton).each(function () {
						if ($(this).data('licence-id') == licenceId) {
							$(this).closest('td').html("Pending");
						}
					});
				})
				.catch(function (error) {
					console.error(error);
					$(_this.selectors.changePaymentConfirmationLabel).text(EFL.DiceRealmLabels.getInstance().getLabel('errorOccurredPleaseTryAgain'));
					// fail silently and do not show any access labels
				});
		};

		withToken(updateUserProfile)();
	};

	// eslint-disable-next-line vars-on-top
	var instance;
	// eslint-disable-next-line vars-on-top
	var _static = {
		name: "EFLMyAccount",

		getInstance: function (options) {
			if (instance === undefined) {
				instance = new EFLMyAccount(options);
			}

			return instance;
		}
	};

	return _static;
}(window.jQuery, window.EFL));
;
window.EFL = window.EFL || {};

window.EFL.EFLLogout = (function ($, EFL) {
	'use strict';

	function EFLLogout() {
		this.elements = {
			'$logoutButton': $('[data-trigger-logout]'),
			'$errorMessage': $('.error-message'),
		};
	}

	EFLLogout.prototype.init = function () {
		this.initEvents();
	};

	EFLLogout.prototype.initEvents = function () {
		this.elements.$logoutButton.on('click', function (e) {
			this.triggerLogout();

			return false;
		}.bind(this));
	};


	EFLLogout.prototype.triggerLogout = function () {
		EFL.DiceAuth.revokeUserToken()
			.then(function () {
				// Make sure we clear all HTTP cache to invalidate current cache state.
				// This makes sure that there won't be access data stored from logedin users.
				EFL.HTTPCache.clearAll();

				this.redirectLogout();
			}.bind(this))
			.catch(function (error) {
				//error if api is unavailable
				console.error(error);
				//even if failed we have cleared the storage so redirect
				this.redirectLogout();
			}.bind(this));
	};

	EFLLogout.prototype.redirectLogout = function () {
		window.location.replace(this.elements.$logoutButton.data('redirect-uri'));
	};

	// eslint-disable-next-line vars-on-top
	var instance;
	// eslint-disable-next-line vars-on-top
	var _static = {
		name: "EFLLogout",

		getInstance: function (options) {
			if (instance === undefined) {
				instance = new EFLLogout(options);
			}

			return instance;
		}
	};

	return _static;
}(window.jQuery, window.EFL));
;
window.EFL = window.EFL || {};

window.EFL.EFLCheckout = (function ($, EFL) {
	'use strict';

	//Constructor
	function EFLCheckout() {
		this.isAuthenticated = false;
		this.API_URL_BASE = EFL.Dice.apiUrl + '/api/v2/';
		this.API_HEADERS = {
			"realm": EFL.Dice.realm,
			"x-api-key": EFL.Dice.key,
			"app": EFL.Dice.app,
			"Content-Type": "application/json"
		};
		this.HTTP = EFL.HTTP;
		this.PACKAGE_INFO_API_URL = '/api/dice/packageinfo';
		this.SKU = "";
		this.CART = {};
		this.$FORM = $("#reqAddrForm");
		this.addedRequiredInfo = false;
		this.retries = 0;
		this.maxRetries = 5;
		this.formElements = document.getElementById('reqAddrForm').elements;

		this.contactEmail = null;
		this.contactFullName = null;
		this.voucherCode = "";
		this.analyticsData = {};

		this.paymentData = [];
		this.packageData = {};

		this.STRIPE = {
			stripe: null,
			elements: null,
			card: null,
			providerId: 0
		};
		this.PAYPAL = {
			authToken: null,
			providerId: 0
		};
		this.elements = {
			'$cartContainer': $('#cart'),
			'$paymentTotalContainer': $('#payment-total'),
			'$paymentContainer': $('#payment'),
			'$voucherInput': $('#voucher-code'),
			'$voucherSubmitBtn': $('#btn-check-voucher'),
			'$paySubmitBtn': $('#btn-submit-payment'),
			'$paymentMethodForm': $('#payment-method'),
			'$existingCardsContainer': $('#existing-cards'),
			'$paypalBtn': $('#btn-paypal'),
			'$paypalFinaliseBtn': $('#btn-submit-paypal'),
			'$orderCompleteContainer': $('#complete #order-info'),
			'$countryDropdown': $('#diceInputCountry'),
			'$customFields': $('.js-custom-address-fields')
		};
		this.labels = {
			'$voucherError': $('#pay-voucher-code .invalid-feedback-client'),
			'$paymentError': $('#payment .invalid-feedback-client.payment-error'),
			'$cardError': $('#payment-form #card-errors'),
			'$freeTrialWarning': $('#free-trial-warning'),
		};
	}

	EFLCheckout.prototype.init = function () {
		this.isAuthenticated = EFL.DiceAuth.isAuthenticated();
		if (!this.isAuthenticated) {
			this.redirectToLogin();
		}
		var urlParams = new URLSearchParams(window.location.search);
		this.SKU = urlParams.get('sku');
		if (this.SKU === '') {
			location.href = EFL.Dice.subscribeUrl;
		}

		// eslint-disable-next-line vars-on-top
		var user = EFL.DiceAuth.getUser();
		this.contactEmail = user.contactEmail;
		this.contactFullName = user.name.fullName;

		this.initCart();

		this.getPackageData();

		this.initEvents();
	};

	EFLCheckout.prototype.initEvents = function () {
		//Setup validation - exclude hidden fields
		var validationInstance = this.$FORM.parsley({
			excluded: "input[type=button], input[type=submit], input[type=reset], input[type=hidden], [disabled], :hidden"
		});

		this.formElements.addrAddress1.setAttribute(
			'data-parsley-required-message',
			'Address line 1 is required.'
		);
		this.formElements.addrPostcode.setAttribute(
			'data-parsley-required-message',
			'Post code is required.'
		);
		this.formElements.addrPostcode.setAttribute(
			'data-parsley-pattern-message',
			'Please enter a valid Post Code.'
		);
		this.formElements.diceInputCountry.setAttribute(
			'data-parsley-required-message',
			'Country is required.'
		);

		this.$FORM.on("submit", function (event) {
			event.preventDefault();
			this.submitAddress(validationInstance);
		}.bind(this));

		this.elements.$voucherSubmitBtn.on('click', function (e) {
			e.preventDefault();
			this.submitVoucher();
			return false;
		}.bind(this));

		this.elements.$paySubmitBtn.on('click', function (e) {
			e.preventDefault();
			this.stripeSubmit();
			return false;
		}.bind(this));

		this.elements.$paypalFinaliseBtn.on('click', function (e) {
			e.preventDefault();
			this.savedCardSubmit(e.target.getAttribute("data-card-id"), true);
			return false;
		}.bind(this));

		this.elements.$paymentMethodForm.on('change', function (e) {
			this.showPaymentOption("pay-" + e.target.id);
			return false;
		}.bind(this));

		this.elements.$countryDropdown.on('change', function () {
			//show hidden fields matching selected country
			var selected = this.elements.$countryDropdown.val();
			this.elements.$customFields.each(function () {
				if ($(this).attr("data-for-country") == selected) {
					$(this).show();
				} else {
					$(this).hide();
				}
			});
		}.bind(this));
	};

	EFLCheckout.prototype.initCart = function () {
		this.addSkuToCart(this.SKU).then(function () {
			return this.alreadyOwnLicence(this.SKU)
		}.bind(this)).then(function (alreadyOwn) {
			if (alreadyOwn === true) {
				this.showSection("alreadyown");
			} else {
				this.renderCart();
				this.renderPaymentMethods();
				this.trackPaymentDetails();
			}
		}.bind(this));
	};

	EFLCheckout.prototype.addSkuToCart = function (sku) {
		return new Promise(function (resolve, reject) {
			this.getHeaders().then(function (headers) {
				EFL.HTTP.put(this.API_URL_BASE + 'baskets/' + sku, {
					headers: headers
				}).then(function (response) {
					this.CART = response.data;
					resolve();
				}.bind(this)).catch(function (error) {
					if (!error.response && !error.status) {
						console.error(error);
					} else {
						if (error.status === 404) {
							this.showSection("carterror");
						}
						if (error.status === 424) {
							// Required fields are missing, show form before payment options/cart
							if (this.addedRequiredInfo && this.retries < this.maxRetries) {
								// Unfortunately, sending required fields doesn't seem to result in immediate processing so may need to periodically retry
								// Allow 250ms longer between each retry
								this.retries++;
								window.setTimeout(this.initCart.bind(this), this.retries * 250);
								return;
							} else if (this.addedRequiredInfo && this.retries >= this.maxRetries) {
								document.getElementById("reqformerror").style.cssText  = "display: block;";
							}
							this.showSection("requiredinfo");
						}
					}
				}.bind(this));
			}.bind(this));
		}.bind(this));
	};

	EFLCheckout.prototype.submitAddress = function (validationInstance) {
		if (validationInstance.isValid()) {
			$('.c-req-info__submit-btn').attr('disabled', true);
			this.saveRequiredInformation();
		}
	};

	EFLCheckout.prototype.saveRequiredInformation = function () {
		return this.getHeaders().then(function (headers) {
			var formElements = this.$FORM[0].elements;
			var payload = {
				address: {
					line1: formElements.addrAddress1.value,
					postalCode: formElements.addrPostcode.value,
					countryCode: formElements.diceInputCountry.value
				}
			}

			if (formElements.addrTown.value.length > 0) payload.address.town = formElements.addrTown.value;
			if (formElements.addrAddress2.value.length > 0) payload.address.line2 = formElements.addrAddress2.value;

			var additionalFields = {};
			this.elements.$customFields.filter(":visible").each(function () {
				var fieldName = $(this).attr("data-fieldName");
				var fieldValue = $(this).find('.form-control').val();
				additionalFields[fieldName] = fieldValue;
			});
			_.assign(payload.address, additionalFields)

			EFL.HTTP.put(this.API_URL_BASE + 'user/billing-details', {
				headers: headers,
				data: JSON.stringify(payload)
			}).catch(function (error) {
				if (!error.response && !error.status) {
					console.error(error);
				} else {
					//API ERROR!
					document.getElementById("reqformerror").style.cssText  = "display: block;";
				}
			}.bind(this)).finally(function () {
				this.addedRequiredInfo = true;
				this.initCart();
			}.bind(this));
		}.bind(this));
	};

	EFLCheckout.prototype.renderCart = function () {
		if (this.CART.basketItems === undefined || this.CART.basketItems.length === 0) {
			return;
		}
		var cartItem = {
			sku: this.CART.basketItems[0],
			total: this.CART.total,
			totalDiscount: this.CART.totalDiscount,
			discountedTotal: this.CART.discountedTotal
		};

		//render top right sku
		var tpl = this.elements.$paymentContainer.find('script#package-tpl').text();
		var html = Sqrl.render(tpl, cartItem);
		this.elements.$cartContainer.html(html);

		this.renderTotal();
	};

	EFLCheckout.prototype.renderTotal = function () {
		if (this.CART.basketItems === undefined || this.CART.basketItems.length === 0 || this.CART.discountedTotal === undefined) {
			return;
		}

		var cartItem = {
			sku: this.CART.basketItems[0],
			total: this.CART.total,
			totalDiscount: this.CART.totalDiscount,
			discountedTotal: this.CART.discountedTotal,
			tax: this.CART.discountedTax ? this.CART.discountedTax : this.CART.tax,
			subtotal: this.CART.subTotal
		};

		//render bottom total price
		var tpl = this.elements.$paymentContainer.find('script#payment-total-tpl').text();
		var html = Sqrl.render(tpl, cartItem);
		this.elements.$paymentTotalContainer.html(html);
	};

	EFLCheckout.prototype.renderPaymentMethods = function () {
		if (!this.CART || !this.CART.paymentProvidersList || this.CART.paymentProvidersList.length === 0) {
			return;
		}

		var useStripe = false;
		var usePaypal = false;

		for (var i = 0; i < this.CART.paymentProvidersList.length; i++) {
			var provider = this.CART.paymentProvidersList[i];

			if (provider.providerType === "STRIPE" || provider.paymentProviderType === "STRIPE") {
				useStripe = true;
				this.stripeInit();

			} else if (provider.providerType === "PAYPAL" || provider.paymentProviderType === "PAYPAL") {
				usePaypal = true;
				this.paypalInit();
			}
		}

		if (!useStripe && !usePaypal) {
			return this.showSection("carterror");
		}

		//only render applicable existing payment methods
		this.existingPaymentInit(useStripe, usePaypal);

		//stripe is default so if it's disabled show Paypal
		if (!useStripe && usePaypal) {
			this.showPaymentOption("pay-new-paypal")
		}

		var cartItem = {
			Stripe: useStripe,
			Paypal: usePaypal
		};
		var tpl = $('#payment-method').find('script#payment-methods-tpl').text();
		var html = Sqrl.render(tpl, cartItem);
		$('#payment-method').html(html);

		this.showSection("payment");
	};

	EFLCheckout.prototype.getHeaders = function () {
		var API_HEADERS = _.clone(this.API_HEADERS);
		return EFL.DiceAuth.getTokenSilently().then(function (tokens) {
			API_HEADERS['Authorization'] = 'Bearer ' + tokens.access_token;
			return API_HEADERS;
		});
	};

	EFLCheckout.prototype.showSection = function (sectionName) {
		var sections = ["carterror", "alreadyown", "requiredinfo", "payment", "loader", "complete"];
		for (var i = 0; i < sections.length; i++) {
			if (sections[i] === sectionName) {
				document.getElementById(sections[i]).style.cssText  = "";
			} else {
				document.getElementById(sections[i]).style.cssText  = "display: none;";
			}
		}
	};

	EFLCheckout.prototype.paypalInit = function () {
		var _this = this;
		var result;
		var paymentProviders = this.CART.paymentProvidersList;
		for (var i = 0; i < paymentProviders.length; i++) {
			var provider = paymentProviders[i];

			if (provider.providerType === "PAYPAL" || provider.paymentProviderType === "PAYPAL") {
				this.PAYPAL.providerId = provider.providerId;
				result = provider;
			}
		}


		var paypalKey = result.details.publishableKey;

		if (paypalKey == null) {
			//fail with generic error if can't get key
			return this.paymentError();
		}

		//attach script to html head
		var PAYPAL_SCRIPT = 'https://www.paypal.com/sdk/js?client-id=' + paypalKey + '&vault=true&components=buttons&disable-funding=card';
		var script = document.createElement('script');
		script.setAttribute('src', PAYPAL_SCRIPT);
		document.head.appendChild(script);

		//when script has loaded init button
		script.addEventListener("load", function () {
			paypal.Buttons({
				createOrder: function () {
					//console.log("create")
					return this.getPaypalAuthToken();
				}.bind(this),
				onApprove: function (data, actions) {
					//console.log("approve")
					return this.authPaypalPayment();
				}.bind(this),
			}).render('#btn-paypal');
		}.bind(this));
	};

	EFLCheckout.prototype.getPaypalAuthToken = function () {
		//dice creates the order - use the order number to create the button
		//https://dicetech.atlassian.net/wiki/spaces/DD/pages/60358663/PayPal#PayPal-GetPayPalAuthToken
		return new Promise(function (resolve, reject) {
			this.getHeaders().then(function (headers) {
				EFL.HTTP.get(this.API_URL_BASE + 'customer/paypal/auth-token/' + this.PAYPAL.providerId, {
					headers: headers
				}).then(function (response) {
					this.PAYPAL.authToken = response.response.paypalAuthToken;
					resolve(this.PAYPAL.authToken);
				}.bind(this)).catch(function (error) {
					//api error
					this.paymentError();
					console.log("auth-token", error);
				}.bind(this));
			}.bind(this));
		}.bind(this));
	};

	EFLCheckout.prototype.authPaypalPayment = function () {
		//payment authed - show spinner while redirecting to payment complete or showing error
		this.showSection("loader");

		//user has approved transaction - dice will add the Paypal card to their account - we then pay for basket with saved card
		//https://dicetech.atlassian.net/wiki/spaces/DD/pages/60358663/PayPal#PayPal-CreatePayPalAuthorisation
		return new Promise(function (resolve, reject) {
			this.getHeaders().then(function (headers) {
				EFL.HTTP.post(this.API_URL_BASE + 'customer/paypal/create-authorization/' + this.PAYPAL.providerId + '/' + this.PAYPAL.authToken, {
					headers: headers
				}).then(function (response) {
					this.paymentData.push({ key: "Payment type", val: "Paypal" });
					this.paymentData.push({ key: "Paypal ID", val: response.response.cardId });
					return response.response.cardId;

				}.bind(this)).then(function (cardID) {
					//try payment
					this.getHeaders().then(function (headers) {
						EFL.HTTP.post(this.API_URL_BASE + 'customer/baskets/pay/' + cardID, {
							headers: headers,
							data: JSON.stringify({ "customerEmailAddress": this.contactEmail }),
						}).then(function (response) {
							this.checkoutComplete();
							//console.log("paid", response);
							resolve();
							}.bind(this)).catch(function (error) {
								this.showSection("payment");
							//payment failed
							//TODO: get error messages
							if (error.status === 409) {
								if (error.response.code == "DUPLICATE_LICENCE") {
									//already purchased
									this.paymentError(error.response.code);
								}
								else if (error.response.code == "PAYMENT_PROVIDER_CHANGE") {
									//already purchased
									this.paymentError(error.response.code);
								} else {
									//card already used for free trial
									

									this.labels.$freeTrialWarning.show();
									this.labels.$freeTrialWarning.data("required", true);
									this.paymentError(error.response.code);
									
									this.elements.$paypalFinaliseBtn.show();
									this.elements.$paypalFinaliseBtn.attr('data-card-id', response.response.cardId);
								}
							}
							console.log("paypal savedCardSubmit", error);
						}.bind(this));
					}.bind(this));


				}.bind(this)).catch(function (error) {
					//api error
					this.showSection("payment");
					this.paymentError();
					console.log("create-authorization", error);
				}.bind(this));
			}.bind(this));
		}.bind(this));
	};


	EFLCheckout.prototype.existingPaymentInit = function (useStripe, usePaypal) {
		// GET customer/cards
		//https://dicetech.atlassian.net/wiki/spaces/DD/pages/327759/Purchase#Purchase-ListCustomerCards
		this.getHeaders().then(function (headers) {
			//TODO: add paging when this is implemented by DICE
			EFL.HTTP.get(this.API_URL_BASE + 'customer/cards', {
				headers: headers
			})
				.then(function (response) {
					var cards = response.response.cards;
					if (cards.length > 0) {
						function pad2(number) {
							return (number < 10 ? '0' : '') + number;
						}

						//remove payment methods from list if they are disabled
						var newCardList = [];
						for (var i = 0; i < cards.length; i++) {
							var card = cards[i];

							if (card.cardType.toLowerCase() === "paypal" && usePaypal) {
								newCardList.push(card);

							} else if (card.cardType.toLowerCase() != "paypal" && useStripe) {
								card.expiryMonthPad = pad2(card.expiryMonth);
								newCardList.push(card);
							}
						}


						if (newCardList.length > 0) {
							var tpl = this.elements.$existingCardsContainer.find('script#existing-cards-tpl').text();
							var html = Sqrl.render(tpl, newCardList);
							this.elements.$existingCardsContainer.html(html);

							//add click event
							this.elements.$existingCardsContainer.delegate('button', 'click', function (e) {
								// Disable this button after click
								$(e.target).prop('disabled', true);

								if (e.target.getAttribute("data-card-type") == "Paypal") {
									this.paymentData.push({ key: "Payment type", val: e.target.getAttribute("data-card-type") });
									this.paymentData.push({ key: "Paypal ID", val: e.target.getAttribute("data-card-id") });
								}
								else {
									// Removed for now, might be needed later.
									// this.paymentData.push({ key:"Card type", val: e.target.getAttribute("data-card-type") });
									this.paymentData.push({ key: "Card Number", val: "XXXX-XXXX-XXXX-" + e.target.getAttribute("data-card-last4") });
									this.paymentData.push({ key: "Expiry Date", val: e.target.getAttribute("data-card-expire") });
									//this.paymentData.push({ key: "Card ID", val: e.target.getAttribute("data-card-id") });
								}

								this.savedCardSubmit(e.target.getAttribute("data-card-id"), false);
								return false;
							}.bind(this));
						}
					}

				}.bind(this)).catch(function (error) {
					//api failed
					this.paymentError("Error getting saved cards");
					console.log("existingPaymentInit", error);
				}.bind(this));
		}.bind(this));
	};

	EFLCheckout.prototype.savedCardSubmit = function (cardId, noFreeTrial) {
		if (!cardId) {
			//generic error
			this.paymentError();
			console.log("error getting card ID", error);
		}

		var freeTrialString = "";
		if (noFreeTrial) {
			freeTrialString = "?acknowledgeNoFreeTrial=true";
		}

		// POST  customer/baskets/pay/{card_id}
		//https://dicetech.atlassian.net/wiki/spaces/DD/pages/327759/Purchase#Purchase-PayBasketwithStoredCard
		this.getHeaders().then(function (headers) {
			EFL.HTTP.post(this.API_URL_BASE + 'customer/baskets/pay/' + cardId + freeTrialString, {
				headers: headers,
				data: JSON.stringify({ "customerEmailAddress": this.contactEmail }),
			})
				.then(function (response) {
					this.checkoutComplete();
					//console.log("paid", response);
				}.bind(this)).catch(function (error) {
					//payment failed
					//TODO: get error messages
					if (error.status === 409) {
						if (error.response.code == "DUPLICATE_LICENCE") {
							//already purchased
							this.paymentError(error.response.code);
						}
						else {
							//card already used for free trial
							this.labels.$freeTrialWarning.show();
							this.labels.$freeTrialWarning.data("required", true);
							this.paymentError(error.response.code);
						}
					}
					console.log("savedCardSubmit", error);
				}.bind(this));
		}.bind(this));
	};

	EFLCheckout.prototype.stripeInit = function () {
		var paymentProviders = this.CART.paymentProvidersList;
		for (var i = 0; i < paymentProviders.length; i++) {
			var provider = paymentProviders[i];

			 if (provider.providerType === "STRIPE" || provider.paymentProviderType === "STRIPE") {
				this.STRIPE.providerId = provider.providerId;
				this.STRIPE.stripeKey = provider.details.publishableKey;
			}
		}

		if (this.STRIPE.stripeKey == null) {
			//fail with generic error if can't get stripe key
			return this.paymentError();
		}

		this.STRIPE.stripe = Stripe(this.STRIPE.stripeKey);
		this.STRIPE.elements = this.STRIPE.stripe.elements();
		var style = {
			base: {
				fontSize: '16px',
				color: '#555555',
				fontFamily: "Open Sans, Helvetica, Arial, sans-serif",
			},
			invalid: {
				iconColor: '#dc3545',
				color: '#dc3545',
			},
		};

		//Create an instance of the card Element.
		this.STRIPE.card = this.STRIPE.elements.create('card', { style: style, hidePostalCode: true });
		// Add an instance of the card Element into the `card-element` <div>.
		this.STRIPE.card.mount('#card-element');

		this.STRIPE.card.on('change', function (event) {
			var displayError = document.getElementById('card-errors');
			if (event.error) {
				this.stripeShowError(event.error.message);
			} else {
				displayError.textContent = '';
			}
		}.bind(this));
	};

	EFLCheckout.prototype.stripeSubmit = function () {
		var _this = this;
		if (this.CART == null || this.CART.discountedTotal.amount == null) {
			return this.paymentError(EFL.DiceRealmLabels.getInstance().getLabel('paymentCardError'));
		}

		// Disable submit button on click
		this.elements.$paySubmitBtn.prop('disabled', true);
		
		EFL.EFLDiceLib.getStripeSetupIntent(this.STRIPE.providerId)
			.then(function(results) {
				if (!results || !results.data || !results.data.clientSecret) {
					return Promise.reject();
				}
				// Have setup secret - now call Stripe SCA api
				return _this.STRIPE.stripe.confirmCardSetup(results.data.clientSecret, { 'payment_method': { 'card' : _this.STRIPE.card}})
			}).then(function(result) {
				// SCA fail
				if (result.error || !result.setupIntent) {
					return Promise.reject(result.error);
				} 

				// SCA success
				var setupIntentID = result.setupIntent.id;

				var freeTrialSkip = false;
				if (_this.labels.$freeTrialWarning.is(":visible")) {
					freeTrialSkip = true;
				}
				// Call Dice payment API
				return _this._processPayment(_this.STRIPE.providerId, setupIntentID, freeTrialSkip);
			}).then(function(paymentResult) {
				// Payment complete
				_this.checkoutComplete();

			}).catch(function (error) {
				if (error && error.message) {
					// Stripe error
					_this.stripeShowError(error.message);
				} else if (error && error.response && error.status === 409) {
					// Dice errors
					if (error.response.code == "DUPLICATE_LICENCE") {
						_this.paymentError(error.response.code);
					} else {
						// Card already used for free trial
						_this.labels.$freeTrialWarning.show();
						_this.labels.$freeTrialWarning.data("required", true);
						_this.paymentError(error.response.code);
					}
				} else {
					// Generic error
					_this.paymentError(EFL.DiceRealmLabels.getInstance().getLabel('paymentCardError'));
				}
			});
	};

	EFLCheckout.prototype.stripeShowError = function (error) {
		// Enable payment button
		this.elements.$paySubmitBtn.prop('disabled', false);

		var errorElement = document.getElementById('card-errors');
		errorElement.textContent = error;
		errorElement.style.cssText = 'display: block;';
	};

	EFLCheckout.prototype.submitVoucher = function () {
		this.elements.$voucherSubmitBtn.prop('disabled', true);
		var voucherCode = this.elements.$voucherInput.val();

		if (voucherCode.length != 0) {
			this.applyVoucher(voucherCode);
		}
		else {
			this.voucherError(EFL.DiceRealmLabels.getInstance().getLabel('emptyVoucher'));
		}
	};

	EFLCheckout.prototype.applyVoucher = function (voucherCode) {
		// PUT baskets/vouchers/{voucherCode}
		//https://dicetech.atlassian.net/wiki/spaces/DD/pages/327759/Purchase#Purchase-ApplyVouchertoBasket
		this.getHeaders().then(function (headers) {
			EFL.HTTP.put(this.API_URL_BASE + 'baskets/vouchers/' + voucherCode, {
				headers: headers
			})
				.then(function (response) {
					//voucher applied

					//hide errors
					this.labels.$voucherError.hide();
					//turn green
					this.elements.$voucherInput.addClass('is-valid');
					this.elements.$voucherInput.removeClass('is-invalid');

					this.voucherCode = voucherCode;

					// Set cart object with discounted values
					this.CART = response.data;

					// Render updated payment total
					this.renderTotal();

				}.bind(this)).catch(function (error) {
					this.voucherError(EFL.DiceRealmLabels.getInstance().getLabel('invalidVoucher'));
					console.log(error);
				}.bind(this));
		}.bind(this));
	};

	EFLCheckout.prototype.voucherError = function (error) {
		this.elements.$voucherSubmitBtn.prop('disabled', false);
		this.elements.$voucherInput.addClass('is-invalid');
		this.labels.$voucherError.text(error);
		this.labels.$voucherError.show();
	};

	EFLCheckout.prototype._processPayment = function (paymentProviderID, setupIntentID, freeTrialSkip) {
		var _this = this;
		var withToken = window.EFL.DiceAuth.withToken;

		var payload = {
			'paymentProviderId' : paymentProviderID,
			'authorizationToken' : setupIntentID,
			'acknowledgeNoFreeTrial' : freeTrialSkip
		};
		return new Promise(function (resolve, reject) {
			var payBasket = function (token) {
				var API_HEADERS = EFL.DiceAuth.getAPIHeaders();
				var apiUrl = EFL.DiceAuth.API_URL_BASE + 'customer/baskets/pay';
				API_HEADERS['Authorization'] = 'Bearer ' + token.access_token;

				window.EFL.HTTP.post(apiUrl, {
					headers: API_HEADERS,
					data: JSON.stringify(payload)
				}) 
					.then(function (response) {
						resolve(response);
					})
					.catch(function (error) {
						//payment failed
						reject(error);
					});
			};
			withToken(payBasket)();
		});
	};

	EFLCheckout.prototype.paymentError = function (error) {
		// Enable payment buttons
		this.elements.$paySubmitBtn.prop('disabled', false);
		this.elements.$existingCardsContainer.find('button:disabled').prop('disabled', false);

		if (!error) {
			//generic error
			error = EFL.DiceRealmLabels.getInstance().getLabel('anErrorOccured');
		}
		this.labels.$paymentError.text(error);
		this.labels.$paymentError.show();
	};

	EFLCheckout.prototype.showPaymentOption = function (sectionName) {
		var sections = ["pay-existing-card", "pay-new-card", "pay-new-paypal"];
		if (sectionName == "pay-new-paypal") {
			//show paypal button
			this.elements.$paySubmitBtn.hide();
			this.elements.$paypalBtn.show();
		}
		else {
			this.elements.$paySubmitBtn.show();
			this.elements.$paypalBtn.hide();
		}
		for (var i = 0; i < sections.length; i++) {
			if (sections[i] === sectionName) {
				document.getElementById(sections[i]).style.cssText  = "";
			} else {
				document.getElementById(sections[i]).style.cssText  = "display: none;";
			}
		}
	};

	EFLCheckout.prototype.checkoutComplete = function () {
		//get payment details
		var data = this.analyticsData;
		data.cart = this.CART.basketItems[0];
		data.paymentData = this.paymentData
		data.discountedTotal = this.CART.discountedTotal;
		data.paymentData = this.paymentData;
		data.contactFullName = this.contactFullName;
		data.contactEmail = this.contactEmail;
		data.packageData = this.packageData;
		data.voucherCode = this.voucherCode;

		var searchParams = new URLSearchParams(window.location.search);
		var redirectUrl = searchParams.get('redirect');
		if (redirectUrl && redirectUrl.replace(window.location.origin, '').indexOf("://") === -1) { //ensure redirect isn't another site
			data.redirectUrl = redirectUrl;
		}

		//save checkout data to show on confirmation screen
		sessionStorage.setItem("EFLCheckoutComplete", JSON.stringify(data));
		//redirect to confirmation screen
		location.href = "/my-account/pay/complete" + location.search;
	};

	EFLCheckout.prototype.getPackageData = function () {
		this.HTTP.get(this.PACKAGE_INFO_API_URL)
			.then(function (result) {
				this.packageData = result.response;
			}.bind(this));
	};

	EFLCheckout.prototype.redirectToLogin = function () {
		location.href = "/my-account/?redirect=" + escape(location.href);
	};

	EFLCheckout.prototype.alreadyOwnLicence = function (sku) {
		if (!sku) {
			return Promise.reject();
		} else {
			var withToken = window.EFL.DiceAuth.withToken;
			return new Promise(function (resolve, reject) {
				var getLicences = function (token) {
					var API_HEADERS = EFL.DiceAuth.getAPIHeaders();
					var apiUrl = EFL.DiceAuth.API_URL_BASE + 'licence';
					API_HEADERS.Authorization = 'Bearer' + token.access_token;
					window.EFL.HTTP.get(apiUrl, {
						headers: API_HEADERS
					})
						.then(function (response) {
							var hasLicence = false;
							var licences = response.data;
							// eslint-disable-next-line guard-for-in
							for (var _licence in licences) {
								var licence = licences[_licence];
								if (licence.licence && licence.licence.amounts && licence.licence.amounts[0].sku === sku) {
									if (licence.licenceStatus && licence.licenceStatus.status === "ACTIVE") {
										//already have access to sku
										hasLicence = true;
										break;
									}
									
								}
							}
							resolve(hasLicence);
						})
						.catch(function (error) {
							reject(error);
						});
				};
				withToken(getLicences)();
			});
		}

	};

	EFLCheckout.prototype.trackPaymentDetails = function () {
		/* format from tracking spreadsheet:
		   dataLayer.push({
			   "event": "checkout",
			   "gift-status-hit": "$gift-status-hit",
			   "ecommerce": {
				  "currencyCode": "$currencyCode",
				  "checkout": {
					 "actionField": {
						"step": 2,
						"stepName": "payment-details"
					 },
					 "products": [{
						"brand": "football-team",
						"category": "Domestic | International | Premier National | Global Free",
						"id":  "BASIC | ((SEASON|MONTHLY|DAY|WEEK|)(D|E|I|PN)) | (MATCH(XD|XE|XI|A))",
						"name": "Free Pass | Match Pass | Day Pass | Week Pass | Month Pass | Season Pass | Audio Match Pass",
						"price": "$price",
						"quantity": 1,
						"variant": "$matchId" (match passes only),
						"dimension12": "$dimension12"
					 }]
				  }
			   }
			});
		 */
		var _this = this;
		EFL.EFLDiceLib.getTeamsFromSku(_this.SKU).then(function (eventString) {
			var userPackageData = _this.CART.basketItems[0];

			EFL.EFLDiceLib.parseTrackingData(
				userPackageData,
				userPackageData.subscriptionSetting.subscriptionType,
				userPackageData.subscriptionSetting.subscriptionPeriod,
				eventString,
				{
					price: userPackageData.catalogPrice,
					currency: userPackageData.currency,
					scale: userPackageData.scale
				})
				.then(function (data) {
					//store this data to pass to payment complete page.
					_this.analyticsData = data;

					var paymentDetails = {
						"event": "checkout",
						"gift-status-hit": data.giftingStatus,
						"ecommerce": {
							"currencyCode": data.currency,
							"checkout": {
								"actionField": {
									"step": 2,
									"stepName": "payment-details"
								},
								"products": [{ //only ever one sku in a basket
									"brand": EFL.video.thisClubName,
									"category": data.category,
									"id": data.id,
									"name": data.name,
									"price": data.price,
									"quantity": 1,
									"variant": data.variant,
									"dimension12": data.dimension12
								}]
							}
						}
					}

					//track chcekout obj
					dataLayer.push(paymentDetails);

					window.EFL.analyticsController.pageView(data.url + 'step-2/payment-details/', 'Registration');
				});
			
			
		}).catch(function (error) {
			console.error(error);
		});
	}

	

	// our instance holder
	// eslint-disable-next-line vars-on-top
	var instance;

	// an emulation of static variables and methods
	// eslint-disable-next-line vars-on-top
	var _static = {
		name: "EFLCheckout",

		// Method for getting an instance. It returns
		// a singleton instance of a singleton object
		getInstance: function (options) {
			if (instance === undefined) {
				instance = new EFLCheckout(options);
			}

			return instance;
		}
	};

	return _static;
}(window.jQuery, window.EFL));
;
window.EFL = window.EFL || {};

window.EFL.EFLCheckoutComplete = (function ($, EFL) {
	'use strict';

	//Constructor
	function EFLCheckoutComplete() {
		this.isAuthenticated = false;

		this.checkoutData = {};

		this.elements = {
			'$orderCompleteContainer': $('#complete #order-info'),
		};
	}

	EFLCheckoutComplete.prototype.init = function () {
		this.isAuthenticated = EFL.DiceAuth.isAuthenticated();
		if (!this.isAuthenticated) {
			this.redirectToLogin();
		}
		var urlParams = new URLSearchParams(window.location.search);
		this.SKU = urlParams.get('sku');
		if (this.SKU === '') {
			location.href = EFL.Dice.subscribeUrl;
		}

		var item = sessionStorage.getItem("EFLCheckoutComplete");
		if (!item) {
			this.redirectToLogin();
		}
		window.sessionStorage.removeItem("EFLCheckoutComplete");
		this.checkoutData = item ? JSON.parse(item) : {};
		

		this.checkoutComplete();

		//track purchase
		this.trackCheckout();
	};

	EFLCheckoutComplete.prototype.showSection = function (sectionName) {
		var sections = ["carterror", "loader", "complete"];
		for (var i = 0; i < sections.length; i++) {
			if (sections[i] === sectionName) {
				document.getElementById(sections[i]).style.cssText = "";
			} else {
				document.getElementById(sections[i]).style.cssText = "display: none;";
			}
		}
	}

	EFLCheckoutComplete.prototype.checkoutComplete = function () {
		var tpl = this.elements.$orderCompleteContainer.find('script#order-tpl').text();
		var html = Sqrl.render(tpl, this.checkoutData);
		this.elements.$orderCompleteContainer.html(html);
		this.showSection("complete");
	}

	EFLCheckoutComplete.prototype.trackCheckout = function () {
		/* format from tracking spreadsheet:
		   dataLayer.push({
			   "event": "checkout",
			   "gift-status-hit": "$gift-status-hit",
			   "ecommerce": {
				  "currencyCode": "$currencyCode",
				  "checkout": {
					 "actionField": {
						"step": 3,
						"stepName": "success"
					 },
					 "products": [{
						"brand": "football-team",
						"category": "Domestic | International | Premier National | Global Free",
						"id":  "BASIC | ((SEASON|MONTHLY|DAY|WEEK|)(D|E|I|PN)) | (MATCH(XD|XE|XI|A))",
						"name": "Free Pass | Match Pass | Day Pass | Week Pass | Month Pass | Season Pass | Audio Match Pass",
						"price": "$price",
						"quantity": 1,
						"variant": "$matchId" (match passes only),
						"dimension12": "$dimension12"
					 }]
				  }
			   }
			});

			dataLayer.push({
			   "event": "transaction",
			   "gift-status-hit": "$gift-status-hit",
			   "ecommerce": {
				  "currencyCode": "$currencyCode",
				  "purchase": {
					 "actionField": {
						"id": "$transactionId",
						"affiliation": "$affiliation"
						"revenue": "$totalTransactionValue",
					   "tax": "$taxValue",
					   "shipping": "$shippingValue",
					   "coupon": "$coupon"
					 },
					 "products": [{
						"brand": "football-team",
						"category": "Domestic | International | Premier National | Global Free",
						"id":  "BASIC | ((SEASON|MONTHLY|DAY|WEEK|)(D|E|I|PN)) | (MATCH(XD|XE|XI|A))",
						"name": "Free Pass | Match Pass | Day Pass | Week Pass | Month Pass | Season Pass | Audio Match Pass",
						"price": "$price",
						"quantity": 1,
						"variant": "$matchId" (match passes only),
						"dimension12": "$dimension12"
					 }]
				  }
			   }
			});
		 */
		var discountedTotal = this.checkoutData.discountedTotal;
		var userPackageData = this.checkoutData.cart;

		var pricePaid = (discountedTotal.amount / (Math.pow(10, discountedTotal.scale))).toFixed(discountedTotal.scale);
		if (discountedTotal.amount === 0) {
			//set tax to 0 if voucher used.
			userPackageData.tax = 0;
		}
		var taxPaid = (userPackageData.tax / (Math.pow(10, userPackageData.scale))).toFixed(userPackageData.scale);
	
		var checkoutSuccess = {
			"event": "checkout",
			"gift-status-hit": this.checkoutData.giftingStatus,
			"ecommerce": {
				"currencyCode": discountedTotal.currency,
				"checkout": {
					"actionField": {
						"step": 3,
						"stepName": "success"
					},
					"products": [{ //only ever one sku in a basket
						"brand": EFL.video.thisClubName,
						"category": this.checkoutData.category,
						"id": this.checkoutData.id,
						"name": this.checkoutData.name || userPackageData.name,
						"price": pricePaid,
						"quantity": 1,
						"variant": this.checkoutData.variant,
						"dimension12": this.checkoutData.dimension12
					}]
				}
			}
		}

		// don't have a transaction ID yet but need a unique identifier for this transaction. sku+email should be unique.
		// create a hash of it so not storing the email directly
		// Convert to 32bit integer 
		var stringToHash = function (string) {
			var hash = 0;
			var strLength = string.length;

			if (strLength == 0) return hash;

			for (var i = 0; i < strLength; i++) {
				var char = string.charCodeAt(i);
				hash = ((hash << 5) - hash) + char;
				hash = hash & hash;
			}
			return hash;
		} 

		var email = EFL.DiceAuth.getUser().contactEmail;
		//not convinced this hash is unique enough so will append date to it
		var hash = stringToHash(email + this.SKU).toString();
		var transactionId = hash.concat(Date.now());

		var checkoutTransaction = {
			"event": "transaction",
			"gift-status-hit": this.checkoutData.giftingStatus,
			"ecommerce": {
				"currencyCode": discountedTotal.currency,
				"purchase": {
					"actionField": {
						"id": transactionId,
						"affiliation": this.checkoutData.affiliation,
						"revenue": pricePaid, 
						"tax": taxPaid,
						"shipping": 0,
						"coupon": this.checkoutData.voucherCode
					},
					"products": checkoutSuccess.ecommerce.checkout.products //same product as above
				}
			}
		}
		
		//track chcekout obj
		dataLayer.push(checkoutSuccess);
		dataLayer.push(checkoutTransaction);

		window.EFL.analyticsController.pageView(this.checkoutData.url + 'step-3/success/', 'Registration');
	}

	EFLCheckoutComplete.prototype.getHeaders = function () {
		var API_HEADERS = _.clone(this.API_HEADERS);
		return EFL.DiceAuth.getTokenSilently().then(function (tokens) {
			API_HEADERS['Authorization'] = 'Bearer ' + tokens.access_token;
			return API_HEADERS;
		});
	};

	EFLCheckoutComplete.prototype.redirectToLogin = function () {
		location.href = "/my-account/?redirect=" + escape(location.href);
	};

	// our instance holder
	// eslint-disable-next-line vars-on-top
	var instance;

	// an emulation of static variables and methods
	// eslint-disable-next-line vars-on-top
	var _static = {
		name: "EFLCheckoutComplete",

		// Method for getting an instance. It returns
		// a singleton instance of a singleton object
		getInstance: function (options) {
			if (instance === undefined) {
				instance = new EFLCheckoutComplete(options);
			}

			return instance;
		}
	};

	return _static;
}(window.jQuery, window.EFL));
;
window.EFL = window.EFL || {};

window.EFL.EFLAddCard = (function ($, EFL) {
	'use strict';

	//Constructor
	function EFLAddCard() {
		this.STRIPE = {
			stripe: null,
			card: null,
			providerId: 0
		};
		this.PAYPAL = {
			authToken: null,
			providerId: 0
		};
		this.elements = {
			sectionPane: '.c-account-mgmt__section',
			sectionContentPane: '.c-account-mgmt__section-content',
			sectionEditPane: '.c-account-mgmt__section-edit',
			visibilityButton: '.c-account-mgmt__edit-actions .js-section-expand-button',
			'$registerCardButton': $('.c-account-mgmt__section-edit-actions button.js-register-card-button'),
			cardErrors: 'card-errors',
			cardSuccess: 'card-confirmation',
			paypalErrors: 'paypal-errors',
			paypalSuccess: 'paypal-confirmation',
			'$addCardContainer': $('div.c-account-mgmt__section[data-section-id="add-card"]'),
			'$addPaypalContainer': $('div.c-account-mgmt__section[data-section-id="add-paypal"]')
		};
	}

	EFLAddCard.prototype.init = function () {
		var isAuthenticated = EFL.DiceAuth.isAuthenticated();
		if (!isAuthenticated) {
			this.redirectToLogin();
		}
		this.initAddCardPage();
	};

	EFLAddCard.prototype.initAddCardPage = function() {
		var _this = this;

		Promise.all([
			this.renderStripe(),
			this.renderPaypal()
		])
		.then(function() {
			_this.sectionInitEditEvents();
			_this.sectionInitVisibilityEvents();
		})
	};

	EFLAddCard.prototype.sectionInitVisibilityEvents = function() {
		$(this.elements.visibilityButton).on('click', function(event) {
			var sectionId = $(event.currentTarget).data('section-id');

			this.toggleSectionVisibility(sectionId, event.currentTarget);
		}.bind(this));
	}
	
	EFLAddCard.prototype.toggleSectionEditPane = function(sectionId) {
		$(this.elements.sectionPane + '[data-section-id="' + sectionId + '"] ' + this.elements.editActionsButtons).toggleClass('hide');
		$(this.elements.sectionContentPane + '[data-section-id="' + sectionId + '"]').toggleClass('hide');
		$(this.elements.sectionEditPane + '[data-section-id="' + sectionId + '"]').toggleClass('hide');
	};

	EFLAddCard.prototype.toggleSectionVisibility = function(sectionId, el) {
		$(el).toggleClass('is-collapsed');
	};

	EFLAddCard.prototype.sectionInitEditEvents = function() {
		this.STRIPE.card.on('change', function (event) {
			if (event.error) {
				this.cardErrorMessage(true, event.error.message);
			} else {
				this.cardErrorMessage(false);
			}
		}.bind(this));

	 	this.elements.$registerCardButton.on('click', function(event) {
			var displayError = document.getElementById(this.elements.cardErrors);
			if (event.error) {
				this.cardErrorMessage(true, event.error.message);
			} else {
				this.cardErrorMessage(false);
				this.elements.$registerCardButton.prop('disabled', true);
				this.addCardToAccount();
			}
		}.bind(this));
	};

	EFLAddCard.prototype.renderStripe = function() {
		var _this = this;
		var withToken = window.EFL.DiceAuth.withToken;

		var renderSection = function (data) {

			var paymentData;
			for (var i = 0; i < data.availablePaymentProviders.length; i++) {
				var provider = data.availablePaymentProviders[i];

				if (provider.providerType === "STRIPE" || provider.paymentProviderType === "STRIPE") {
					paymentData = provider;
					this.STRIPE.providerId = provider.providerId;
				}
			}

			if (!paymentData) {
				this.elements.$addCardContainer.hide();
				return;
			}
			var stripeKey = paymentData.details.publishableKey;

			this.STRIPE.stripe = Stripe(stripeKey);
			var elements = this.STRIPE.stripe.elements();
			var style = {
				base: {
					fontSize: '16px',
					color: '#555555',
					fontFamily: "Open Sans, Helvetica, Arial, sans-serif",
				},
				invalid: {
					iconColor: '#dc3545',
					color: '#dc3545',
				},
			};
	
			//Create an instance of the card Element.
			//think we get postcode on signup
			this.STRIPE.card = elements.create('card', { style: style, hidePostalCode: true });
			// Add an instance of the card Element into the `card-element` <div>.
			this.STRIPE.card.mount('#card-element');

		}.bind(this)

		return new Promise(function(resolve, reject) {
			var getPaymentProvider = function (tokens) {
				var API_HEADERS = EFL.DiceAuth.getAPIHeaders();
				var apiUrl = EFL.DiceAuth.API_URL_BASE + 'customer/cards'
				API_HEADERS['Authorization'] = 'Bearer ' + tokens.access_token;

				window.EFL.HTTP.get(apiUrl, {
					headers: API_HEADERS
				})
					.then(function (response) {
						renderSection(response.data);
						resolve(response.data);
					})
					.catch(function (error) {
						reject(error);
					});
			};
			withToken(getPaymentProvider)();
		});
	};

	EFLAddCard.prototype.addCardToAccount = function () {
		var _this = this;
		EFL.EFLDiceLib.getStripeSetupIntent(this.STRIPE.providerId)
			.then(function(results) {
				// Have setup intent - call stripe sca api
				return _this.STRIPE.stripe.confirmCardSetup(results.data.clientSecret, { 'payment_method': { 'card' : _this.STRIPE.card}})
			}).then(function (result) {
				// Sca failed
				if(!result.setupIntent) {
					return Promise.reject(result);
				}
				// Sca success - call add new card api
				return _this.addNewSCACard(_this.STRIPE.providerId, result.setupIntent.id);
			}).then(function (success) {
				// Card added successfully
				var successEl = document.getElementById(_this.elements.cardSuccess);
				successEl.textContent = EFL.DiceRealmLabels.getInstance().getLabel('cardBeingAdded');
			}).catch(function (error) {
				var errorMessage = "";
				if(error && error.status == 409) {
					// Failed to add card to Dice
					errorMessage = EFL.DiceRealmLabels.getInstance().getLabel('cardBeingAddedError')
				} else if(error && error.error && error.error.message) {
					// Stripe error message
					errorMessage = error.error.message;
				} else {
					// Generic error message
					errorMessage = EFL.DiceRealmLabels.getInstance().getLabel('paymentCardError');
				}
				_this.cardErrorMessage(true,errorMessage)
				console.error(error);
			});
	};

	EFLAddCard.prototype.addNewSCACard = function (providerId, setupIntentID) {
		var withToken = window.EFL.DiceAuth.withToken;
		return new Promise(function (resolve, reject) {
			if (!providerId && !setupIntentID) {
				return reject();
			}
			var addCard = function (token) {
				var API_HEADERS = EFL.DiceAuth.getAPIHeaders();
				var apiUrl = EFL.DiceAuth.API_URL_BASE + 'customer/cards/sca/' + providerId + '/' + setupIntentID;
				API_HEADERS['Authorization'] = 'Bearer ' + token.access_token;

				window.EFL.HTTP.post(apiUrl, {
					headers: API_HEADERS
				})
					.then(function (response) {
						resolve(response);
					})
					.catch(function (error) {
						reject(error);
					});
			};
			withToken(addCard)();
		});
	};

	EFLAddCard.prototype.cardErrorMessage = function (show, error) {
		var displayError = document.getElementById(this.elements.cardErrors);
		if(show) {
			//show
			//console.log(show, error);
			this.elements.$registerCardButton.prop('disabled', false);
			displayError.textContent = error;
			displayError.style.display = 'initial';
		} else {
			//hide
			displayError.textContent = "";
		}
	};

	EFLAddCard.prototype.renderPaypal = function () {
		var _this = this;
		var withToken = window.EFL.DiceAuth.withToken;

		var renderSection = function(data) {
			var paymentData;
			for (var i = 0; i < data.availablePaymentProviders.length; i++) {
				var provider = data.availablePaymentProviders[i];

				if (provider.providerType === "PAYPAL" || provider.paymentProviderType === "PAYPAL") {
					paymentData = provider;
					_this.PAYPAL.providerId = provider.providerId;
				}
			}

			if (!paymentData) {
				_this.elements.$addPaypalContainer.hide();
				return;
			}
			var paypalKey = paymentData.details.publishableKey;

			//attach script to html head
			var PAYPAL_SCRIPT = 'https://www.paypal.com/sdk/js?client-id=' + paypalKey + '&vault=true&components=buttons&disable-funding=card';
			var script = document.createElement('script');
			script.setAttribute('src', PAYPAL_SCRIPT);
			document.head.appendChild(script);

			//when script has loaded init button
			script.addEventListener("load", function () {
				paypal.Buttons({
					createOrder: function () {
						return _this.getPaypalAuthToken();
					},
					onApprove: function (data, actions) {
						return _this.addPaypalToAccount();
					},
				}).render('#btn-paypal');
			});
		}
		return new Promise(function(resolve, reject) {
			var getPaymentProvider = function (tokens) {
				var API_HEADERS = EFL.DiceAuth.getAPIHeaders();
				var apiUrl = EFL.DiceAuth.API_URL_BASE + 'customer/cards'
				API_HEADERS['Authorization'] = 'Bearer ' + tokens.access_token;

				window.EFL.HTTP.get(apiUrl, {
					headers: API_HEADERS
				})
					.then(function (response) {
						renderSection(response.data);
						resolve(response.data);
					})
					.catch(function (error) {
						_this.paypalErrorMessage(true,EFL.DiceRealmLabels.getInstance().getLabel('paymentCardError'));
						reject(error);
					});
			};
			withToken(getPaymentProvider)();
		});
	};

	EFLAddCard.prototype.getPaypalAuthToken = function () {
		var _this = this;
		var withToken = window.EFL.DiceAuth.withToken;

		return new Promise(function(resolve, reject) {
			var getPaymentProvider = function (tokens) {
				var API_HEADERS = EFL.DiceAuth.getAPIHeaders();
				var apiUrl = EFL.DiceAuth.API_URL_BASE + 'customer/paypal/auth-token/' + _this.PAYPAL.providerId
				API_HEADERS['Authorization'] = 'Bearer ' + tokens.access_token;

				window.EFL.HTTP.get(apiUrl, {
					headers: API_HEADERS
				})
					.then(function (response) {
						_this.PAYPAL.authToken = response.response.paypalAuthToken;
						_this.paypalErrorMessage(false);
						resolve(_this.PAYPAL.authToken);
					})
					.catch(function (error) {
						_this.paypalErrorMessage(true,EFL.DiceRealmLabels.getInstance().getLabel('paymentCardError'));
						reject(error);
					});
			};
			withToken(getPaymentProvider)();
		});
	};

	EFLAddCard.prototype.addPaypalToAccount = function () {
		var _this = this;
		var withToken = window.EFL.DiceAuth.withToken;

		return new Promise(function(resolve, reject) {
			var getPaymentProvider = function (tokens) {
				var API_HEADERS = EFL.DiceAuth.getAPIHeaders();
				var apiUrl = EFL.DiceAuth.API_URL_BASE + 'customer/paypal/create-authorization/' + _this.PAYPAL.providerId + '/' + _this.PAYPAL.authToken
				API_HEADERS['Authorization'] = 'Bearer ' + tokens.access_token;

				window.EFL.HTTP.post(apiUrl, {
					headers: API_HEADERS
				})
					.then(function (response) {
						var successEl = document.getElementById(_this.elements.paypalSuccess);
						//TODO: get label for this
						successEl.textContent = "Paypal successfully added";

						resolve(response.response.cardId);
					})
					.catch(function (error) {
						_this.paypalErrorMessage(true,EFL.DiceRealmLabels.getInstance().getLabel('paymentCardError'));
						reject(error);
					});
			};
			withToken(getPaymentProvider)();
		});
	};

	EFLAddCard.prototype.paypalErrorMessage = function (show, error) {
		var displayError = document.getElementById(this.elements.paypalErrors);
		if(show) {
			//show
			//console.log(show, error);
			displayError.textContent = error;
			displayError.style.display = 'initial';
		} else {
			//hide
			displayError.textContent = "";
		}
	};

	EFLAddCard.prototype.redirectToLogin = function () {
		location.href = "/my-account/?redirect=" + escape(location.href);
	};

	// our instance holder
	// eslint-disable-next-line vars-on-top
	var instance;

	// an emulation of static variables and methods
	// eslint-disable-next-line vars-on-top
	var _static = {
		name: "EFLAddCard",

		// Method for getting an instance. It returns
		// a singleton instance of a singleton object
		getInstance: function (options) {
			if (instance === undefined) {
				instance = new EFLAddCard(options);
			}

			return instance;
		}
	};

	return _static;
}(window.jQuery, window.EFL));
;
/* eslint-disable complexity */
/* eslint-disable block-scoped-var */
/* eslint-disable vars-on-top */
window.EFL = window.EFL || {};

window.EFL.EFLSubscribePackages = (function($, EFL) {
	'use strict';

	var getUnixTimeInMs = EFL.DateUtils.getUnixTimeInMs;

	/**
	 * Main EFLSubscribePackages function constructor function.
	 * @method EFLSubscribePackages
	 * @return {void}
	 */
	function EFLSubscribePackages() {
		this.elements = {
			'$component': $('.js-subscribe-packages'),
		};
		this.classes = {
			component: '.js-subscribe-packages'
		};
		this.HTTP = EFL.HTTP;
		this.DICE_API_URL_BASE = EFL.Dice.apiUrl + '/api/v2/';
		this.PACKAGE_INFO_API_URL = '/api/dice/packageinfo';
		this.NO_PACKAGES = false;
	}

	/**
	 * Initialize the module on the subscribe page by calling all init functions, like events and so on.
	 */
	EFLSubscribePackages.prototype.initSubscribePage = function() {
		Promise.all([this.getAllPackages(), this.getDicePackages(), EFL.EFLDiceLib.getUpcomingMatches({ rpp: 10 }), EFL.EFLDiceLib.getLiveEvents(new Date())])
			.then(this._cleanPackagesData.bind(this))
			.then(this._compilePageTemplate.bind(this))
			.then(this._renderPageComponent.bind(this))
			.then(this.initGenericEvents.bind(this))
			.catch(this.handleError.bind(this));
	};

	EFLSubscribePackages.prototype.initSubscribeOverlay = function(vodId, isPremium, isClubEvent) {
		// eslint-disable-next-line max-len
		Promise.all([this.getAllPackages(), this.getDicePackagesByVideo(vodId, isClubEvent)])
			.then(function(data) { return this._cleanPackagesDataOverlay(data, isPremium); }.bind(this))
			.then(this._compileOverlayTemplate.bind(this))
			.then(this._renderOverlayComponent.bind(this))
			.then(this.initGenericEvents.bind(this))
			.catch(this.handleOverlayError.bind(this));
	};

	/**
	 * The main purpose of this function is to clean the Packages from the data.
	 * We should only show those that are part of the Dice call.
	 */
	// eslint-disable-next-line complexity
	EFLSubscribePackages.prototype._cleanPackagesData = function(data) {
		var compData = data[0].data;
		var diceLicenses = data[1].data;
		var upcomingEventsData = data[2].data.events;
		var liveEventsData = data[3].events;

		var diceLicensesNames = [];
		var diceLicensesBySubscriptionPeriodAndFormat = {};
		var diceLicensesByTypeAndFormat = { 'Audio': [], 'Video': []};
		var skusToHide = [];
		var userTokenData = EFL.DiceAuth.getDecodedTokenData();
		var userIsGBLocal = userTokenData ? (['GB', 'GG', 'IE', 'IM', 'JE'].indexOf(userTokenData.lo2.substring(0, 2)) > -1 ? true : false) : false;
		var isPremierNational = compData.IsPremierNational;
		var freePassIndex = -1;
		var keepLiveGamesActiveInMs = compData.MatchPassKeepDisplayedForMinutes * 60000;
		var currentTimeInMs = getUnixTimeInMs(new Date());
		var upcomingMatchesHaveAudioPass = false;

		for (var i = 0; i < diceLicenses.length; i++) {
			var subscriptionPeriod = diceLicenses[i].licence.purchaseStrategy.subscriptionPeriod;
			var type = diceLicenses[i].licence.purchaseStrategy.type;
			var licenceFormat = ((diceLicenses[i].licence.name.indexOf("Video") >= 0) ? "Video" : "Audio");

			diceLicensesNames.push(diceLicenses[i].licence.name.trim());

			if (subscriptionPeriod) {
				diceLicensesBySubscriptionPeriodAndFormat[
					(subscriptionPeriod + licenceFormat)
				] = diceLicenses[i];
			} else if (type === 'PPV') {
				diceLicensesByTypeAndFormat[licenceFormat].push(diceLicenses[i]);
			}

			if (diceLicenses[i].licence.type === 'FREE') {
				freePassIndex = i;
				if (diceLicenses[i].licence.amounts === undefined) {
					diceLicenses[i].licence.amounts = [
						{
							sku: '',
							default: true,
							local: true,
							scale: 2,
							amount: 0,
							currency: 'GBP'
						}
					];
				}
			}
		}

		//if more than one event in the list have the same optaid then only show one.
		//likely one is a audio event and one is video - we only want to show the video event
		var newevents = [];

		var allEvents = liveEventsData.concat(upcomingEventsData);

		for (var _event in allEvents) {
			var diceEvent = allEvents[_event];
			//already seen this externalId?
			var exist = _.findIndex(newevents, { externalId: diceEvent.externalId })

			if (exist === -1) {
				//not seen this one before
				newevents.push(diceEvent);
			} else {
				//seen this one so check if it had video match passes
				var prevContainsVideoMatchPass = false;
				var prevContainsAnyMatchPass = false;
				var prevEvent = newevents[exist];
				var isPrevVideoEventWithAccess = !prevEvent.audioOnly && prevEvent.accessLevel == "GRANTED";
				var isVideoEventWithAccess = !diceEvent.audioOnly && diceEvent.accessLevel == "GRANTED";


				var ap = prevEvent.availablePurchases;
				var apLen = prevEvent.availablePurchases.length;
				if (apLen > -1) {
					for (var i = 0; i < apLen; i++) {

						if (ap[i].purchaseStrategy.type
							&& ap[i].purchaseStrategy.type === "PPV") {

							//match pass found
							prevContainsAnyMatchPass = true;

							if (ap[i].name.toUpperCase().indexOf('VIDEO') >= 0) {
								//video match pass found
								prevContainsVideoMatchPass = true;
							}
						}
					}
				}

				if (!isPrevVideoEventWithAccess) {
					if (isVideoEventWithAccess || (!prevContainsAnyMatchPass && !prevContainsVideoMatchPass)) {
						//no match pass found in previous event replace with new one or this event is a video with access granted
						newevents[exist] = diceEvent
					} else if (prevContainsAnyMatchPass && !prevContainsVideoMatchPass) {
						//audio match pass found, check this new event to see if it contains a video one
						var ap = diceEvent.availablePurchases;
						var apLen = diceEvent.availablePurchases.length;
						if (apLen > -1) {
							for (var i = 0; i < apLen; i++) {

								if (ap[i].purchaseStrategy.type
									&& ap[i].purchaseStrategy.type === "PPV"
									&& ap[i].name.toUpperCase().indexOf('VIDEO') >= 0) {
									//video match pass found so replace with this one
									newevents[exist] = diceEvent
								}
							}
						}
					}
					//else video match pass found

				}
			}
		}


		allEvents = newevents;

		var upcomingMatches = [];
		//TODO: Gifting: revist when gifting has been added. atm after purchase no licenses are available.

		// eslint-disable-next-line guard-for-in
		for (var _event in allEvents) {
			var diceEvent = allEvents[_event];
			if (diceEvent.accessLevel === "GRANTED") {
				//already own
				var matchData = compData.MatchPassMatches.find(function (match) {
					return match.OptaId === diceEvent.externalId;
				});
				if (matchData) {
					//set up opponent data used for club logo
					var compLogos = window.EFL.competitionLogos || {};
					var comp = compLogos.filter(function (arr) { return arr.OptaId == matchData.CompetitionOptaId })[0];
					if (comp) {
						matchData.CompetitionName = comp.Title;
						matchData.ImgSrc = comp.ImgSrc;
						matchData.ImgAlt = comp.ImgAlt;
					} else {
						matchData.CompetitionName = "";
					}

					var opponentTeamOptaId = matchData.HomeTeamOptaId;
					var opponentTeamName = matchData.HomeTeam;
					if (opponentTeamOptaId === window.EFL.video.clubOptaId) {
						opponentTeamOptaId = matchData.AwayTeamOptaId;
						opponentTeamName = matchData.AwayTeam;
					}
					matchData.dice = diceEvent;
					matchData.alreadyOwn = true;
					matchData.opponentTeamName = opponentTeamName;
					matchData.opponentTeamOptaId = opponentTeamOptaId.replace("t", "");
					matchData.Variant = matchData.HomeTeam + "-vs-" + matchData.AwayTeam + "-(" + matchData.OptaId + ")-on-" + matchData.KO.substring(0, 10);

					upcomingMatches.push(matchData);
				}
			} else {
				//only want to show one match pass per event- preferably video.
				var passToShow = undefined;
				var ap = diceEvent.availablePurchases;
				var apLen = diceEvent.availablePurchases.length;
				if (apLen > -1) {
					for (var i = 0; i < apLen; i++) {
						if (ap[i].purchaseStrategy.type && ap[i].purchaseStrategy.type === "PPV") {
							if (!passToShow) {
								passToShow = ap[i];
							} else {
								if (passToShow.name.toUpperCase().indexOf('VIDEO') === -1) {
									passToShow = ap[i];
								}
							}
						}
					}
				}

				// Get local currency sku
				if(passToShow){
                    var defaultAmount;
                    var amountObj = _.find(passToShow.amounts, function(o) {
                        if (o.default) {
                            defaultAmount = o;
                        }
                        return o.local;
                    });
                    passToShow.computedAmount = amountObj ? amountObj : defaultAmount;
				}
				
				// eslint-disable-next-line no-loop-func
				var matchData = compData.MatchPassMatches.find(function (match) {
					return match.OptaId === diceEvent.externalId;
				});
				var shouldShow = true;

				if (diceEvent.live) {
					shouldShow = (diceEvent.startDate + keepLiveGamesActiveInMs) > currentTimeInMs;
				}

				if (matchData && shouldShow) {
					//diceEvent contains licence info
					//matchData contains info about the fixure

					//set up opponent data used for club logo
					var compLogos = window.EFL.competitionLogos || {};
					var comp = compLogos.filter(function (arr) { return arr.OptaId == matchData.CompetitionOptaId })[0];
					if (comp) {
						matchData.CompetitionName = comp.Title;
						matchData.ImgSrc = comp.ImgSrc;
						matchData.ImgAlt = comp.ImgAlt;
					} else {
						matchData.CompetitionName = "";
					}

					//get opponent
					var opponentTeamOptaId = matchData.HomeTeamOptaId;
					var opponentTeamName = matchData.HomeTeam;
					if (opponentTeamOptaId === window.EFL.video.clubOptaId) {
						opponentTeamOptaId = matchData.AwayTeamOptaId;
						opponentTeamName = matchData.AwayTeam;
					}
					matchData.dice = diceEvent;
					matchData.diceLicence = passToShow;
					matchData.alreadyOwn = false;
					matchData.opponentTeamName = opponentTeamName;
					matchData.opponentTeamOptaId = opponentTeamOptaId.replace("t", "");
					matchData.Variant = matchData.HomeTeam + "-vs-" + matchData.AwayTeam + "-(" + matchData.OptaId + ")-on-" + matchData.KO.substring(0, 10);

					upcomingMatches.push(matchData);
				}
			}
		}

		// Do any of the upcoming matches have a valid audio match pass?
		upcomingMatchesHaveAudioPass = this.audioMatchPassIsAvailable(upcomingMatches);

		// SkusToHide values will be splited in the string using `|`, `,` or `;`
		skusToHide = compData.SkusToHide ? compData.SkusToHide.split(/[|,;]+/) : [];

		// eslint-disable-next-line complexity
		compData.Packages = compData.Packages.reduce(function(acc, pkg) {
			var diceLicence;
			var diceMatchPasses = [];
			var minAmount = 99999999999;
			var maxAmount = 0;
			var defaultAmountIdx = 0;

			if (pkg.SubscriptionPeriod) {
				diceLicence = diceLicensesBySubscriptionPeriodAndFormat[pkg.SubscriptionPeriod + (pkg.IsVideo ? 'Video' : 'Audio')];
			} else if (pkg.TypeName === 'Free') {
				diceLicence = diceLicenses[freePassIndex];
			} else if (pkg.IsMatchPass) {
				diceMatchPasses = diceLicensesByTypeAndFormat[(pkg.IsVideo ? 'Video' : 'Audio')];

				if (diceMatchPasses.length >= 2) {
					var firstItemDefaultAmountIndex = 0;
					for (var index = 0; index < diceMatchPasses.length; index++) {
						for (var idx = 0; idx < diceMatchPasses[index].licence.amounts.length; idx++) {
							if (diceMatchPasses[index].licence.amounts[idx].default) {
								defaultAmountIdx = idx;
								break;
							}
						}

						if (index === 0) {
							firstItemDefaultAmountIndex = defaultAmountIdx;
						}

						var licenceAmount = diceMatchPasses[index].licence.amounts[defaultAmountIdx].amount;

						if (licenceAmount > maxAmount) {
							maxAmount = licenceAmount;
						}

						if (licenceAmount < minAmount) {
							minAmount = licenceAmount;
						}
					}

					if (minAmount !== maxAmount) {
						diceMatchPasses[0].licence.amounts[firstItemDefaultAmountIndex].minAmount = minAmount;
						diceMatchPasses[0].licence.amounts[firstItemDefaultAmountIndex].maxAmount = maxAmount;
					}
				}

				diceLicence = diceMatchPasses[0];
			}

			if (diceLicence) {
				var defaultAmount;
				// This retrieves the `amount` object from Dice API response that is best suited to be used.
				// The API returns amounts as an Array and we need to select only 1 item from it, based on
				// the following criteria:
				// IF there is one item with `obj.local: true` then we chose that one.
				// ELAE we chose the one that has `obj.default: true`.
				var amountObj = _.find(diceLicence.licence.amounts, function(o) {
					if (o.default) {
						defaultAmount = o;
					}
					return o.local;
				});
				diceLicence.computedAmount = amountObj ? amountObj : defaultAmount;

				if (skusToHide.indexOf(diceLicence.computedAmount.sku) === -1) {
					if (
						(!isPremierNational &&
						(pkg.TypeName.startsWith('Free') ||
							!pkg.IsMatchPass ||
							(userIsGBLocal && pkg.IsMatchPass && pkg.TypeName.startsWith('Domestic') && EFLSubscribePackages.prototype.matchPassIsAllowed(pkg, upcomingMatchesHaveAudioPass)) ||
							(!userIsGBLocal && pkg.IsMatchPass && pkg.TypeName.startsWith('International') && EFLSubscribePackages.prototype.matchPassIsAllowed(pkg, upcomingMatchesHaveAudioPass)))) ||
						(isPremierNational && (pkg.TypeName.startsWith('PN') || pkg.TypeName.startsWith('Free')))
					) {
						var item = pkg;
						item.diceLicence = diceLicence;
						acc.push(item);
					}
				}
			}

			return acc;
		}, []);

		if (compData.Packages.length === 0) {
			//this.handleNoPackages();
			this.NO_PACKAGES = true;
			return;
		}

		if(upcomingMatches.length > 0) {
			compData.nextmatchData = upcomingMatches[0];
			if(upcomingMatches.length > 1) {
				upcomingMatches.shift();
				compData.additionalMatchData = upcomingMatches;
			}
		}

		return Promise.resolve(compData);
	};

	EFLSubscribePackages.prototype._cleanPackagesDataOverlay = function(data, isPremium) {
		var compData = data[0].data;
		var diceLicenses = data[1].data;

		var diceLicensesNames = [];
		var diceLicensesBySubscriptionPeriodAndFormat = {};
		var diceLicensesByTypeAndFormat = { 'Audio': [], 'Video': []};
		var skusToHide = [];
		var userTokenData = EFL.DiceAuth.getDecodedTokenData();
		var userIsGBLocal = userTokenData ? (['GB', 'GG', 'IE', 'IM', 'JE'].indexOf(userTokenData.lo2.substring(0, 2)) > -1 ? true : false) : false;
		var isPremierNational = compData.IsPremierNational;
		var freePassIndex = -1;
		var videoData = data[1] != null ? data[1].data : null;

		for (var i = 0; i < diceLicenses.availablePurchases.length; i++) {
			var currentLicence = diceLicenses.availablePurchases[i];
			var subscriptionPeriod = currentLicence.purchaseStrategy.subscriptionPeriod;
			var type = currentLicence.purchaseStrategy.type;
			var licenceFormat = ((currentLicence.name.indexOf("Video") >= 0) ? "Video" : "Audio");

			diceLicensesNames.push(currentLicence.name.trim());

			if (subscriptionPeriod) {
				diceLicensesBySubscriptionPeriodAndFormat[
					(subscriptionPeriod + licenceFormat)
				] = currentLicence;
			} else if (type === 'PPV') {
				diceLicensesByTypeAndFormat[licenceFormat].push(currentLicence);
			}

			if (currentLicence.type === 'FREE') {
				freePassIndex = i;
				if (currentLicence.amounts === undefined) {
					currentLicence.amounts = [
						{
							sku: '',
							default: true,
							local: true,
							scale: 2,
							amount: 0,
							currency: 'GBP'
						}
					];
				}
			}
		}

		// SkusToHide values will be splited in the string using `|`, `,` or `;`
		skusToHide = compData.SkusToHide ? compData.SkusToHide.split(/[|,;]+/) : [];

		// eslint-disable-next-line complexity
		compData.Packages = compData.Packages.reduce(function(acc, pkg) {
			var diceLicence;
			var diceMatchPasses = [];
			var minAmount = 99999999999;
			var maxAmount = 0;
			var defaultAmountIdx = 0;

			if (pkg.SubscriptionPeriod) {
				diceLicence = diceLicensesBySubscriptionPeriodAndFormat[pkg.SubscriptionPeriod + (pkg.IsVideo ? 'Video' : 'Audio')];
			} else if (pkg.TypeName === 'Free') {
				compData.FreePass = pkg;
			} else if (pkg.IsMatchPass) {
				diceMatchPasses = diceLicensesByTypeAndFormat[(pkg.IsVideo ? 'Video' : 'Audio')];

				if (diceMatchPasses.length >= 2) {
					var firstItemDefaultAmountIndex = 0;
					for (var index = 0; index < diceMatchPasses.length; index++) {
						for (var idx = 0; idx < diceMatchPasses[index].amounts.length; idx++) {
							if (diceMatchPasses[index].amounts[idx].default) {
								defaultAmountIdx = idx;
								break;
							}
						}

						if (index === 0) {
							firstItemDefaultAmountIndex = defaultAmountIdx;
						}

						var licenceAmount = diceMatchPasses[index].amounts[defaultAmountIdx].amount;

						if (licenceAmount > maxAmount) {
							maxAmount = licenceAmount;
						}

						if (licenceAmount < minAmount) {
							minAmount = licenceAmount;
						}
					}

					if (minAmount !== maxAmount) {
						diceMatchPasses[0].amounts[firstItemDefaultAmountIndex].minAmount = minAmount;
						diceMatchPasses[0].amounts[firstItemDefaultAmountIndex].maxAmount = maxAmount;
					}
				}

				diceLicence = diceMatchPasses[0];
			}

			if (diceLicence) {
				// TODO: Not sure if this IF is needed anymore
				if (pkg.TypeName !== "Free") {
					if (!videoData) {
						return acc;
					}

					var videoLicence = videoData.availablePurchases.find(function(availablePurchase) {
						return availablePurchase.name.trim() === pkg.Name.trim();
					});

					if (videoLicence) {
						diceLicence = videoLicence;
					} else {
						return acc;
					}
				}

				var defaultAmount;
				// This retrieves the `amount` object from Dice API response that is best suited to be used.
				// The API returns amounts as an Array and we need to select only 1 item from it, based on
				// the following criteria:
				// IF there is one item with `obj.local: true` then we chose that one.
				// ELAE we chose the one that has `obj.default: true`.
				var amountObj = _.find(diceLicence.amounts, function(o) {
					if (o.default) {
						defaultAmount = o;
					}
					return o.local;
				});
				diceLicence.computedAmount = amountObj ? amountObj : defaultAmount;

				if (skusToHide.indexOf(diceLicence.computedAmount.sku) === -1) {
					if (
						(!isPremierNational &&
						(pkg.TypeName.startsWith('Free') ||
						!pkg.IsMatchPass ||
						(userIsGBLocal && pkg.IsMatchPass && pkg.TypeName.startsWith('Domestic')) ||
						(!userIsGBLocal && pkg.IsMatchPass && pkg.TypeName.startsWith('International')))) ||
						(isPremierNational && (pkg.TypeName.startsWith('PN') || pkg.TypeName.startsWith('Free')))
					) {
						var item = pkg;
						item.diceLicence = diceLicence;
						acc.push(item);
					}
				}
			}

			return acc;
		}, []);
		compData.HideBasicPass = !!isPremium;

		if (compData.Packages.length === 0) {
			this.NO_PACKAGES = true;
			return;
		}

		var matchPassesArr = diceLicensesByTypeAndFormat.Video;

		if (matchPassesArr.length === 0) {
			matchPassesArr = diceLicensesByTypeAndFormat.Audio;
		}

		//load match data from epi
		//var matchData = compData.MatchPassMatches.find(function (match) {
		//	return match.OptaId === videoData.externalId;
		//});

		//if (matchData) {
		//	//set up opponent data used for club logo
		//	var opponentTeamOptaId = matchData.HomeTeamOptaId;
		//	var opponentTeamName = matchData.HomeTeam;
		//	if (opponentTeamOptaId === window.EFL.video.clubOptaId) {
		//		opponentTeamOptaId = matchData.AwayTeamOptaId;
		//		opponentTeamName = matchData.AwayTeam;
		//	}

		//	matchData.opponentTeamName = opponentTeamName;
		//	matchData.opponentTeamOptaId = opponentTeamOptaId.replace("t", "");

		//	matchData.alreadyOwn = videoData.accessLevel === "GRANTED" ? true : false;
		//	matchData.videoData = videoData; 
		//}


		var matchData = window.EFL.MatchPassData
		if (matchData) {
			//set up opponent data used for club logo
			var compLogos = window.EFL.competitionLogos || {};
			var comp = compLogos.filter(function (arr) { return arr.OptaId == matchData.CompetitionID })[0];
			if (comp) {
				matchData.CompetitionName = comp.Title;
				matchData.ImgSrc = comp.ImgSrc;
				matchData.ImgAlt = comp.ImgAlt;
			} else {
				matchData.CompetitionName = "";
			}
			matchData.alreadyOwn = videoData.accessLevel === "GRANTED" ? true : false;
			matchData.videoData = videoData; 
		}

		if (!videoData || videoData.type === "VOD" || !matchData || matchPassesArr.length === 0) {
			compData.HideMatchPasses = true;
		} else {
			compData.nextMatchData = matchData;
			compData.matchPassesArr = matchPassesArr;
		}

		var hasSubscriptions = compData.Packages.find(function(pkg){
			return pkg.diceLicence.purchaseStrategy.type === "SUBSCRIPTION";
		});

		compData.HideSubscription = !hasSubscriptions;

		// Always hiding heading for the overlay
		compData.HideHeading = true;

		var index = 0;

		compData.HeadingEvenClass = this._getEvenClass(!compData.HideHeading && index++);
		compData.BasicPassEvenClass = this._getEvenClass(!compData.HideBasicPass && index++);
		compData.MatchPassEvenClass = this._getEvenClass(!compData.HideMatchPasses && index++);
		compData.SubscriptionEvenClass = this._getEvenClass(!compData.HideSubscription && index++);

		return Promise.resolve(compData);
	};

	EFLSubscribePackages.prototype._getEvenClass = function(index) {
		if (index === false) {
			return '';
		}

		return index % 2 === 0 ? 'even' : '';
	};

	EFLSubscribePackages.prototype._compilePageTemplate = function(tplData) {
		var tpl = this.elements.$component.find(this.NO_PACKAGES ? 'script#subscribe-packages-none-tpl' : 'script#subscribe-packages-tpl').first().text();
		var html = Sqrl.render(tpl, tplData);

		return Promise.resolve(html);
	};

	EFLSubscribePackages.prototype._renderPageComponent = function(html) {
		this.elements.$component.html(html);

		return Promise.resolve('rendered');
	};

	EFLSubscribePackages.prototype._compileOverlayTemplate = function (tplData) {
		var template = "script#modal-packages-overlay-tpl"
		if (window.EFL.video.videoDisabled) {
			template = "script#subscribe-packages-overlay-disabled-tpl"
		} else if (this.NO_PACKAGES) {
			template = "script#subscribe-packages-overlay-none-tpl";
		}
		var tpl = $('body').find(template).first().text();
		var html = Sqrl.render(tpl, tplData);

		return Promise.resolve(html);
	};

	EFLSubscribePackages.prototype._renderOverlayComponent = function(html) {
		$('[data-video-packages]').html(html);

		$('#loginContainer .btn.js-show-login').click(function() {
			// Initialise the login script used by the modal
			EFL.EFLLogin.getInstance().init();
		});

		return Promise.resolve('rendered');
	};

	/**
	 * Initialise UI events related to reset password, forgot password.
	 * @method initGenericEvents
	 * @return {void}
	 */
	EFLSubscribePackages.prototype.initGenericEvents = function() {
		// TODO: Perhaps this seclector should be a bit more specific.
		this.elements.$component.find('.js-gift-label').on('click', function(e) {
			e.stopPropagation();
			this.setGiftPurchase($(e.currentTarget));
		}.bind(this));

		$("a[data-variant]").on('click', function (e) {
			// when purchasing a match pass store the variant in local storage to add to analytics later.
			var cacheKey = 'EFLMatchPassVariant'// + $(this).data('sku');
			window.localStorage.setItem(cacheKey, JSON.stringify({ variant: $(this).data('variant'), expiry: Date.now() + 600000 }));

			//console.log(cacheKey, $(this).data('variant'))
			return true;
		});
		//scroll to anchor link after content has loaded
		var anchorLink = window.location.hash.substr(1);
		if (anchorLink) {
			var anchorEl = document.getElementsByName(anchorLink)[0];
			if (anchorEl) {
				anchorEl.scrollIntoView({
					behavior: 'smooth'
				});
			}	
		}

		//trigger match-heights
		window.EFL.eventDispatcher.dispatch("markup-injected");
	};

	EFLSubscribePackages.prototype.getAllPackages = function() {
		var displayid = $('.c-subscribe-packages').data('displayid');
		var query = displayid ? ('?id=' + displayid) : '';
		var API_URL = this.PACKAGE_INFO_API_URL + query;

		return this.HTTP.get(API_URL);
	};

	EFLSubscribePackages.prototype.getDicePackages = function() {
		var _this = this;
		return new Promise(function (resolve, reject) {
			EFL.DiceAuth.getTokenSilently()
				.then(function(tokens) {
					var API_HEADERS = EFL.DiceAuth.getAPIHeaders();
					API_HEADERS['Authorization'] = 'Bearer ' + tokens.access_token;

					_this.HTTP.get(_this.DICE_API_URL_BASE + 'licence', {
						headers: API_HEADERS
					})
						.then(function(response) {
							resolve(response);
						})
						.catch(function(error) {
							reject(error);
						});
				})
				.catch(function(error) {
					reject(error);
				});
		});
	};

	EFLSubscribePackages.prototype.getDicePackagesByVideo = function(vodId, isClubEvent) {
		var _this = this;
		return new Promise(function (resolve) {
			EFL.DiceAuth.getTokenSilently()
				.then(function(tokens) {
					var API_HEADERS = EFL.DiceAuth.getAPIHeaders();
					API_HEADERS['Authorization'] = 'Bearer ' + tokens.access_token;

					_this.HTTP.get(_this.DICE_API_URL_BASE + (isClubEvent ? 'event/' : 'vod/') + vodId, {
						headers: API_HEADERS
					})
						.then(function(response) {
							resolve(response);
						})
						.catch(function(error) {
							console.error(error);
							resolve(null);
						});
				})
				.catch(function(error) {
					console.error(error);
					resolve(null);
				});
		});
	};

	/**
	 * Will update the `Subscribe now` CTA url with gift parameter if selected.
	 * Function copied from `components/video/video-packages.js`
	 */
	EFLSubscribePackages.prototype.setGiftPurchase = function (item) {
		var packagePanel = $(item).closest('.package-panel');
		//is it a match pass? Need to change the target as its different markup
		if ($(item).data("mp")) {
			packagePanel = $(item).closest('.purchase-info');
		}

		//Different method when its in the comparision table
		if ($(item).data("subtable")) {
			packagePanel = $(item).closest('.gift-control');
		}

		var ctaButton = $(packagePanel).find('a.btn-primary');

		//Different method when its in the comparision table
		if ($(item).data("subtable")) {
			ctaButton = $(".packages-comparison").find('a.btn-primary[data-sku="' + $(item).data("sku") + '"]');
		}

		var href = $(ctaButton).attr('href');

		var isChecked = $(packagePanel).find($('.gift-checkbox')).is(':checked');
		if (href != null) {
			if (isChecked) {
				href = href.replace('register=true', 'register=gift');
			} else {
				href = href.replace('register=gift', 'register=true');
			}
			$(ctaButton).attr('href', href);
		}
	};

	EFLSubscribePackages.prototype.audioMatchPassIsAvailable = function (matches) {
		if (!matches || !matches.length) {
			return false;
		}

		for (var i = 0; i < matches.length; i++) {
			if (matches[i].diceLicence
				&& matches[i].diceLicence.purchaseStrategy
				&& matches[i].diceLicence.purchaseStrategy.type
				&& matches[i].diceLicence.purchaseStrategy.type === "PPV"
				&& matches[i].diceLicence.name.toUpperCase().indexOf('AUDIO') >= 0) {
				return true;
			}
		}

		return false;
	};

	EFLSubscribePackages.prototype.matchPassIsAllowed = function (passPackage, audioPassIsAvailable) {
		if (!passPackage) {
			return false;
		}

		return passPackage.IsVideo || (passPackage.IsAudio && audioPassIsAvailable);
	};

	EFLSubscribePackages.prototype.handleError = function(error) {
		var tpl = $('script#subscribe-packages-error-tpl').first().text();
		var html = Sqrl.render(tpl, {});

		this.elements.$component.html(html);

		console.error(error);
	};

	EFLSubscribePackages.prototype.handleOverlayError = function(error) {
		var tpl = $('script#modal-packages-error-tpl').first().text();
		var html = Sqrl.render(tpl, {});

		$('[data-video-packages]').html(html);

		console.error(error);
	};

	// our instance holder
	// eslint-disable-next-line vars-on-top
	var instance;

	// an emulation of static variables and methods
	// eslint-disable-next-line vars-on-top
	var _static = {
		name: "EFLSubscribePackages",

		// Method for getting an instance. It returns
		// a singleton instance of a singleton object
		getInstance: function(options) {
			if ( instance === undefined ) {
				instance = new EFLSubscribePackages(options);
			}

			return instance;
		}
	};

	return _static;
}(window.jQuery, window.EFL));
;
window.EFL = window.EFL || {};

window.EFL.DiceVideoAccess = (function ($, EFL) {
	"use strict";

	function DiceVideoAccess() {}

	// TODO: Needs docs. It is an important method.
	// TODO: /accesscheck can get 2 prams vodId and eventId. The function structure is not flexible enough to support that. Should be refactored.
	DiceVideoAccess.prototype.getAccessLevels = function (ids, elementBuilder, isEvent) {
		var withToken = window.EFL.DiceAuth.withToken;
		var getDiceData;

		ids = _.uniq(_.compact(ids));

		if (EFL.DiceAuth.getUser()) {
			ids.forEach(function(id){
				elementBuilder(id).removeClass('freemium-video');
			});
		}

		getDiceData = function (tokens) {
			var accessCheckedIds = [];
			var API_HEADERS = EFL.DiceAuth.getAPIHeaders();
			API_HEADERS['Authorization'] = 'Bearer ' + tokens.access_token;

			var apiUrl = EFL.DiceAuth.API_URL_BASE + 'accesscheck?vodId=' + ids.join('&vodId=');

			if (isEvent) {
				apiUrl = EFL.DiceAuth.API_URL_BASE + 'accesscheck?eventId=' + ids.join('&eventId=');
			}

			window.EFL.HTTP.get(apiUrl, {
				headers: API_HEADERS
			})
				.then(function (response) {
					var data = response.data;
					if (data && data.accessLevels && data.accessLevels.length) {
						data.accessLevels.forEach(function (accessLevel) {
							if (accessLevel.accessLevel === 'GRANTED') {
								elementBuilder(accessLevel.contentId).removeClass('freemium-video');
								elementBuilder(accessLevel.contentId).removeClass('premium-video');
							} else if (accessLevel.accessLevel === 'DENIED') {
								elementBuilder(accessLevel.contentId).addClass('freemium-video');
							}

							accessCheckedIds.push(accessLevel.contentId.toString());
						});
					}

					_.pullAll(ids, accessCheckedIds).forEach(function(id){
						var $el = elementBuilder(id);

						if (!$el.hasClass('premium-video') && !$el.hasClass('freemium-video')) {
							$el.addClass('locked-video');

							// if no access to replays then remove video indicator
							if ($el.data('replay-vod')) {
								$('body').removeClass('video-available');
							}
						}
					});
				})
				.catch(function (error) {
					_.pullAll(ids, accessCheckedIds).forEach(function(id){
						var $el = elementBuilder(id);

						if (!$el.hasClass('premium-video') && !$el.hasClass('freemium-video')) {
							$el.addClass('locked-video');
						}
					});

					console.error(error);
					// fail silently and do not show any access labels
				});
		};

		withToken(getDiceData)();
	};

	DiceVideoAccess.prototype.getIds = function ($htmlBlock) {
		var ids = [];
		var eventIds = [];

		$htmlBlock.map(function() {
			// eslint-disable-next-line no-invalid-this
			var $currentEl = $(this);
			var isEvent = $currentEl.data('playvideo-clubevent');
			var isReplay = $currentEl.data('replay-vod');

			if (isEvent) {
				eventIds.push($currentEl.attr('data-playvideo-id'));
			} else if (isReplay) {
				ids.push($currentEl.attr('data-replay-vod'));
			} else {
				ids.push($currentEl.attr('data-playvideo-id'));
			}
		});

		return {
			ids: ids,
			eventIds: eventIds
		};
	};

	DiceVideoAccess.prototype.getLiveIds = function ($htmlBlock) {
		var ids = [];

		$htmlBlock.map(function() {
			// eslint-disable-next-line no-invalid-this
			ids.push($(this).attr('data-playvideo-id-live'));
		});

		return ids;
	};

	// TODO: Add a description.
	// TODO: This way of searching for videos is error prone, and could lead to duplicates.
	// It should be refactored into 1 function looking for 1 selector.
	DiceVideoAccess.prototype.loadThumbnailIndicators = function () {
		//Not sure why this should be either .video.article or .video-splash. As far as I can see, it can be a combo so I've combined them
		var allIDs = this.getIds($('.video.article,.video-splash'));

		//The following "var" and "if" are probably not needed anymore
		var videoSelector = '.video.article';

		if (!allIDs.ids.length) {
			// News article videos
			// TODO: It think this selector should be more specific.
			allIDs = this.getIds($('.video-splash'));
			videoSelector = '.video-splash';
		}

		if (allIDs.ids.length > 0) {
			this.getAccessLevels(allIDs.ids, function (id) {
				return $('.video.article[data-playvideo-id="' + id + '"],.video-splash[data-playvideo-id="' + id + '"]');
			});
		}

		//This should be the same as above so I've amended the same way
		allIDs = this.getIds($('.video.article,.video-splash'));
		videoSelector = '.video.article';

		if (!allIDs.eventIds.length) {
			// News article videos
			// TODO: It think this selector should be more specific.
			allIDs = this.getIds($('.video-splash'));
			videoSelector = '.video-splash';
		}

		if (allIDs.eventIds.length > 0) {
			this.getAccessLevels(allIDs.eventIds, function (id) {
				//return $(videoSelector + '[data-playvideo-id="' + id + '"]');
				return $('.video.article[data-playvideo-id="' + id + '"],.video-splash[data-playvideo-id="' + id + '"]');
			}, true);
		}

	};

	// Checks permission access for all videos displayed in a `article-grid-container`
	// TODO: Add a description
	DiceVideoAccess.prototype.loadThumbnailIndicatorsInElement = function ($newHtmlBlock) {
		var allIDs = this.getIds($newHtmlBlock.find('.video.article'));

		if (allIDs.ids.length > 0) {
			this.getAccessLevels(allIDs.ids, function (id) {
				return $('.video.article[data-playvideo-id="' + id + '"]');
			});
		}

		if (allIDs.eventIds.length > 0) {
			this.getAccessLevels(allIDs.eventIds, function (id) {
				return $('.video.article[data-playvideo-id="' + id + '"]');
			}, true);
		}
	};

	// TODO: This needs more info to what it does and what are the use cases.
	DiceVideoAccess.prototype.loadVideoPlayerIndicators = function () {
		//Page ID isn't a DICE ID so can't see what this would be doing hence disabling the function
		return;
		var pageid = $('body').attr('data-pageid');

		if (!pageid) {
			return;
		}

		this.getAccessLevels([pageid], function (id) {
			return $('.video-splash[data-playvideo-id="' + id + '"]');
		});
	};

	DiceVideoAccess.prototype.loadMatchCenterIndicators = function (matchedEvents) {
		var ids = this.getLiveIds($('.live-bar .live-button a'));

		if (!ids.length) {
			return;
		}

		matchedEvents.forEach(function (event) {
			if (ids.indexOf(matchedEvents[0].id.toString()) > -1) {
				var $el = $('.live-bar .live-button a[data-playvideo-id-live="' + event.id + '"]');

				if (event.accessLevel === 'GRANTED') {
					$el.removeClass('freemium-video');
					$el.removeClass('premium-video');
				} else if (event.accessLevel === 'DENIED') {
					$el.addClass('freemium-video');
				}
			}
		});
	};

	DiceVideoAccess.prototype.loadMatchCenterReplayIndicators = function () {
		var allIDs = this.getIds($('[data-replay-vod]'));
		var videoSelector = '.play-video > a';

		if (allIDs.ids.length > 0) {
			this.getAccessLevels(allIDs.ids, function (id) {
				return $(videoSelector + '[data-replay-vod="' + id + '"]');
			}, false);
		}

		//efl-1758 autoplay replays
		if (window.location.search.indexOf('autoplayvideo=true') > -1) {
			window.addEventListener('load', function () {
				$('.video-available .play-video a[data-playvideo-id][data-replay-vod][data-playvideo-live]').first().click();
			});
		}
	};

	return new DiceVideoAccess();

}(window.jQuery, window.EFL));
;
/* eslint-disable vars-on-top */
/* global isMobile */
window.EFL = window.EFL || {};
window.EFL.EFLVideoTrigger = (function($, EFL) {
	'use strict';

	function EFLVideoTrigger() {
		// TODO: This needs a refactor to use `this.classes` instead of manually concatenate "."
		this.settings = {
			videoTriggerDataId: 'playvideo-id',
			videoTriggerDataLiveId: 'playvideo-id-live',
			videoTriggerSelector: '[data-playvideo-id]:not(.locked-video):not([data-full-match-replay])',
			videoLockedSelector: '[data-playvideo-id].locked-video',

			videoReplayId: 'replay-vod',

			isLive: 'playvideo-live',
			isDisabled:'playback-disabled',
			isMatchAudio: 'match-audio',
			isMatchVideo: 'match-video',
			isAudio: 'playvideo-audio',
			isVideo: 'playvideo-video',
			isClubEvent: 'playvideo-clubevent',

			freemiumAccessClass: 'freemium-video',
			premiumAccessClass: 'premium-video',

			area: 'playvideo-area',
			areaClass: 'video-area',
			playerClass: 'video-player',
			playingClass: 'video-playing',
			playedClass: 'video-played',
			containerClass: 'video-container',
			metadataClass: 'video-metadata',

			dicePlayerId: 'dice-video-player',

			scrollToPlayer: true,
			scrollOffset: 100,
			scrollSpeed: 300
		};

		this.elements = {
			$videoContainer: $('.video-container'),
		};

		this.classes = {

		};

		this.currentVideo = '';
		this.HTTP = EFL.HTTP;
		this.DICE_API_URL_BASE = EFL.Dice.apiUrl + '/api/v2/';
	}

	EFLVideoTrigger.prototype.init = function() {
		var _this = this;
		var $videoTriggers = $(this.settings.videoTriggerSelector);

		if ($videoTriggers.length) {
			// If first video player is empty then populate it
			$videoTriggers.each(function () {
				// eslint-disable-next-line no-invalid-this
				if ($(this).attr('data-' + _this.settings.videoTriggerDataId) === '' && $(this).height()) {
					// eslint-disable-next-line no-invalid-this
					_this._findVideoContent(this);
				}
				else {
					var isLive = $(this).attr('data-' + _this.settings.isLive) || false;
					var videoId = $(this).data(_this.settings.videoTriggerDataId);

					if (!isLive && videoId) {
						var $area = $(this).closest("." + _this.settings.areaClass).length ? $(this).closest("." + _this.settings.areaClass) : $('.master-player').first();
						var $container = $area.closest('.' + _this.settings.containerClass);
						var $metadata = $container.find('.' + _this.settings.metadataClass);
						_this._renderMetadata(videoId, $metadata);

					}
				}
			});
		}

		this.initEvents();
	};

	EFLVideoTrigger.prototype.initEvents = function() {
		// This ensures that all future elements that are added dynamicaly will have this event handler attached.
		// This should work if we use `$(document)` but for some reason the propagation is not prevented using the return false
		// at the end, to make it work, `body` seems to do the trick.
		$('body').on('click', this.settings.videoTriggerSelector, this.initVideoPlayback.bind(this));
		// If the video is locked (.locked-video) video playback will not run but bootstrap collapse will, this prevents that.
		$('body').on('click', this.settings.videoLockedSelector, function() { return false; });
	};

	// TODO: Using Lyon legacy code. It should be refactored when time is at hand.
	// eslint-disable-next-line complexity
	EFLVideoTrigger.prototype.initVideoPlayback = function(event) {
		event.preventDefault();

		var $area;
		var $element = $(event.currentTarget);
		var videoId = $element.data(this.settings.videoTriggerDataId);
		var videoLiveId = $element.data(this.settings.videoTriggerDataLiveId);
		var playerVideoId;
		var isLive = ($element.attr('data-' + this.settings.isLive) || false);
		var isAudio = ($element.attr('data-' + this.settings.isAudio) || false);
		var isMatchAudio = ($element.attr('data-' + this.settings.isMatchAudio) || false);
		var isMatchVideo = ($element.attr('data-' + this.settings.isMatchVideo) || false);
		var isClubEvent = ($element.attr('data-' + this.settings.isClubEvent) || false);
		var isDisabled = ($element.attr('data-' + this.settings.isDisabled) || false);
		var videoImage = $element.find('img').first().attr('src');
		var replayVodId = $element.attr('data-' + this.settings.videoReplayId);

		isLive = (isLive === true || isLive === 'true') ? true : false;
		isAudio = (isAudio === true || isAudio === 'true' || isAudio === 'away' || isAudio === 'home') ? true : false;
		isClubEvent = (isClubEvent === true || isClubEvent === 'true') ? true : false;

		if (isDisabled) {
			return false;
		}

		EFL.video.isMatchAudio = isMatchAudio;
		EFL.video.isMatchVideo = isMatchVideo;

		var isFreemium = $element.hasClass(this.settings.freemiumAccessClass);
		var isPremium = $element.hasClass(this.settings.premiumAccessClass);

		if (!isFreemium && !isPremium) {
			var stopAtFirstPlayer = false;

			// If there is a specific player
			if ($('.master-player').length && !$element.closest('.master-player').length) {
				$area = $('.master-player').first();
				stopAtFirstPlayer = true;
			} else if ($element.data(this.settings.area)) {
				$area = $($element.data(this.settings.area)); // If a video area is specified then use that
			} else if ($element.closest('.' + this.settings.areaClass).length) {
				$area = $element.closest('.' + this.settings.areaClass); // Else see if there's a parent we can use
			} else if (isLive) {
				stopAtFirstPlayer = true;
			} else if ($element.parents('.video-archive-container').length > 0) {
				var $archiveContainer = $element.parents('.video-archive-container');
				$area = $archiveContainer.find('.' + this.settings.areaClass);
			} else {
				$area = $('.' + this.settings.areaClass).first(); // Otherwise find the first player instance on the page
				stopAtFirstPlayer = ($('.' + this.settings.areaClass).length > 1);
			}
			var $player = $area.find('.' + this.settings.playerClass);
			var $container = $area.closest('.' + this.settings.containerClass);
			var $metadata = $container.find('.' + this.settings.metadataClass);
			var $otherLinks = $('[data-' + this.settings.videoTriggerDataId + '="' + videoId + '"]');

			$('.audio-player').removeClass('audio-player');
			if (isAudio) {
				$area.addClass('audio-player');
				$area.find('.' + this.settings.areaClass).addClass('audio-player');
			}
			if (isAudio && (isMobile.apple.device || this._browserDetect().isSafari() || this._browserDetect().isEdge())) {
				$('.cookies-warning').removeClass('hidden');
			} else {
				$('.cookies-warning').addClass('hidden');
			}

			// If the video player is an accordion, ensure we open it
			if (!$element.hasClass(this.settings.premiumAccessClass) && !stopAtFirstPlayer) {
				if (!$element.hasClass(this.settings.premiumAccessClass)) {
					if ($area.hasClass('.collapse')) {
						$area.collapse();
					} else if ($area.closest('.collapse').length) {
						$area.closest('.collapse').collapse();
					}
				}
			}

			// If the video player is on match centre and we're using first player, it will be hidden, so needs to be opened
			if (!$element.hasClass(this.settings.premiumAccessClass) && (stopAtFirstPlayer && $('body').hasClass('match-centre'))) {
				if (!$element.hasClass(this.settings.premiumAccessClass)) {
					if ($area.hasClass('.collapse')) {
						$area.collapse();
					} else if ($area.closest('.collapse').length) {
						$area.closest('.collapse').collapse();
					}
				}
			}

			// TODO: Lion legacy: A dirty delay in case the player is hidden in an accordion
			setTimeout(function () {
				var currentVideoUniqueId = String(videoLiveId).concat(videoId);
				if (currentVideoUniqueId !== this.currentVideo) {
					// Create a player instance if it doesn't already exist
					if (!$('#' + this.settings.dicePlayerId).length) {
						$player.append('<div id="' + this.settings.dicePlayerId + '" data-matchparentdimensions></div>');
					} else {
						// Move the player to the closest and most appropriate spot to play the video
						$('#' + this.settings.dicePlayerId).appendTo($player);
					}

					// TODO: I don't know what this does.
					// $('#' + this.settings.dicePlayerId).css('visibility', 'hidden');
					// This is to run matchparentdimensions
					$(window).trigger('throttled-resize');

					// Set appropriate CSS class
					$('.' + this.settings.playingClass).removeClass(this.settings.playingClass);
					$area.addClass(this.settings.playingClass);
					$area.find('.' + this.settings.areaClass).addClass(this.settings.playingClass);
					$element.addClass(this.settings.playingClass);
					$otherLinks.addClass(this.settings.playingClass);
					$area.addClass(this.settings.playedClass);
					$area.find('.' + this.settings.areaClass).addClass(this.settings.playedClass);
					$element.addClass(this.settings.playedClass);
					$otherLinks.addClass(this.settings.playedClass);

					if (!isLive) {
						var $tempPlayer = $area.find('.video-splash').first();
						this._renderMetadata(videoId, $metadata);
						this._setBackgroundImage($tempPlayer, videoImage); // Extract the thumbnail image
						this._setVideoClass($tempPlayer, $element);
						$tempPlayer.data(this.settings.videoTriggerDataId, videoId);
					}

					// TODO: I don't know why this might be needed. Disabling for now
					// setTimeout(function () {
					// Player init code.
					// }.bind(this), 1000);

					this.currentVideo = currentVideoUniqueId;
					playerVideoId = videoId;

					if (isLive && videoLiveId) {
						playerVideoId = videoLiveId;
					} else if (replayVodId) {
						//if replay set and event not live then play replay
						playerVideoId = replayVodId;
						isLive = false;
					}

					EFL.DiceVideoPlayer.initPlayer(playerVideoId, isLive, isAudio, this.settings.dicePlayerId);
				}

				if (!$element.hasClass(this.settings.premiumAccessClass)) {
					// Scroll the player into view
					if (this.settings.scrollToPlayer && !this._elementInViewport($area)) {
						$('html, body').animate({
							scrollTop: this.elements.$videoContainer.offset().top - this.settings.scrollOffset
						}, this.settings.scrollSpeed);
					}
				}
			}.bind(this), 100);

			// This also prevents Bootstrap collapse plugin to execut if it returns false.
			// `return false` here basically is the same as calling `e.preventDefault` and `e.stopPropagation`.
			if ($element.hasClass(this.settings.premiumAccessClass) || stopAtFirstPlayer) {
				return false;
			}
		} else {
			playerVideoId = videoId;

			if (isLive && videoLiveId) {
				playerVideoId = videoLiveId;
				isPremium = true; // event don't show register button
			} else if (replayVodId) {
				//if replay set and event not live then play replay
				playerVideoId = replayVodId;
				isLive = false;
				isPremium = true; // event don't show register button
			}

			this._populateModal(playerVideoId, isPremium, isLive);
			this._displayPackagesModal();

			return false;
		}
	};

	EFLVideoTrigger.prototype._renderMetadata = function(videoId, location) {
		$(location).empty();
		var metadataTpl = '<div><h3>{{it.vodData.title}}</h3><p>{{it.vodData.description}}</p><div class="social-sharing"><div class="addthis_inline_share_toolbox" data-title="{{it.vodData.title}}" data-url="{{it.socialUrl}}" data-image="{{it.vodData.coverUrl}}" style="clear: both;"></div></div>';

		this._getVodById(videoId).then(function(data) {
			var html = Sqrl.render(metadataTpl, {
				vodData: data.data,
				videoId: videoId,
				socialUrl: window.location.origin + "/ifollow/video-archive/?video=" + videoId
			});

			$(location).html(html);
			//Need to reinitialise sharing
			if (typeof addthis !== 'undefined' && addthis.layers && addthis.layers.refresh) {
				addthis.layers.refresh();
			}
		});
	};

	EFLVideoTrigger.prototype._elementInViewport = function(element) {
		var $element = $(element);

		var viewportTop = $(window).scrollTop();
		var viewportBottom = viewportTop + $(window).height();

		var elementTop = $element.offset().top;
		var elementBottom = elementTop + $element.height();

		return ((elementBottom <= viewportBottom) && (elementTop >= viewportTop));
	};

	EFLVideoTrigger.prototype._setBackgroundImage = function($player, imgUrl) {
		if (!$player || !imgUrl) {
			return false;
		}

		imgUrl = imgUrl.replace('_es.jpg', '_eh.jpg'); // Convert to "hero" size
		imgUrl = imgUrl.replace('_eb.jpg', '_eh.jpg');

		$player.css('background-image', 'url(' + imgUrl + ')');
	};

	EFLVideoTrigger.prototype._setVideoClass = function($player, $target) {
		if (!$player || !$target) {
			return false;
		}

		if ($target.hasClass(this.settings.freemiumAccessClass)) {
			$player.addClass(this.settings.freemiumAccessClass);
		} else {
			$player.removeClass(this.settings.freemiumAccessClass);
		}

		if ($target.hasClass(this.settings.premiumAccessClass)) {
			$player.addClass(this.settings.premiumAccessClass);
		} else {
			$player.removeClass(this.settings.premiumAccessClass);
		}

		if ($target.hasClass(this.settings.eventNotLiveClass)) {
			$player.addClass(this.settings.eventNotLiveClass);
		} else {
			$player.removeClass(this.settings.eventNotLiveClass);
		}
	};

	EFLVideoTrigger.prototype._findVideoContent = function (player) {
		var $player = $(player);
		var videos = $('.article[data-' + this.settings.videoTriggerDataId + '][data-' + this.settings.videoTriggerDataId + ' != ""]')
			.not('.video-match')
			.not('.premium-match');

		if (videos.length) {
			var $target = videos.first();
			var videoId = $target.attr('data-' + this.settings.videoTriggerDataId); // Extract the video ID

			// Find where to put the metadata
			var $container = $player.closest('.' + this.settings.containerClass);
			var $metadata = $container.find('.' + this.settings.metadataClass);

			this._renderMetadata(videoId, $metadata);
			this._setBackgroundImage($player, $target.find('img').first().attr('src')); // Extract the thumbnail image
			this._setVideoClass($player, $target);

			$player.attr("data-" + this.settings.videoTriggerDataId, videoId);
			$player.data(this.settings.videoTriggerDataId, videoId);
		}
	};

	EFLVideoTrigger.prototype._displayPackagesModal = function() {
		if ($('.packages-modal').length) {
			$('.packages-modal').appendTo('body'); // Move to modal to end of the DOM so that it doesn't inherit any parent mark-up styles

			if (EFL.DiceAuth.getUser()) {
				$('#packages-modal').html($('#packages-modal-loggedin').html()).modal();
			} else {
				$('#packages-modal').html($('#packages-modal-loggedout').html()).modal();
			}

		} else { // fallback for if the modal is missing for some reason
			if (EFL.DiceAuth.getUser()) {
				alert('You are logged in but do not have access to view that content');
			} else {
				alert('You must be logged in to view that content');
			}
		}
	};

	EFLVideoTrigger.prototype._populateModal = function(videoId, isPremium, isLive) {
		EFL.EFLSubscribePackages.getInstance().initSubscribeOverlay(videoId, isPremium, isLive);
	};

	EFLVideoTrigger.prototype._getVodById = function(vodId) {
		var _this = this;
		return new Promise(function (resolve, reject) {
			EFL.DiceAuth.getTokenSilently()
				.then(function(tokens) {
					var API_HEADERS = EFL.DiceAuth.getAPIHeaders();
					API_HEADERS['Authorization'] = 'Bearer ' + tokens.access_token;

					_this.HTTP.get(_this.DICE_API_URL_BASE + 'vod/' + vodId, {
						headers: API_HEADERS
					})
						.then(function(response) {
							resolve(response);
						})
						.catch(function(error) {
							console.error(error);
							reject(error)
						});
				})
				.catch(function(error) {
					console.error(error);
					reject(error)
				});
		});
	};

	EFLVideoTrigger.prototype._browserDetect = function() {
		return {
			isSafari: function () {
				var isDesktopSafari =
					!!navigator.platform &&
					/MacIntel/.test(navigator.platform) &&
					!!navigator.userAgent &&
					/Safari/.test(navigator.userAgent) &&
					!/Chrome/.test(navigator.userAgent);
				return isDesktopSafari;
			},
			isEdge: function () {
				return (window.navigator.userAgent.indexOf('Edge') > -1);
			}
		};
	};

	// our instance holder
	// eslint-disable-next-line vars-on-top
	var instance;

	// an emulation of static variables and methods
	// eslint-disable-next-line vars-on-top
	var _static = {
		name: "EFLVideoTrigger",

		// Method for getting an instance. It returns
		// a singleton instance of a singleton object
		getInstance: function(options) {
			if ( instance === undefined ) {
				instance = new EFLVideoTrigger(options);
			}

			return instance;
		}
	};

	return _static;
}(window.jQuery, window.EFL));
;
/* eslint-disable vars-on-top */

window.EFL = window.EFL || {};
window.EFL.EFLVideoLive = (function ($, EFL) {
	'use strict';

	var getUnixTimeInMs = EFL.DateUtils.getUnixTimeInMs;
	var compareAsc = EFL.DateUtils.compareAsc;
	var subHours = EFL.DateUtils.subHours;
	var addMinutes = EFL.DateUtils.addMinutes;
	var startOfDay = EFL.DateUtils.startOfDay;
	var endOfDay = EFL.DateUtils.endOfDay;

	function EFLVideoLive() {
		this.settings = {
			videoaccessCheckId: $('[data-videoaccess-check]').data('videoaccess-check'),
			eventIds: $('[data-event-list]').data('event-list'),
			$subscribeLinksContainer: $('.subscribe-links')
		};

		this.classes = {
			nextMatchDateElement: 'input#ko'
		};

		this.nextMatchDateVal = $(this.classes.nextMatchDateElement).val();
		this.ko = new Date(parseInt(this.nextMatchDateVal, 10));
		this.liveEventsTimeoutID;
		this.eventIdArray = (this.settings.eventIds ? this.settings.eventIds : []);
	}

	EFLVideoLive.prototype.init = function () {
		var _this = this;
		if (_this.eventIdArray.length > 0) {
			_this.getEventDataFromMailmanIds(_this.eventIdArray)
				.then(function (eventData) {

					// remove nulls from list
					var eventData = eventData.filter(function (el) {
						return el != null;
					});

					if (eventData.length) {
						// use mailman data

						var now = new Date();
						if (_this.nextMatchDateVal) {
							var dateOffset = compareAsc(now, subHours(_this.ko, 1));

							// format event data into same format as api data for helper functions
							var data = {};
							var data2 = data['data'] = {}
							data2['events'] = eventData;

							// return mailman data in a promise so we can promise chain below
							var mailmanData = new Promise(function (resolve) {
								if (dateOffset === -1) {
									resolve(data)
								}
								else {
									resolve(data2)
								}
							});

							// Before match (-1 hour ahead)
							if (dateOffset === -1) {
								mailmanData.then(_this.parseAvailablePurchases.bind(_this))
									.then(_this.initUpcomingEvents.bind(_this))
									.catch(function (error) {
										console.error(error);
									});
							} else {
								mailmanData.then(_this.parseAvailableLiveEvents.bind(_this))
									.then(_this.initLiveEvents.bind(_this))
									.catch(function (error) {
										console.error(error);
									});
							}
						}


					} else {
						// error getting mailman data use api
						_this.initApi();
					}
				}).catch(function (error) {
					// error somewhere try again with api data
					console.error(error)
					_this.initApi();
				})
		} else {
			_this.initApi();
		}
	};

	EFLVideoLive.prototype.initApi = function () {
		var _this = this;
		var now = new Date();

		if (this.nextMatchDateVal) {
			var dateOffset = compareAsc(now, subHours(this.ko, 1));

			// Before match (-1 hour ahead)
			if (dateOffset === -1) {
				EFL.EFLDiceLib.getUpcomingMatches({
					startDateFrom: getUnixTimeInMs(startOfDay(this.ko)),
					startDateUntil: getUnixTimeInMs(endOfDay(this.ko))
				})
					.then(this.parseAvailablePurchases.bind(_this))
					.then(this.initUpcomingEvents.bind(_this))
					.catch(function (error) {
						console.error(error);
					});
			} else {
				EFL.EFLDiceLib.getLiveEvents(this.ko)
					.then(this.parseAvailableLiveEvents.bind(_this))
					.then(this.initLiveEvents.bind(_this))
					.catch(function (error) {
						console.error(error);
					});
			}
		}
	};

	EFLVideoLive.prototype.parseAvailablePurchases = function (data) {
		var videoaccessCheckId = this.settings.videoaccessCheckId.toString();
		var availablePurchases = [];
		var passesData = [];
		var grantedAccess = false;
		var bodyClasses = [];
		var videoEventGrantedAccess = false;

		var videoPassesAvailable = false;

		data = data.data;

		_.forEach(data.events, function(obj) {
			var externalId = obj.externalId.toString();


			if (externalId && (externalId === videoaccessCheckId || externalId === 'g' + videoaccessCheckId)) {
				if (obj.accessLevel === 'GRANTED' && !grantedAccess) {
					grantedAccess = true;
				}

				if (obj.accessLevel === 'GRANTED' && !videoEventGrantedAccess && !obj.audioOnly) {
					videoEventGrantedAccess = true;
				}

				availablePurchases = availablePurchases.concat(obj.availablePurchases);

				//Shouldn't need a return value
				//return true;
			}

			//returning false prompts early exit from foreach
			//return false;
		});

		if (grantedAccess) {
			bodyClasses.push('passes-available');
		}

		if (!videoEventGrantedAccess && availablePurchases.length) {
			if (!grantedAccess) bodyClasses.push('passes-available');

			availablePurchases.forEach(function (item) {
				var defaultAmountIdx = 0;
				// This retrieves the `amount` object from Dice API response that is best suited to be used.
				// The API returns amounts as an Array and we need to select only 1 item from it, based on
				// the following criteria:
				// IF there is one item with `obj.local: true` then we chose that one.
				// ELSE we chose the one that has `obj.default: true`.
				var amountObj = _.find(item.amounts, function(o) {
					if (o.default) {
						defaultAmountIdx++;
					}
					return o.local;
				});
				item.computedAmount = amountObj ? amountObj : item.amounts[defaultAmountIdx];


				//var replaceIfExist = function (cssclass, data) {
				//	var exist = _.findIndex(passesData, { class: data.class })
				//	//replace audio with video if it exists
				//	if (exist === -1) {
				//		bodyClasses.push(cssclass);
				//		passesData.push(data);
				//	} else if (data.label.toUpperCase().indexOf('VIDEO') >= 0) {
				//		passesData[exist] = data;
				//	}
				//}

				// If any type of video pass exists no audio passes should be shown
				var replaceIfExist = function (cssclass, data) {
					if (videoPassesAvailable) {
						//video available - only add new video passes
						if (data.label.toUpperCase().indexOf('VIDEO') >= 0) {
							bodyClasses.push(cssclass);
							passesData.push(data);
						}
					} else {
						//if this is first video pass we've seen then clear list and only add new video passes
						if (data.label.toUpperCase().indexOf('VIDEO') >= 0) {
							videoPassesAvailable = true;
							passesData = [];
							bodyClasses = [];
							bodyClasses.push('passes-available')
						}
						bodyClasses.push(cssclass);
						passesData.push(data);
					}
				}

				

				switch (item.purchaseStrategy.type.toUpperCase()) {
					case 'NONE':
						bodyClasses.push('free-pass-available');
						break;
					case 'PPV':
						replaceIfExist('match-pass-available', { class: 'match-pass', sku: item.computedAmount.sku, label: item.name, variant: EFL.EFLDiceLib.getEventString(data.events, item.computedAmount.sku) })
						//bodyClasses.push('match-pass-available');
						//passesData.push({ class: 'match-pass', sku: item.computedAmount.sku, label: item.name });
						break;
					default:
						break;
				}

				if (item.purchaseStrategy.type.toUpperCase() === 'SUBSCRIPTION' && item.purchaseStrategy.subscriptionPeriod) {
					switch (item.purchaseStrategy.subscriptionPeriod.toUpperCase()) {
						case 'P1Y':
							replaceIfExist('season-pass-available', { class: 'season-pass', sku: item.computedAmount.sku, label: item.name })
							//bodyClasses.push('season-pass-available');
							//passesData.push({ class: 'season-pass', sku: item.computedAmount.sku, label: item.name });
							break;
						case 'P1M':
							replaceIfExist('month-pass-available', { class: 'month-pass', sku: item.computedAmount.sku, label: item.name })
							//bodyClasses.push('month-pass-available');
							//passesData.push({ class: 'month-pass', sku: item.computedAmount.sku, label: item.name });
							break;
						case 'P7D':
							replaceIfExist('week-pass-available', { class: 'week-pass', sku: item.computedAmount.sku, label: item.name })
							//bodyClasses.push('week-pass-available');
							//passesData.push({ class: 'week-pass', sku: item.computedAmount.sku, label: item.name });
							break;
						case 'P1D':
							replaceIfExist('day-pass-available', { class: 'day-pass', sku: item.computedAmount.sku, label: item.name })
							//bodyClasses.push('day-pass-available');
							//passesData.push({ class: 'day-pass', sku: item.computedAmount.sku, label: item.name });
							break;
						default:
							break;
					}
				}
			});
		}

		return {
			passesData: passesData,
			bodyClasses: bodyClasses,
			eventData: data.events
		};
	};

	EFLVideoLive.prototype.initUpcomingEvents = function(result) {
		var _this = this;

		if (!result.passesData.length) {
			EFL.EFLDiceLib.getLiveEvents(this.ko)
				.then(function (data) {
					if (result.eventData != null && result.eventData.length && data.events != null) {
						data.events = data.events.concat(result.eventData);
					}
					return data;
				})
				.then(this.parseAvailableLiveEvents.bind(_this))
				.then(function(data) {
					if (data.matchedEvents.length === 0) {
						_this.renderUpcomingEvents(result);
					} else {
						_this.renderLiveEvents(data);
					}
				});
		} else {
			_this.renderUpcomingEvents(result);
		}
	};

	EFLVideoLive.prototype.renderUpcomingEvents = function(result) {
		if (result.bodyClasses.length) {
			$('body').addClass(result.bodyClasses.join(' '));
		}

		var exclude = false;

		window.EFL.video.suppressedLinkFixtures.forEach(function (x) {
			result.passesData.forEach(function (y) {
				if (y.variant && y.variant.includes(x)) {
					exclude = true;
				}
			})
		})

		if (result.passesData.length && !exclude) {
			this.renderPassesBar(result.passesData);
		} else {
			this.settings.$subscribeLinksContainer.removeClass('hidden');
		}
	};

	EFLVideoLive.prototype.renderPassesBar = function(passesData) {
		var passButtonsTpl =
		'{{@each(it.data) => item, index}}' +
		'<div class="pass-button {{item.class}}">' +
			'<span aria-hidden="true" class="icon icon-Ticket"></span>' +
			'<a href="/my-account/?register=true&amp;sku={{item.sku}}&amp;redirect=' + window.location.href + '" {{@if(item.variant)}}data-variant="{{item.variant}}"{{/if}}>' +
			'Buy {{@if(/[aeiou]/.test(item.label.split("")[0].toLowerCase()))}}an{{#else}}a{{/if}} {{item.label}}' +
			'</a>' +
		'</div>' +
		'{{/each}}';

		var html = Sqrl.render(passButtonsTpl, { data: _.uniqBy(passesData, 'sku') });

		this.settings.$subscribeLinksContainer.removeClass('hidden');
		this.settings.$subscribeLinksContainer.html(html);

		$("a[data-variant]").on('click', function (e) {
			// when purchasing a match pass store the variant in local storage to add to analytics later.
			var cacheKey = 'EFLMatchPassVariant'// + $(this).data('sku');
			window.localStorage.setItem(cacheKey, JSON.stringify({ variant: $(this).data('variant'), expiry: Date.now() + 600000 }));
			return true;
		});
	};

	EFLVideoLive.prototype.getEvent = function (eventId) {
		var withToken = window.EFL.DiceAuth.withToken;
		var getEvent;

		getEvent = function (resolve, reject, tokens) {
			var API_HEADERS = EFL.DiceAuth.getAPIHeaders();
			API_HEADERS['Authorization'] = 'Bearer ' + tokens.access_token;

			var apiUrl =
				EFL.DiceAuth.API_URL_BASE + 'event/' + eventId

			window.EFL.HTTP.get(apiUrl, {
				headers: API_HEADERS
			})
				.then(function (response) {
					resolve(response.data);
				})
				.catch(function (error) {
					reject(error);
				});
		};

		return new Promise(function (resolve, reject) {
			withToken(getEvent)(resolve, reject);
		});
	};

	EFLVideoLive.prototype.getEventDataFromMailmanIds = function (eventIdArray) {
		var _this = this;
		var matchedEvents = [];

		return new Promise(function (resolve, reject) {

			if (eventIdArray.length) {
				// mail man event ids are on the page
				var eventLookups = [];
				eventIdArray.forEach(function (eventId) {
					eventLookups.push(_this.getEvent(eventId));
				});

				// Promise.all will break if the access check returns 404 (user does not have access to content)
				// Below function will just return a list of events that have data.
				function allSkippingErrors(promises) {
					return Promise.all(
						//promises.map(p => p.catch(error => null))
						promises.map(function (p) {
							return p.catch(function (error) { return null })
						})
					);
				}

				resolve(allSkippingErrors(eventLookups));
			}
			else {
				resolve(matchedEvents);
			}

		});
	};
	
	EFLVideoLive.prototype.parseAvailableLiveEvents = function(data) {
		var _this = this;
		var videoaccessCheckId = this.settings.videoaccessCheckId.toString();

		var matchedEvents = _.filter(data.events, function (obj) {
			if (obj.externalId) {
				var externalId = obj.externalId.toString();

				if (obj.live && externalId && (externalId === videoaccessCheckId || externalId === 'g' + videoaccessCheckId)) {
					return true;
				}
			}

			return false;
		});

		
		var bodyClasses = ['live-available'];

		if (matchedEvents.length) {
			matchedEvents.forEach(function (item) {
				if (item.audioOnly) {
					bodyClasses.push('audio-available');
					// TODO: Side effect, should be moved downstream.
					$('.live-bar .play-audio a[data-playvideo-id], .widget-match-header-mini .play-audio a[data-playvideo-id]').attr('data-playvideo-id-live', item.id);
				} else {
					bodyClasses.push('video-available');
					// TODO: Side effect, should be moved downstream.
					$('.live-bar .play-video a[data-playvideo-id], .widget-match-header-mini .play-video a[data-playvideo-id]').attr('data-playvideo-id-live', item.id);
				}
			});
		}
		else {
			//no event, check for replay
			var replayVodId = $('[data-replay-vod]').data('replay-vod');
			if (replayVodId) {
				$('body').addClass('video-available');
			}
			EFL.DiceVideoAccess.loadMatchCenterReplayIndicators();

		}

		return {
			bodyClasses: bodyClasses,
			matchedEvents: matchedEvents
		};
	};

	EFLVideoLive.prototype.initLiveEvents = function(result) {
		var _this = this;

		if (result.matchedEvents.length === 0) {
			EFL.EFLDiceLib.getUpcomingMatches({
				startDateFrom: getUnixTimeInMs(startOfDay(this.ko)),
				startDateUntil: getUnixTimeInMs(endOfDay(this.ko))
			})
				.then(this.parseAvailablePurchases.bind(_this))
				.then(function(data) {
					if (data.passesData.length === 0) {
						_this.liveEventsTimeoutID = setTimeout(function() {
							EFL.EFLDiceLib.getLiveEvents(this.ko)
								.then(this.parseAvailableLiveEvents.bind(_this))
								.then(this.initLiveEvents.bind(_this))
								.catch(function(error) {
									console.error(error);
								});
						}.bind(_this), 5 * 60000);
					} else {
						_this.renderUpcomingEvents(data);
					}
				});
		} else {
			clearTimeout(this.liveEventsTimeoutID);
			this.renderLiveEvents(result);
		}
	};

	EFLVideoLive.prototype.renderLiveEvents = function(result) {
		$('body').addClass(result.bodyClasses.join(' '));
		EFL.DiceVideoAccess.loadMatchCenterIndicators(result.matchedEvents);
	};

	// our instance holder
	// eslint-disable-next-line vars-on-top
	var instance;

	// an emulation of static variables and methods
	// eslint-disable-next-line vars-on-top
	var _static = {
		name: "EFLVideoLive",

		// Method for getting an instance. It returns
		// a singleton instance of a singleton object
		getInstance: function(options) {
			if ( instance === undefined ) {
				instance = new EFLVideoLive(options);
			}

			return instance;
		}
	};

	return _static;
}(window.jQuery, window.EFL));
;
/* eslint-disable block-scoped-var */
/* eslint-disable vars-on-top */
/* This script gathers information and passes it into the datalayer for use by GA Optimize so that it can make decisions (e.g. inject popups with promos) (iConnect project) */

/* Calls to NeuLion are cached in local storage for X seconds to minimize number of calls required (currently every 15 mins) */
/*
Data includes:

    - next-match-first-team-ko:     next first team match KO in UTC/GMT+0   (dd MMMM yyyy HH:mm:ss GMT+00:00)
        [from epi fixture search (find)]

    - next-match-minutes:           minutes until next first team match     (a number. empty if no game)
        [from epi fixture search (find)]

    - next-match-first-team-mc:     next first team match MC Url             (full https:// url)
        [from epi fixture search (find)]

    - video-subscribe-page:         subscribe page url                      (full https:// url)
        [from epi site properties]

    - video-logged-in:              if user is logged into Dice          (true/false)
        [from checking access to DICE]

    - video-access-level:           video access level                      (free / freemium / premium)
        [N/A]

    - video-region:                 video region                            (D / E / I / Unknown)
        [derived from neulion video packages service response meaning Domestic, Europe, International. Will get Unknown if its a premier
        or national club as its not identifiable from their video packages]

    - video-country:                video country                           (two letter alpha-2 iso country code)
        [derived from dice login token]

     - video-country-name:          video country name                     (country name)
        [derived from dice login token]

    - video-state:                  video state                            (format/accuracy dependent on NeuLion)
        [derived from dice login token]

    - video-city:                   video city                              (format/accuracy dependent on NeuLion)
        [derived from dice login token]

    - video-match-pass-available:   match pass available on next first team game (true/false)
        [derived from dice game service response - if the date of the next game matches the date in dice.]

    - match-pass-url:               match pass purchase url for next first team game (full https:// url)
        [derived from neulion game service response and epi site data]

    - match-pass-price:             match pass price for next first team game (price in local currency. format dependent on NeuLion)
        [derived from neulion game service response and epi site data]

    - league-blackout-excluded      for games with a league/partial blackout        (true/false)
                                    it means user country is NOT blacked out
        [derived from neulion config service response]
*/
window.EFL = window.EFL || {};

window.EFL.EFLOptimize = (function ($, EFL) {
	'use strict';

	function EFLOptimize() {

	}

	EFLOptimize.prototype.init = function () {
		this.initEFLOptimize();
	};

	EFLOptimize.prototype.initEFLOptimize = function () {
		var _this = this;


		Promise.all([
			this.updateDataLayerRegion(),
			this.updateDataLayerMatchPass()
		])
			.then(function () {
				_this.initEvents();
			})
			.catch(function (data) { console.log("fail",data) });
	};

	EFLOptimize.prototype.updateDataLayerRegion = function () {
		var updateRegion = function (data) {
			var userTokenData = EFL.DiceAuth.getDecodedTokenData();

			if (userTokenData === undefined || userTokenData.lo2 === undefined) {
				return Promise.reject(userTokenData);
			}
			var regionData = userTokenData.lo2.split(',');

			var blackoutList = window.EFL.video.blackoutCountries;

			var userRegion; //'D' for Domestic or 'I' for International
			var regionCode = regionData[0];//2 letter code eg GB
			if (regionCode == "GB") {
				userRegion = "D";
			} else if (EFL.video.thisPremNat === "True") {
				userRegion = "PN";
			} else {
				userRegion = "I";
			}

			window.EFL.analyticsController.track({ 'video-region': userRegion });

			if (blackoutList.indexOf(regionCode) > -1) {
				window.EFL.analyticsController.track({ 'league-blackout-excluded': 'false' });
			}
			else {
				window.EFL.analyticsController.track({ 'league-blackout-excluded': 'true' });
			}

			window.EFL.analyticsController.track({ 'video-country': regionData[0] });
			window.EFL.analyticsController.track({ 'video-country-name': regionData[1] });
			window.EFL.analyticsController.track({ 'video-state': regionData[4] });
			window.EFL.analyticsController.track({ 'video-city': regionData[3] });


			this.showTargetedCountryContent(regionCode);
			this.showTargetedRegionContent(userRegion);
		}.bind(this);

		return new Promise(function (resolve, reject) {
			EFL.DiceAuth.getTokenSilently()
				.then(function (response) {
					resolve(updateRegion());
				})
				.catch(function (error) {
					reject(error);
				});
		});
	};

	EFLOptimize.prototype.updateDataLayerMatchPass = function () {
		//don't trigger match pass on efl.com
		if (!window.EFL.Dice.useDiceForVideo) {
			return Promise.resolve();
		}
		var withToken = window.EFL.DiceAuth.withToken;
		var updateNextGame = function (games) {
			var loggedin = EFL.DiceAuth.isAuthenticated();
			var accesslevel = "free"; //(free / freemium / premium)
			var matchPassSku;
			var game;

			if (typeof loggedin === 'undefined') {
				loggedin = 'false';
			}

			var dateOfnextKOepoch = new Date(window.EFL.video.nextfxKOBST).getTime();
			//find game in upcoming events with matching starttime.

			for (var i = 0; i < games.length; i++) {
				if (games[i].startDate == dateOfnextKOepoch) {
					game = games[i];
					break;
				}
			}

			if (game !== undefined) {
				//no game with matching start date
				if (game.accessLevel === "GRANTED") {
					accesslevel = 'premium';
				} else if (loggedin) {
					accesslevel = 'fremium';
				}

				//find match pass from game
				var ap = game.availablePurchases;
				var apLen = game.availablePurchases.length;
				if (apLen > -1) {
					for (var i = 0; i < apLen; i++) {
						if (ap[i].purchaseStrategy.type && ap[i].purchaseStrategy.type === "PPV") {
							//found match pass
							if (ap[i].amounts && ap[i].amounts.length) {
								matchPassSku = ap[i].amounts[0];
								break;
							}
						}
					}
				}
			}

			if (matchPassSku !== undefined) {
				var priceString = Number((matchPassSku.amount / (Math.pow(10, matchPassSku.scale))).toFixed(matchPassSku.scale))
					.toLocaleString(undefined, { style: 'currency', currency: matchPassSku.currency });
				var matchPassUrl = "/my-account/?register=true&amp;sku=" + matchPassSku.sku + "&amp;redirect=" + window.location.origin + "/iFollow/subscribe/";
				window.EFL.analyticsController.track({ 'video-match-pass-available': 'true' });
				window.EFL.analyticsController.track({ 'match-pass-url': matchPassUrl });
				window.EFL.analyticsController.track({ 'match-pass-price': priceString });
			} else {
				window.EFL.analyticsController.track({ 'video-match-pass-available': 'false' });
				window.EFL.analyticsController.track({ 'match-pass-url': "" });
				window.EFL.analyticsController.track({ 'match-pass-price': "" });
			}

			window.EFL.analyticsController.track({ 'video-logged-in': loggedin });
			window.EFL.analyticsController.track({ 'video-access-level': accesslevel });

			window.EFL.analyticsController.track({ 'next-match-first-team-ko': window.EFL.video.nextfxKO });
			window.EFL.analyticsController.track({ 'next-match-minutes': window.EFL.video.nextfxKOMins });
			window.EFL.analyticsController.track({ 'next-match-first-team-mc': window.EFL.video.nextfxMC });
			window.EFL.analyticsController.track({ 'video-subscribe-page': window.EFL.video.packagesUrl });


		}.bind(this);

		return new Promise(function (resolve, reject) {
			// TODO if HTTP lib caching is added can remove the below caching
			// TODO or replace with call to get event by Opta ID lookup which doesn't exist yet.
			EFL.EFLDiceLib.getUpcomingMatches({ rpp: 10 })
				.then(function (data) {
					resolve(updateNextGame(data.data.events));
				})
				.catch(function(error) {
					reject(error);
				});
		});
	};

	EFLOptimize.prototype.initEvents = function () {
		window.dataLayer.push({ 'event': 'optimize.activate' });
	};

	//duplicated from original analytics-optimize.js
	EFLOptimize.prototype.showTargetedCountryContent = function (country) {
		$("*[data-target-country]").each(function () {
			var targetCountry = $(this).data('target-country');
			console.log('found content for country ' + targetCountry);
			console.log('user in country ' + country);
			if (targetCountry === country) {
				$(this).removeClass('hide');
			}
		});
	};

	//duplicated from original analytics-optimize.js
	EFLOptimize.prototype.showTargetedRegionContent = function (region) {
		$("*[data-target-region]").each(function () {
			var targetRegion = $(this).data('target-region');
			console.log('found content for region ' + targetRegion);
			console.log('user in region ' + region);
			if (targetRegion === region) {
				$(this).removeClass('hide');
			}
			//Non-domestic, show for all regions except D
			if (targetRegion === 'ND' && region !== 'D') {
				$(this).removeClass('hide');
			}
		});
	};

	// eslint-disable-next-line vars-on-top
	var instance;
	// eslint-disable-next-line vars-on-top
	var _static = {
		name: "EFLOptimize",

		getInstance: function (options) {
			if (instance === undefined) {
				instance = new EFLOptimize(options);
			}

			return instance;
		}
	};

	return _static;
}(window.jQuery, window.EFL));
;
window.EFL = window.EFL || {};

window.EFL.EFLDiceLib = (function (EFL) {
	'use strict';

	var getUnixTimeInMs = EFL.DateUtils.getUnixTimeInMs;
	var startOfDay = EFL.DateUtils.startOfDay;
	var endOfDay = EFL.DateUtils.endOfDay;

	function EFLDiceLib() {

	}

	EFLDiceLib.prototype.init = function () {

	};

	//flags eg {rpp:10, startDateFrom:1595080800000}
	EFLDiceLib.prototype.getUpcomingMatches = function (flags) {
		var withToken = window.EFL.DiceAuth.withToken;
		//build query string from flags
		var querystring = "";
		if (flags) {
			var count = 0;
			for (var key in flags) {
				count++;
				if (count == 1) {
					querystring = "?" + key + "=" + flags[key];
				} else {
					querystring += "&" + key + "=" + flags[key];
				}
			}
		}

		return new Promise(function (resolve, reject) {
			var getEvents = function (token) {
				var API_HEADERS = EFL.DiceAuth.getAPIHeaders();
				//TODO: revisit when paging has been documented
				var apiUrl = EFL.DiceAuth.API_URL_BASE + 'event/upcoming' + querystring;
				API_HEADERS['Authorization'] = 'Bearer ' + token.access_token;

				window.EFL.HTTP.get(apiUrl, {
					headers: API_HEADERS
				})
					.then(function (response) {
						resolve(response);
					})
					.catch(function (error) {
						reject(error);
					});
			};
			withToken(getEvents)();
		});
	};

	// flags eg {rpp:10}
	EFLDiceLib.prototype.getLiveMatches = function (flags) {
		var withToken = window.EFL.DiceAuth.withToken;
		//build query string from flags
		var querystring = "";
		if (flags) {
			var count = 0;
			for (var key in flags) {
				count++;
				if (count == 1) {
					querystring = "?" + key + "=" + flags[key];
				} else {
					querystring += "&" + key + "=" + flags[key];
				}
			}
		}

		return new Promise(function (resolve, reject) {
			var getEvents = function (token) {
				var API_HEADERS = EFL.DiceAuth.getAPIHeaders();
				var apiUrl = EFL.DiceAuth.API_URL_BASE + 'event/live' + querystring;
				API_HEADERS['Authorization'] = 'Bearer ' + token.access_token;

				window.EFL.HTTP.get(apiUrl, {
					headers: API_HEADERS
				})
					.then(function (response) {
						resolve(response);
					})
					.catch(function (error) {
						reject(error);
					});
			};
			withToken(getEvents)();
		});
	};

	EFLDiceLib.prototype.getTeamsFromSku = function (sku) {
		//if found return string HOME-vs-AWAY-datestring
		return new Promise(function (resolve, reject) {
			if (!sku) {
				return reject("Error finding team; no sku - " + sku);
			}
			var eventString = "(not set)";

			var cacheKey = "EFLMatchPassVariant"// + sku;
			var variantString;

			var cachedVariant = window.localStorage.getItem(cacheKey);

			if (cachedVariant) {
				var item;
				try {
					item = JSON.parse(cachedVariant);
				} catch (e) {
					console.log("Mathch Pass Variant is not JSON", e)
				}
				if (item && item.expiry && (item.expiry > Date.now())) {
					variantString = item.variant;
				}
			}

			var referrerIsSameSite = document.referrer.startsWith(location.origin);

			// eventString variant should already be stored in localstorage if clicked through from overlay, subscribe page or match pass link on upcoming games
			// checking referrer so we don't use variantString if linked from outside the site as it could have an old value
			if (referrerIsSameSite && variantString) {
				console.log("found variant" + variantString)
				return resolve(variantString);
			}

			Promise.all([
				EFL.EFLDiceLib.getUpcomingMatches({ rpp: 25 }),
				EFL.EFLDiceLib.getLiveMatches({ rpp: 25 })
			])
			.then(function (data) {
				// combine both calls into one 
				var matchesCombined = data[0].data.events.concat(data[1].data.events)

				if (matchesCombined) {
					eventString = EFL.EFLDiceLib.getEventString(matchesCombined, sku);
				}
				resolve(eventString)

			})
			.catch(function (error) {
				reject(error);
			});
		});
	};

	EFLDiceLib.prototype.getEventString = function (matches, sku) {
		// eslint-disable-next-line guard-for-in
		for (var _event in matches) {
			var diceEvent = matches[_event];
			var ap = diceEvent.availablePurchases;
			var apLen = ap.length;
			if (apLen > 0) {
				//loop through available purchases to find the matching sku
				for (var i = 0; i < apLen; i++) {

					if (ap[i].amounts) {

						var amountsLen = ap[i].amounts.length;
						if (amountsLen > 0) {

							for (var j = 0; j < amountsLen; j++) {

								var amountObj = ap[i].amounts[j];
								if (amountObj.sku === sku) {
									//found the sku now
									var title = diceEvent.title.replace(/\s/g, "-").toLowerCase();

									//if participants available then use that
									if (diceEvent.details && diceEvent.details.participants && diceEvent.details.participants.length >= 2) {
										title = diceEvent.details.participants[0].name + "-vs-" + diceEvent.details.participants[1].name;
									}
									var dateString = new Date(diceEvent.startDate).toISOString().slice(-25, -14);

									return title + "-(" + diceEvent.externalId + ")-on-" + dateString;
								}
							}
						}
					}
				}
			}
		}

		return null;
	}

	EFLDiceLib.prototype.parseTrackingData = function (licence, purchaseType, subscriptionPeriod, eventString, price) {
		return new Promise(function (resolve, reject) {
			var userPackageData = licence;
			var tokenData = EFL.DiceAuth.getDecodedTokenData();
			var countryCode = tokenData.lo2.split(',')[0];

			var skuPrice = (price.price / (Math.pow(10, price.scale))).toFixed(price.scale);

			var name = userPackageData.name;
			var giftingStatus = "(not set)"; // (not set) / gift-payment / gift-redemption TODO: no gifting set up yet
			var region = ""; //D,I
			var category = ""; //"Domestic | International | Premier National | Global Free",
			var id = ""; //"BASIC | ((SEASON|MONTHLY|DAY|WEEK|)(D|E|I|PN)) | (MATCH(XD|XE|XI|A))",
			var variant = "(not set)"; // (match passes only) - currently eg brentford-at-reading-on-06302020
			var dimension12 = "(not set)"; // (not set) / gift-payment / gift-redemption - this is gift status //TODO when gifting is implemented.
			var period = "";

			//region
			if (window.EFL.video.domesticCountries.includes(countryCode)) {
				//domestic
				region = "D";
			} else {
				//international
				region = "I";
			}

			//set category. purchase screen so no "Global Free"
			if (EFL.video.thisPremNat === "True") {
				category = "Premier National";
			} else {
				if (region === "D") {
					category = "Domestic";
				} else {
					category = "International";
				}
			}

			//set ID
			//"((SEASON|MONTHLY|DAY|WEEK|)(D|E|I|PN)) | (MATCH(XD|XE|XI|A))"
			if (purchaseType) {
				if (purchaseType == "PPV") {
					if (userPackageData.name.indexOf("Video") > -1) {
						//match pass (MATCH(XD|XE|XI|A))
						//MATCH6/MATCH1 is legacy EFL only want to separate domestic match passes from international ones
						id = "MATCH6" + region;
						period = "match";
						name = "Video Match Pass";
					} else {
						//Audio
						id = "MATCHA";
						period = "audio-match";
						name = "Audio Match Pass"
					}
					variant = eventString;
				} else if (subscriptionPeriod && purchaseType == "SUBSCRIPTION") {
					//subscription

					// Normalise name structure because National league teams have oddly named passes.
					if (userPackageData.name.indexOf("Video") > -1) {
						name = "Video"
					} else {
						name = "Audio"
					}

					switch (subscriptionPeriod.toUpperCase()) {
						case 'P1Y':
							id = "SEASON";
							break;
						case 'P1M':
							id = "MONTHLY";
							break;
						case 'P7D':
							id = "WEEK";
							break;
						case 'P1D':
							id = "DAY";
							break;
						default:
							break;
					}
					period = id === "MONTHLY" ? "MONTH" : id;

					var titleCaseSub = id.charAt(0).toUpperCase() + id.substr(1).toLowerCase();
					name = name + " " +titleCaseSub + " Pass";

					if (EFL.video.thisPremNat === "True") {
						id += "PN";
					} else {
						id += region;
					}
				}
			}
			var url = '/club-tv/packages/' + period + '/' + category + '/';
			var data = {
				currency: price.currency,
				price: skuPrice,
				giftingStatus: giftingStatus,
				region: region,
				category: category,
				id: id,
				name: name,
				variant: variant,
				dimension12: dimension12,
				url: url.replace(/\s/g, "-").toLowerCase()
			}
			resolve(data);
		});
	};

	EFLDiceLib.prototype.getLiveEvents = function (ko) {
		var withToken = window.EFL.DiceAuth.withToken;
		var getLiveEvents;

		getLiveEvents = function (resolve, reject, tokens) {
			var API_HEADERS = EFL.DiceAuth.getAPIHeaders();
			API_HEADERS['Authorization'] = 'Bearer ' + tokens.access_token;

			var apiUrl =
				EFL.DiceAuth.API_URL_BASE +
				'event/live?startDateFrom=' + getUnixTimeInMs(startOfDay(ko)) +
				'&startDateUntil=' + getUnixTimeInMs(endOfDay(ko));

			window.EFL.HTTP.get(apiUrl, {
				headers: API_HEADERS
			})
				.then(function (response) {
					resolve(response.data);
				})
				.catch(function (error) {
					reject(error);
				});
		};

		return new Promise(function (resolve, reject) {
			withToken(getLiveEvents)(resolve, reject);
		});
	};

	EFLDiceLib.prototype.getStripeSetupIntent = function (paymentProviderID) {
		var withToken = window.EFL.DiceAuth.withToken;
		return new Promise(function (resolve, reject) {
			var getSetupIntent = function (token) {
				var API_HEADERS = EFL.DiceAuth.getAPIHeaders();
				var apiUrl = EFL.DiceAuth.API_URL_BASE + 'customer/stripe/' + paymentProviderID + '/start-authorization';
				API_HEADERS['Authorization'] = 'Bearer ' + token.access_token;

				window.EFL.HTTP.get(apiUrl, {
					headers: API_HEADERS
				})
					.then(function (response) {
						resolve(response);
					})
					.catch(function (error) {
						reject(error);
					});
			};
			withToken(getSetupIntent)();
		});
	};


	return new EFLDiceLib();
}(window.EFL));
;
window.EFL = window.EFL || {};

window.EFL.EFLPaymentAuthorisation = (function ($, EFL) {
	'use strict';

	//Constructor
	function EFLPaymentAuthorisation() {
		this.API_HEADERS = {
			"x-api-key": EFL.Dice.key,
			"app": EFL.Dice.app,
			"Content-Type": "application/json"
		};
		this.STRIPE = {
			Stripe: undefined,
			paymentIntentId: undefined
		};
		this.elements = {
			authForm: '#payment-authorisation-form',
			stripeErrorEl: 'card-errors',
			'$genericFormError': $('.form-error'),
			'$cardSuccess': $('.card-confirmation'),
			'$cardConfirmed': $('.card-already-confirmed'),
			'$verifyCardButton': $('button.js-verify-card-button'),
		};
	}

	EFLPaymentAuthorisation.prototype.init = function () {
		var _this = this;

		this.getQueryStrings()
			.then(_this.getStripeKey.bind(this))
			.then(_this.initStripeAndEvents.bind(this))
			.catch(_this.showErrorMessage.bind(this));
	};

	EFLPaymentAuthorisation.prototype.getQueryStrings = function () {
		var urlParams = new URLSearchParams(window.location.search);

		var providerId = urlParams.get('providerId');
		var paymentIntentId = urlParams.get('token');

		if (!providerId || !paymentIntentId) {
			// no query string data
			return Promise.reject();
		}

		this.STRIPE.paymentIntentId = paymentIntentId;

		return Promise.resolve(providerId);
	};

	EFLPaymentAuthorisation.prototype.getStripeKey = function (providerId) {
		var _this = this;
		return new Promise(function (resolve, reject) {
			// Get stripe key from realm settings
			EFL.HTTP.get(EFL.Dice.apiUrl + '/api/v2/realm-settings/dce.efl', {
				headers: _this.API_HEADERS
			}).then(function (response) {
				if (response && response.data && response.data.paymentProviders && response.data.paymentProviders.length) {
					var paymentProviderList = response.data.paymentProviders;

					var stripeData = paymentProviderList.filter(function (item) {
						return item.paymentProviderId == providerId;
					})[0];

					if (stripeData && stripeData.details && stripeData.details.publishableKey) {
						return resolve(stripeData.details.publishableKey);
					} else {
						reject();
					}
				}
			}).catch(function (error) {
				reject();
			});
		});
	};

	EFLPaymentAuthorisation.prototype.initStripeAndEvents = function (stripePublishableKey) {
		var _this = this;
		// Load stripe sdk
		this.STRIPE.Stripe = Stripe(stripePublishableKey);

		// Check intent and show button
		this._checkStripePayment(this.STRIPE.paymentIntentId)
			.then(function (result) {
				// Sca failed
				if (result.error) {
					throw undefined;
				} else {
					if (result.paymentIntent.status === "requires_source_action") {
						// Requires auth; show button
						_this.elements.$verifyCardButton.show();
					} else if (result.paymentIntent.status === "succeeded") {
						// Already authed
						_this.elements.$cardConfirmed.show();
					} else {
						// Error with token.
						throw undefined;
					}
				}
			}).catch(function (error) {
				_this.showErrorMessage(error);
			});

		this.elements.$verifyCardButton.on('click', function (e) {
			e.preventDefault();

			this.elements.$verifyCardButton.prop('disabled', true);

			this.hideAllErrorMessages();
			this.confirmCardPayment();

			return false;
		}.bind(this));
	};

	EFLPaymentAuthorisation.prototype.confirmCardPayment = function () {
		var _this = this;
		this._stripeConfirmCardPayment(this.STRIPE.paymentIntentId)
			.then(function (result) {
				// Sca failed
				if(result.error) {
					throw EFL.DiceRealmLabels.getInstance().getLabel('paymentAuthenticationFailure'); 
				} else {
					// Sca success - show complete mesasge
					_this.elements.$cardSuccess.show();
				}
			}).catch(function (error) {
				_this.showErrorMessage(error);
			});
	};

	EFLPaymentAuthorisation.prototype._stripeConfirmCardPayment = function (paymentIntentId) {
		var _this = this;
		// Add Stripe call into promise to trap sdk errors and show error message.
		return new Promise(function (resolve, reject) {
			_this.STRIPE.Stripe.confirmCardPayment(paymentIntentId)
				.then(function (result) {
					resolve(result);
				}).catch(function (error) {
					reject(error);
				});
		});
	};

	EFLPaymentAuthorisation.prototype._checkStripePayment = function (paymentIntentId) {
		var _this = this;
		// Add Stripe call into promise to trap sdk errors and show error message.
		return new Promise(function (resolve, reject) {
			_this.STRIPE.Stripe.retrievePaymentIntent(paymentIntentId)
				.then(function (result) {
					resolve(result);
				}).catch(function (error) {
					reject(error);
				});
		});
	};

	EFLPaymentAuthorisation.prototype.showErrorMessage = function (error) {
		console.log(error);
		// Enable button to try again and show error message
		this.elements.$verifyCardButton.prop('disabled', false);

		if (error) {
			var displayError = document.getElementById(this.elements.stripeErrorEl);

			displayError.textContent = error;
			displayError.style.cssText = 'display:block;';
		} else  {
			// Show generic error
			this.elements.$genericFormError.show();
		}
	};

	EFLPaymentAuthorisation.prototype.hideAllErrorMessages = function () {
		this.elements.$genericFormError.hide();

		var displayError = document.getElementById(this.elements.stripeErrorEl);
		displayError.textContent = "";
	};

	// our instance holder
	// eslint-disable-next-line vars-on-top
	var instance;

	// an emulation of static variables and methods
	// eslint-disable-next-line vars-on-top
	var _static = {
		name: "EFLPaymentAuthorisation",

		// Method for getting an instance. It returns
		// a singleton instance of a singleton object
		getInstance: function (options) {
			if (instance === undefined) {
				instance = new EFLPaymentAuthorisation(options);
			}

			return instance;
		}
	};

	return _static;
}(window.jQuery, window.EFL));
;
(function () {
	'using strict';

	var settings = {
		trigger: '[id="fifa21"]'
	};

	function init() {
		
		var element = $(this);
		let url = new URL(document.location.href);
		let league = url.searchParams.get("league");
		let club = url.searchParams.get("club");


		//default iframe - https://fifa21-club-packs.ea.com/en-us?layout=iframe&leagues=league-one%7Cleague-two%7Cchampionship

		if (league && club) {
			element.attr("data-src", "https://fifa21-club-packs.ea.com/en-us/england/" + league + "/" + club + "/?layout=iframe");
			element.attr("src", "https://fifa21-club-packs.ea.com/en-us/england/" + league + "/" + club + "/?layout=iframe");
		}
		else if (league) {
			element.attr("data-src", "https://fifa21-club-packs.ea.com/en-us/england/" + league + "/?layout=iframe");
			element.attr("src", "https://fifa21-club-packs.ea.com/en-us/england/" + league + "/?layout=iframe");
		}
	}

	function clearStyles(element) {
		$(element).find("*").removeAttr("style");
		$(element).removeClass("hidden");
	}

	function updateLinks(element) {
		element.find('a').each(function () {
			$(this).attr('target', '_blank');
		});
	}

	$(function () {
		$(settings.trigger).each(init);
	});

})();
;
/* eslint-disable vars-on-top */
(function($, EFL) {
	'use strict';

	$(document).ready(function () {
		$('input, textarea').placeholder(); // Input placeholder polyfill for IE9 and below

		$('input,select, textarea').each(function () {
			var req = jQuery(this).attr('aria-required');
			if (undefined != req) {
				var label = jQuery('label[for="' + jQuery(this).attr('id') + '"]');
				var text = label.text();
				if (text.length > 0) {
					label.append('<span class="required">*</span>');
				}
			}
		});

		// run tooltips
		$('.custom-tooltip').on('click', function (e) {
			e.preventDefault();
			$('.custom-tooltip').not(this).find('.tooltip').attr("aria-hidden", "true").hide();
			var iconTrigger = $(this);
			var tooltip = iconTrigger.find('.tooltip');
			if (tooltip.is(':visible')) {
				tooltip.attr("aria-hidden", "true").hide();
				iconTrigger.attr("aria-expanded", "false");
			} else {
				tooltip.attr("aria-hidden", "false").show();
				tooltip.find('a').first().focus();
				iconTrigger.attr("aria-expanded", "true");
			}

			tooltip.find('a:last').on('blur touchend', function () {
				tooltip.attr("aria-hidden", "true").hide();
				iconTrigger.attr("aria-expanded", "false");
				// after last link is tabbed from, focus back to tooltip icon
				iconTrigger.focus();
			});
		});

		// EFLRM-420 Add margins back onto the filters to sort overlap issue. Rotten way of doing it - I know.
		if ($('.match-alert-panel').length > 0) {
			if ($('.filters-group').length > 0) {
				$('.filters-group').first().removeClass("remove-header-margin");
			}

			// Had to do this nonsence for when the first block in MainContent in a Latest News Block...sigh.
			if ($("#maincontent.home-page").length > 0) {
				if ($("#maincontent.home-page section:first").is(".news-grid") ||
					$("#maincontent.home-page section:first").find("article-container")) {
					$("#maincontent.home-page").css("margin-top", "0");
				}
			}

		}

        // Deep link to accordions
		function openAnchorAccordion() {
			if ($('.accordion').length > 0 && window.location.hash) {
				var hash = window.location.hash;
				// replace characters within the first # and _ to change from #accordion and #collapse
				var collapseId = hash.replace(/#.*?_/, '#collapse_');
				if ($(collapseId).length > 0) {
					// show deeplinked accordion
					$(collapseId).collapse('show');

					var accordionTrigger = $('.accordion-trigger[data-parent="' + hash + '"]');

					if (accordionTrigger.length === 0) {
						accordionTrigger = $('.accordion-trigger[data-parent="' + hash.replace(/#.*?_/, '#accordion_') + '"]');
					}

					var location = accordionTrigger.offset().top - 100;
					// when using bootstrap's on shown event it was scrolling again when a user manually clicks on the accordion
					// so having to use a setTimeout instead
					window.setTimeout(function () {
						$("html, body").animate({ scrollTop: location }, 1000);
					}, 1000);

				}
			}
		}

		openAnchorAccordion();

		$('.accordion-panel a').on('click', function() {
			window.addEventListener('hashchange', openAnchorAccordion, false);
		});

		// Hackery.  AddThis uses tabindex=1 on all instances, so overtakes the natural flow
        //of keyboard navigation.  They also have no JS events when the markup loads.
        //So.. this polls, removes the tab index, and adds a fake href to bring it back into the natural document flow.
        function shareEventHandler(evt) {

			var maxPoll = 0;

			if (evt.type == 'addthis.ready') {

				var addthisPoll = setInterval(function () {

					var $buttons = $('.at-share-btn');
					var expectedSocialButtonCount = $('.social-sharing').length;

					if ($('.at-share-btn-elements').length >= expectedSocialButtonCount) {
						window.EFL.eventDispatcher.dispatch('addthis-loaded');
						$buttons.removeAttr('tabindex').attr('href', '#').find('svg').attr('focusable', false);
						clearInterval(addthisPoll);
						return;
					}

					if (maxPoll < 1000) {
						maxPoll += 20;
					} else {
						maxPoll = 0;
						clearInterval(addthisPoll);
					}
				}, 20);
			}
		}

		function addThisAnalyticsEventHandler(evt) {
			// eslint-disable-next-line default-case
			switch (evt.type) {
				case "addthis.menu.share":
					if (dataLayer) {
						window.dataLayer.push({
							'event': 'gaSocial',
							'gaSocialNetwork': evt.data.service,
							'gaSocialAction': 'Share',
							'gaSocialTarget': evt.data.url
						});
					}
					break;
			}
		}

		window.addEventListener('addthis.onload', function () {
			// Listen for the share event
			if (addthis.addEventListener !== undefined) {
				addthis.addEventListener('addthis.ready', shareEventHandler);
				addthis.addEventListener('addthis.menu.share', addThisAnalyticsEventHandler);
			}
		})

		doneResizing();

		window.EFL.eventDispatcher.registerHandler('markup-injected', doneResizing);
		$(window).on('throttled-resize', doneResizing);
		$(window).on('update-navigation', doneResizing);

		$('[data-imagetype]').picture({
			background: true
		});

		if (window.EFL.Dice.useDiceForVideo) {
			// Dice modules
			// Most will only run if the page has all the elements needed to proceed,
			// or each module will self init only if it has to.
			$('.video-down').first().removeClass('hidden');
			var initDiceModules = function() {
				EFL.EFLProfile.getInstance().init();
				EFL.EFLRegister.getInstance().init();

				document.querySelector('[data-trigger-efl-login]') && EFL.EFLLogin.getInstance().init();
				document.querySelector('[data-trigger-pass-reset]') && EFL.EFLPasswordReset.getInstance().init();
				document.querySelector('[data-trigger-subscribe-packages]') && EFL.EFLSubscribePackages.getInstance().initSubscribePage();
				document.querySelector('[data-trigger-myaccount]') && EFL.EFLMyAccount.getInstance().init();
				document.querySelector('[data-trigger-checkout]') && EFL.EFLCheckout.getInstance().init();
				document.querySelector('[data-trigger-checkoutcomplete]') && EFL.EFLCheckoutComplete.getInstance().init();
				document.querySelector('[data-trigger-addcard]') && EFL.EFLAddCard.getInstance().init();
				
				document.querySelector('[data-trigger-live-video]') && EFL.EFLVideoLive.getInstance().init();

				document.querySelector('[data-trigger-paymentauthorisation]') && EFL.EFLPaymentAuthorisation.getInstance().init();

				// Not EFL.com
				if (window.EFL.video.siteType !== '0') {
					EFL.DiceVideoAccess.loadThumbnailIndicators();
					EFL.DiceVideoAccess.loadVideoPlayerIndicators();
					EFL.EFLVideoTrigger.getInstance().init();
					EFL.EFLOptimize.getInstance().init();
				}
			};

			initDiceModules();
		} else {
			EFL.VideoStorage();
			EFL.VideoAccess();
			EFL.VideoPackages();
			EFL.VideoCrossDomain();
			EFL.VideoTrigger();
			EFL.VideoLoggedIn();

			if (window.EFL.video.siteType === 0) {
				//0 = efl.com, 1 = efl league club, 2 = national/premier league
				EFL.AnalyticsOptimizeEFL();
			} else {
				EFL.AnalyticsOptimize();
			}
		}

		$('a:has(img)').addClass('hasImage')
	});

	function doneResizing() {
		var scope = $('body');

		// On Nokia Lumia, the matchHeight code was causing header navigation to flicker whilst scrolling
		if (isMobile.windows.device) {
			scope = $('#maincontent, .site-footer');
		}

		scope.find('.match-height-inner-1').matchHeight();
		scope.find('.match-height-inner-2').matchHeight();
		scope.find('.match-height-inner-3').matchHeight();
		scope.find('.match-height').matchHeight();
		scope.find('.match-height-outer').matchHeight();

		$('.home-efl-today .standings-summary-heading').matchHeight();

		window.bootstrap = findBootstrapEnvironment();

		if (window.bootstrap === "xs" || window.bootstrap === "sm") {
			$('.cta-container:not(.width-quarter) .content').matchHeight();
		} else {
			$('.cta-container:not(.width-quarter) .content').attr("style", "");
		}

	}
}(window.jQuery, window.EFL));
;
