/* Minification failed. Returning unminified contents.
(3910,109-110): run-time error JS1195: Expected expression: >
(3910,145-146): run-time error JS1004: Expected ';': )
(4042,17-18): run-time error JS1014: Invalid character: `
(4042,37-38): run-time error JS1193: Expected ',' or ')': :
(4042,52-53): run-time error JS1004: Expected ';': {
(4042,63-64): run-time error JS1014: Invalid character: `
(4042,64-65): run-time error JS1195: Expected expression: )
(4046,12-13): run-time error JS1004: Expected ';': :
(4051,29-30): run-time error JS1004: Expected ';': {
(4053,14-15): run-time error JS1195: Expected expression: ,
(4054,44-45): run-time error JS1004: Expected ';': {
(4058,5-6): run-time error JS1002: Syntax error: }
(4063,52-53): run-time error JS1004: Expected ';': {
(4281,17-18): run-time error JS1014: Invalid character: `
(4281,18-19): run-time error JS1195: Expected expression: <
(4281,157-158): run-time error JS1197: Too many errors. The file might not be a JavaScript file: >
(4257,1-74): run-time error JS1301: End of file encountered before function is properly closed: function buildLocationPinDataAttributesInfoboxContent(location, mapIndex)
(4281,165-169): run-time error JS1004: Expected ';': this
(4281,184-185): run-time error JS1014: Invalid character: `
(4281,185-186): run-time error JS1195: Expected expression: ;
(4282,5-6): run-time error JS1002: Syntax error: }
(4283,5-9): run-time error JS1197: Too many errors. The file might not be a JavaScript file: html
 */
/*
    http://www.JSON.org/json2.js
    2011-10-19

    Public Domain.

    NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.

    See http://www.JSON.org/js.html


    This code should be minified before deployment.
    See http://javascript.crockford.com/jsmin.html

    USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
    NOT CONTROL.


    This file creates a global JSON object containing two methods: stringify
    and parse.

        JSON.stringify(value, replacer, space)
            value       any JavaScript value, usually an object or array.

            replacer    an optional parameter that determines how object
                        values are stringified for objects. It can be a
                        function or an array of strings.

            space       an optional parameter that specifies the indentation
                        of nested structures. If it is omitted, the text will
                        be packed without extra whitespace. If it is a number,
                        it will specify the number of spaces to indent at each
                        level. If it is a string (such as '\t' or '&nbsp;'),
                        it contains the characters used to indent at each level.

            This method produces a JSON text from a JavaScript value.

            When an object value is found, if the object contains a toJSON
            method, its toJSON method will be called and the result will be
            stringified. A toJSON method does not serialize: it returns the
            value represented by the name/value pair that should be serialized,
            or undefined if nothing should be serialized. The toJSON method
            will be passed the key associated with the value, and this will be
            bound to the value

            For example, this would serialize Dates as ISO strings.

                Date.prototype.toJSON = function (key) {
                    function f(n) {
                        // Format integers to have at least two digits.
                        return n < 10 ? '0' + n : n;
                    }

                    return this.getUTCFullYear()   + '-' +
                         f(this.getUTCMonth() + 1) + '-' +
                         f(this.getUTCDate())      + 'T' +
                         f(this.getUTCHours())     + ':' +
                         f(this.getUTCMinutes())   + ':' +
                         f(this.getUTCSeconds())   + 'Z';
                };

            You can provide an optional replacer method. It will be passed the
            key and value of each member, with this bound to the containing
            object. The value that is returned from your method will be
            serialized. If your method returns undefined, then the member will
            be excluded from the serialization.

            If the replacer parameter is an array of strings, then it will be
            used to select the members to be serialized. It filters the results
            such that only members with keys listed in the replacer array are
            stringified.

            Values that do not have JSON representations, such as undefined or
            functions, will not be serialized. Such values in objects will be
            dropped; in arrays they will be replaced with null. You can use
            a replacer function to replace those with JSON values.
            JSON.stringify(undefined) returns undefined.

            The optional space parameter produces a stringification of the
            value that is filled with line breaks and indentation to make it
            easier to read.

            If the space parameter is a non-empty string, then that string will
            be used for indentation. If the space parameter is a number, then
            the indentation will be that many spaces.

            Example:

            text = JSON.stringify(['e', {pluribus: 'unum'}]);
            // text is '["e",{"pluribus":"unum"}]'


            text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t');
            // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]'

            text = JSON.stringify([new Date()], function (key, value) {
                return this[key] instanceof Date ?
                    'Date(' + this[key] + ')' : value;
            });
            // text is '["Date(---current time---)"]'


        JSON.parse(text, reviver)
            This method parses a JSON text to produce an object or array.
            It can throw a SyntaxError exception.

            The optional reviver parameter is a function that can filter and
            transform the results. It receives each of the keys and values,
            and its return value is used instead of the original value.
            If it returns what it received, then the structure is not modified.
            If it returns undefined then the member is deleted.

            Example:

            // Parse the text. Values that look like ISO date strings will
            // be converted to Date objects.

            myData = JSON.parse(text, function (key, value) {
                var a;
                if (typeof value === 'string') {
                    a =
/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
                    if (a) {
                        return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4],
                            +a[5], +a[6]));
                    }
                }
                return value;
            });

            myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) {
                var d;
                if (typeof value === 'string' &&
                        value.slice(0, 5) === 'Date(' &&
                        value.slice(-1) === ')') {
                    d = new Date(value.slice(5, -1));
                    if (d) {
                        return d;
                    }
                }
                return value;
            });


    This is a reference implementation. You are free to copy, modify, or
    redistribute.
*/

/*jslint evil: true, regexp: true */

/*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply,
    call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours,
    getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join,
    lastIndex, length, parse, prototype, push, replace, slice, stringify,
    test, toJSON, toString, valueOf
*/


// Create a JSON object only if one does not already exist. We create the
// methods in a closure to avoid creating global variables.

var JSON;
if (!JSON) {
    JSON = {};
}

(function () {
    'use strict';

    function f(n) {
        // Format integers to have at least two digits.
        return n < 10 ? '0' + n : n;
    }

    if (typeof Date.prototype.toJSON !== 'function') {

        Date.prototype.toJSON = function (key) {

            return isFinite(this.valueOf())
                ? this.getUTCFullYear()     + '-' +
                    f(this.getUTCMonth() + 1) + '-' +
                    f(this.getUTCDate())      + 'T' +
                    f(this.getUTCHours())     + ':' +
                    f(this.getUTCMinutes())   + ':' +
                    f(this.getUTCSeconds())   + 'Z'
                : null;
        };

        String.prototype.toJSON      =
            Number.prototype.toJSON  =
            Boolean.prototype.toJSON = function (key) {
                return this.valueOf();
            };
    }

    var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
        escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
        gap,
        indent,
        meta = {    // table of character substitutions
            '\b': '\\b',
            '\t': '\\t',
            '\n': '\\n',
            '\f': '\\f',
            '\r': '\\r',
            '"' : '\\"',
            '\\': '\\\\'
        },
        rep;


    function quote(string) {

// If the string contains no control characters, no quote characters, and no
// backslash characters, then we can safely slap some quotes around it.
// Otherwise we must also replace the offending characters with safe escape
// sequences.

        escapable.lastIndex = 0;
        return escapable.test(string) ? '"' + string.replace(escapable, function (a) {
            var c = meta[a];
            return typeof c === 'string'
                ? c
                : '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
        }) + '"' : '"' + string + '"';
    }


    function str(key, holder) {

// Produce a string from holder[key].

        var i,          // The loop counter.
            k,          // The member key.
            v,          // The member value.
            length,
            mind = gap,
            partial,
            value = holder[key];

// If the value has a toJSON method, call it to obtain a replacement value.

        if (value && typeof value === 'object' &&
                typeof value.toJSON === 'function') {
            value = value.toJSON(key);
        }

// If we were called with a replacer function, then call the replacer to
// obtain a replacement value.

        if (typeof rep === 'function') {
            value = rep.call(holder, key, value);
        }

// What happens next depends on the value's type.

        switch (typeof value) {
        case 'string':
            return quote(value);

        case 'number':

// JSON numbers must be finite. Encode non-finite numbers as null.

            return isFinite(value) ? String(value) : 'null';

        case 'boolean':
        case 'null':

// If the value is a boolean or null, convert it to a string. Note:
// typeof null does not produce 'null'. The case is included here in
// the remote chance that this gets fixed someday.

            return String(value);

// If the type is 'object', we might be dealing with an object or an array or
// null.

        case 'object':

// Due to a specification blunder in ECMAScript, typeof null is 'object',
// so watch out for that case.

            if (!value) {
                return 'null';
            }

// Make an array to hold the partial results of stringifying this object value.

            gap += indent;
            partial = [];

// Is the value an array?

            if (Object.prototype.toString.apply(value) === '[object Array]') {

// The value is an array. Stringify every element. Use null as a placeholder
// for non-JSON values.

                length = value.length;
                for (i = 0; i < length; i += 1) {
                    partial[i] = str(i, value) || 'null';
                }

// Join all of the elements together, separated with commas, and wrap them in
// brackets.

                v = partial.length === 0
                    ? '[]'
                    : gap
                    ? '[\n' + gap + partial.join(',\n' + gap) + '\n' + mind + ']'
                    : '[' + partial.join(',') + ']';
                gap = mind;
                return v;
            }

// If the replacer is an array, use it to select the members to be stringified.

            if (rep && typeof rep === 'object') {
                length = rep.length;
                for (i = 0; i < length; i += 1) {
                    if (typeof rep[i] === 'string') {
                        k = rep[i];
                        v = str(k, value);
                        if (v) {
                            partial.push(quote(k) + (gap ? ': ' : ':') + v);
                        }
                    }
                }
            } else {

// Otherwise, iterate through all of the keys in the object.

                for (k in value) {
                    if (Object.prototype.hasOwnProperty.call(value, k)) {
                        v = str(k, value);
                        if (v) {
                            partial.push(quote(k) + (gap ? ': ' : ':') + v);
                        }
                    }
                }
            }

// Join all of the member texts together, separated with commas,
// and wrap them in braces.

            v = partial.length === 0
                ? '{}'
                : gap
                ? '{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}'
                : '{' + partial.join(',') + '}';
            gap = mind;
            return v;
        }
    }

// If the JSON object does not yet have a stringify method, give it one.

    if (typeof JSON.stringify !== 'function') {
        JSON.stringify = function (value, replacer, space) {

// The stringify method takes a value and an optional replacer, and an optional
// space parameter, and returns a JSON text. The replacer can be a function
// that can replace values, or an array of strings that will select the keys.
// A default replacer method can be provided. Use of the space parameter can
// produce text that is more easily readable.

            var i;
            gap = '';
            indent = '';

// If the space parameter is a number, make an indent string containing that
// many spaces.

            if (typeof space === 'number') {
                for (i = 0; i < space; i += 1) {
                    indent += ' ';
                }

// If the space parameter is a string, it will be used as the indent string.

            } else if (typeof space === 'string') {
                indent = space;
            }

// If there is a replacer, it must be a function or an array.
// Otherwise, throw an error.

            rep = replacer;
            if (replacer && typeof replacer !== 'function' &&
                    (typeof replacer !== 'object' ||
                    typeof replacer.length !== 'number')) {
                throw new Error('JSON.stringify');
            }

// Make a fake root object containing our value under the key of ''.
// Return the result of stringifying the value.

            return str('', {'': value});
        };
    }


// If the JSON object does not yet have a parse method, give it one.

    if (typeof JSON.parse !== 'function') {
        JSON.parse = function (text, reviver) {

// The parse method takes a text and an optional reviver function, and returns
// a JavaScript value if the text is a valid JSON text.

            var j;

            function walk(holder, key) {

// The walk method is used to recursively walk the resulting structure so
// that modifications can be made.

                var k, v, value = holder[key];
                if (value && typeof value === 'object') {
                    for (k in value) {
                        if (Object.prototype.hasOwnProperty.call(value, k)) {
                            v = walk(value, k);
                            if (v !== undefined) {
                                value[k] = v;
                            } else {
                                delete value[k];
                            }
                        }
                    }
                }
                return reviver.call(holder, key, value);
            }


// Parsing happens in four stages. In the first stage, we replace certain
// Unicode characters with escape sequences. JavaScript handles many characters
// incorrectly, either silently deleting them, or treating them as line endings.

            text = String(text);
            cx.lastIndex = 0;
            if (cx.test(text)) {
                text = text.replace(cx, function (a) {
                    return '\\u' +
                        ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
                });
            }

// In the second stage, we run the text against regular expressions that look
// for non-JSON patterns. We are especially concerned with '()' and 'new'
// because they can cause invocation, and '=' because it can cause mutation.
// But just to be safe, we want to reject all unexpected forms.

// We split the second stage into 4 regexp operations in order to work around
// crippling inefficiencies in IE's and Safari's regexp engines. First we
// replace the JSON backslash pairs with '@' (a non-JSON character). Second, we
// replace all simple value tokens with ']' characters. Third, we delete all
// open brackets that follow a colon or comma or that begin the text. Finally,
// we look to see that the remaining characters are only whitespace or ']' or
// ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.

            if (/^[\],:{}\s]*$/
                    .test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@')
                        .replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']')
                        .replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {

// In the third stage we use the eval function to compile the text into a
// JavaScript structure. The '{' operator is subject to a syntactic ambiguity
// in JavaScript: it can begin a block or an object literal. We wrap the text
// in parens to eliminate the ambiguity.

                j = eval('(' + text + ')');

// In the optional fourth stage, we recursively walk the new structure, passing
// each name/value pair to a reviver function for possible transformation.

                return typeof reviver === 'function'
                    ? walk({'': j}, '')
                    : j;
            }

// If the text is not JSON parseable, then a SyntaxError is thrown.

            throw new SyntaxError('JSON.parse');
        };
    }
}());
;
/**
*  https://github.com/PowerKiKi/ie_expand_select_width
*/
(function ($) {
    "use strict";

    $.fn.ieExpandSelectWidth = function () {
        this.filter('select')
		.bind('mouseenter focus', function (event) {
		    open($(this), event.type == 'mouseenter');
		});

        return this;
    };

    /**
    * Open the expanded select
    * @param select jQuery object for the original select element
    * @param openedViaMouse boolean whether the open was initiated via mouse or keyboard
    */
    function open(select, openedViaMouse) {
        // Allow only one clone for one select to be opened at any given time
        // and only select in 'single choice' mode
        if ($.data(document.body, 'ie_expand_select_width_lock')
			|| select.data('ie_expand_select_width_clone')
			|| select.attr('multiple')
			|| select.attr('size') > 1
			|| select.data('ie_expand_select_width_ignore')) {
            return;
        }
        $.data(document.body, 'ie_expand_select_width_lock', true);

        // Clone the select to keep the layout intact
        var selectClone = select.clone();
        selectClone.val(select.val());
        select.data('ie_expand_select_width_clone', selectClone);

        var style = getComputedStyleMap(select);
        style['min-width'] = select.width(); // Cannot be shorter than current width
        style['max-width'] = 'none'; // Can be as long as it needs to be
        style['width'] = 'auto';
        style['z-index'] = 9999; // be sure it's always on top of everything else
        selectClone.css(style);

        // Insert the clone at the very end of document, so it does not break layout
        selectClone.appendTo('body');

        // If the clone is actually shorter than original, cancel everything and 
        // never expand this select anymore
        if (selectClone.width() <= select.width()) {
            select.data('ie_expand_select_width_ignore', true);
            $.data(document.body, 'ie_expand_select_width_lock', false);
            close(select, selectClone);
            return;
        }

        // Move the clone as an overlay on top of the original
        reposition(select, selectClone);

        if (!openedViaMouse) {
            selectClone.focus();
        }

        // Bind events to close
        selectClone
		.bind('keydown keyup', function (event) {
		    selectClone.data('ie_expand_select_width_key_is_down', event.type == 'keydown');
		})
		.bind('mousedown mouseup', function (event) {
		    selectClone.data('ie_expand_select_width_mouse_is_down', event.type == 'mousedown');
		})
		.bind('blur', function () {
		    close(select, selectClone);
		})
		.bind('change', function () {
		    // Only close if the change was made via mouse
		    if (!selectClone.data('ie_expand_select_width_key_is_down'))
		        close(select, selectClone);
		});

        // Only close if we are doing a simple hover and not an a choice in a expanded select
        if (openedViaMouse) {
            selectClone.bind('mouseleave', function () {
                if (!selectClone.is(':focus'))
                    close(select, selectClone);
            });
        }

        $(window).bind('resize.ie_expand_select_width', function () { reposition(select, selectClone); });

        // Remember we are the last select to have been cloned
        $.data(document.body, 'ie_expand_select_width_last_select', select);

        $.data(document.body, 'ie_expand_select_width_lock', false);
    }

    /**
    * Close the expanded select
    * @param select jQuery object for the original select element
    * @param selectClone jQuery object for the cloned select element
    */
    function close(select, selectClone) {
        if (!selectClone || $.data(document.body, 'ie_expand_select_width_lock')) {
            return;
        }

        // Update value if different
        var cloneValue = selectClone.val();
        if (cloneValue != select.val())
            select.val(cloneValue).change();

        selectClone.remove();
        select.data('ie_expand_select_width_clone', null);

        // If we are closing because another select opened, then we need
        // to reposition that second select's clone after destroying our clone
        var lastSelect = $.data(document.body, 'ie_expand_select_width_last_select');
        if (lastSelect) {
            var lastSelectClone = lastSelect.data('ie_expand_select_width_clone');
            reposition(lastSelect, lastSelectClone);
        }

        $(window).unbind('resize.ie_expand_select_width');
    }

    /**
    * Reposition clone on top of its original
    * @param select jQuery object for the original select element
    * @param selectClone jQuery object for the cloned select element
    */
    function reposition(select, selectClone) {
        if (!select || !selectClone)
            return;

        // Move the clone as an overlay on top of the original
        selectClone.position({
            my: 'left',
            at: 'left',
            of: select,
            collision: 'none'
        });
    }

    /**
    * Returns a map of computed CSS style for the fiven element
    * Highly inspired from http://stackoverflow.com/a/6416477/37706
    */
    function getComputedStyleMap(element) {
        var dom = element.get(0);
        var style;
        var result = {};
        if (window.getComputedStyle) {
            style = window.getComputedStyle(dom, null);
            for (var i = 0; i < style.length; i++) {
                var prop = style[i];
                result[prop] = style.getPropertyValue(prop);
            }
        }
        else if (dom.currentStyle) {
            style = dom.currentStyle;
            for (var prop in style) {
                result[prop] = style[prop];
            }
        }

        return result;
    }

})(jQuery);


jQuery(document).ready(function () {
    if (navigator.appVersion.indexOf("MSIE 8.") != -1) {
        jQuery('select').ieExpandSelectWidth();
    }
});
;
(function ($) {

	$.fn.toggleDisplay = function () {
		this.hide();
		$(this).find('*[evaluationfunction]').each(function () { ValidatorEnable(this, false); });
	};

})(jQuery);
;
var validateOnLoad = false;

Sys.Application.add_load(applicationLoadHandler);

/*

jQuery(window).on('load', function () {

    var data = sessionStorage.getItem('tabId');

    if (!data) {
        data = S4();
        sessionStorage.setItem('tabId', data);        
    }
    jQuery('#_BrowserTabId').val(data);

    console.log('common loaded _ ' + data);

});

function S4() {
    return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1);
}*/


function applicationLoadHandler(sender, args) {

    if (!Sys.WebForms.PageRequestManager.getInstance().get_isInAsyncPostBack()) {
        Sys.WebForms.PageRequestManager.getInstance().add_initializeRequest(eghCommonInitializeRequest);
        Sys.WebForms.PageRequestManager.getInstance().add_beginRequest(eghCommonBeginRequest);
        Sys.WebForms.PageRequestManager.getInstance().add_endRequest(checkStatus);
    }

    jQuery('.repeater_trigger').each(function () {
        jQuery(this).data('initialValue', jQuery(this).val());
    });
}

function eghCommonInitializeRequest(sender, args) {

    var prm = Sys.WebForms.PageRequestManager.getInstance();
    var postbackElement = args.get_postBackElement();

    if (postbackElement != null) {
        var repeaterTrigger = jQuery(postbackElement);

        // sometimes the repeater trigger is the containing div rather than the select/input?
        if (!repeaterTrigger.hasClass('repeater_trigger')) {
            repeaterTrigger = repeaterTrigger.find('.repeater_trigger').first();
        }

        if (repeaterTrigger.hasClass('repeater_trigger')) {
            if (prm.get_isInAsyncPostBack()) {
                args.set_cancel(true);
                var before_change = repeaterTrigger.data('initialValue'); //get the pre data
                repeaterTrigger.val(before_change);
                jQuery('#' + repeaterTrigger.data('tryLoadLaterID')).show();
            }
            else {
                // show the progress indicator?
                repeaterTrigger.data('initialValue', repeaterTrigger.val());
            }
        }
    }
}

function eghCommonBeginRequest(sender, args) {

 //   debugger;

//    for (var i = 0; i < args._updatePanelsToUpdate.length; i++) {
//        alert(jQuery("#" + args._updatePanelsToUpdate[i]).html());
//        jQuery("#" + args._updatePanelsToUpdate[i]).hide(); // .attr('disabled', 'disabled');
//    }


}

function checkStatus(sender, args) {

    jQuery('.try_load_later').hide();

	if (args.get_error()) {
		args.set_errorHandled(true);

		var msg = "Sorry, something unexpected has occurred\r\n\r\n";
		msg += "Error code: " + args.get_error().httpStatusCode + "\r\n\r\n";
		msg += "Error message: " + args.get_error().message;

		// lazy way of closing all of the popup extenders
//		var extenders = new Array('UnearnedIncomeDialogExtender', 'PleaseWaitExtender', 'PrepopPersonalizationExtender', 'ContactOptionsExtender', 'MessageWindowExtender');
//		for (var i = 0; i < extenders.length; i++) {
//			var extender = $find(extenders[i]);
//			if (extender != undefined && extender != null) {
//				extender.hide();
//			}
//		}

		alert(msg);
	}
}

jQuery(window).on('load', function () {

	if (typeof (Page_Validators) != "undefined") {
		var i, val;
		for (i = 0; i < Page_Validators.length; i++) {
			val = Page_Validators[i];
			if (typeof (val.overrideevaluationfunction) == "string") {
				eval("val.evaluationfunction = " + val.overrideevaluationfunction + ";");
			}
		}
	}

	// configure client side event handlers etc (does not fire after async postback)
	if (validateOnLoad) {
		try {
			Page_ClientValidate();
			Page_BlockSubmit = false;
		}
		catch (error) {
			// if there are no validators on the page it errors when you  try and call Page_ClientValidate()
		}
	}
});

jQuery(document).ready(function () {

    // allows a hookin for when eGovHub forms are hosted in an iframe - needs a bit of refining, ideally needs to call a function  on the host page that can do whatever it needs to do
    if (window.parent != window) {
        jQuery("#form1").submit(function (event) {
            let suppress = false;
            if (event != null && event.originalEvent != null) {
                if (event.originalEvent.submitter != null) {
                    if (event.originalEvent.submitter.attributes != null) {
                        if (event.originalEvent.submitter.attributes["data-suppress-submit-post-message"] && event.originalEvent.submitter.attributes["data-suppress-submit-post-message"].value == "true") {
                            suppress = true;
                        }
                    }
                }
                else {
                    suppress = true;
                }
            }
            if (!suppress) {
                if (Page_ClientValidate("")) {
                    window.parent.postMessage("eGovHubFormSubmitting", "*");
                }
            }
        });
        jQuery("#form1").on("mouseenter mouseleave click keypress", function () {
            window.parent.postMessage("eGovHubInterrupt", "*");
        });
    }

    jQuery('.navigation-toggle').click(function () {
    	NavigationToggle(this);
    	return false;
    });

    jQuery('.dropdown.display-options select').click(function (e) {
    	e.stopPropagation();
    })

    jQuery('.close-design-tools').click(function () {
        if (!jQuery('.design-tools').hasClass('closed')) {
            jQuery('.design-tools').addClass('closed');
    	}
    	return false;
    });

    jQuery('.open-design-tools').click(function () {
        if (jQuery('.design-tools').hasClass('closed')) {
            jQuery('.design-tools').removeClass('closed');
    	}
    	return false;
    });
});

function NavigationToggle(toggle)
{
    if (jQuery('#pop-down-navigation > .navigation_panel_contents').length == 0) {
        let clonedContent = jQuery('.navigation_panel_contents').clone(true, true);
        jQuery('#pop-down-navigation').html(clonedContent);
	}

    let popDownNav = jQuery('#pop-down-navigation');

    if (jQuery(popDownNav).hasClass("hidden")) {
        jQuery('#pop-down-navigation').removeClass("hidden");
        jQuery(toggle).addClass('open')
        //jQuery('.navigation_panel_contents').slideToggle('slow', function () { return; });
        //jQuery('.menu').toggle();
        //jQuery('.title_and_body').toggle();
        //jQuery('.header').toggle();
    }
    else {
        jQuery('#pop-down-navigation').addClass("hidden");
        jQuery(toggle).removeClass('open')
        //jQuery('.menu').toggle();
        //jQuery('.toggle_navigation_hidden_text').hide();
        //jQuery('.toggle_navigation_shown_text').show();
        //jQuery('.title_and_body').toggle();
        //jQuery('.header').toggle();
        //jQuery('.navigation_panel_contents').slideToggle('slow', function () { return; });
    }
}

function translateAll(lang) {

    if (jQuery("div.translate").length > 0) {
        var findControl = jQuery("div.translate");

        for (var index = 0; index < findControl.length; index++) {
            recursiveReplace(findControl[index], lang);
        }
    }
}

function recursiveReplace(node, language) {

    if (node.nodeType == 3) { // text node
        control = node;
        translate(node, multiLineHtmlEncode(node.nodeValue), 'en', language);
    } else if (node.nodeType == 1) { // element
        $(node).contents().each(function () {
            recursiveReplace(this, language);
        });
    }
}

function multiLineHtmlEncode(value) {
    var lines = value.split(/\r\n|\r|\n/);
    for (var i = 0; i < lines.length; i++) {
        lines[i] = htmlEncode(lines[i]);
    }
    return lines.join('\r\n');
}

function htmlEncode(value) {
    return $('<div/>').text(value).html();
}

function translate(node, text, from, to) {

    var p = {};
    p.appid = "Bearer " + window.accessToken;
    p.to = to;
    p.from = from;
    p.text = text;
    $.ajax({
        url: 'http://api.microsofttranslator.com/V2/Ajax.svc/Translate',
        data: p,
        dataType: 'jsonp',
        jsonp: 'oncomplete'
    })
            .done(function (jqXHR, textStatus, errorThrown) {
                console.log('done', this, jqXHR, textStatus, errorThrown);
                // show the translation result to the user
                node.nodeValue = jqXHR
            });
}

function getUrlVars() {
	var vars = [], hash;
	var hashes = window.location.href.slice(window.location.href.indexOf('?') + 1).split('&');
	for (var i = 0; i < hashes.length; i++) {
		hash = hashes[i].split('=');

		var key = hash[0].toLowerCase()
		vars.push(key);
		vars[key] = hash[1];
	}
	return vars;
}

function SubmitClicked() {
    var valid = Page_ClientValidate("");
    if (valid) {
        $find('ProgressExtender').show();
        // IE stops animated gifs  from animating when the form is submitted so we need to force it
        ProgressImg = jQuery(".progress_image").get(0);
        setTimeout("ProgressImg.src = ProgressImg.src", 100);
        return true;
    }
    return false;
}

//function autoTab(event, field1, len, field2) {

//    if (document.getElementById(field1).value.length == len) {
//        document.getElementById(field2).focus();
//    }
//}

function autoTab(event, maxLength, nextField) {

    if (!(event.keyCode == "9" && event.shiftKey == true) && !(event.keyCode == "16") && !(event.keyCode == "9")
        && !(event.keyCode == "13") && !(event.keyCode == "17") && !(event.keyCode == "18")) {

        var parent = jQuery(event.explicitOriginalTarget).closest("div.question");
        var nextFieldSearch = "[id$=|fieldname|]".replace("|fieldname|", nextField);

        if (event.explicitOriginalTarget.value.length == maxLength) {
            $(parent).find(nextFieldSearch).focus();
        }

    }

};
Sys.Application.add_load(pageLogicLoaded);

function DesignerRuleTrigger() {
    this.ClientID = null;
    this.ControlValue = null;
    this.DecisionVariable = null;
}

function pageLogicLoaded() {
    jQuery(".page_logic_trigger").each(function (index, value) {
        if (value.tagName == "SELECT") {
            jQuery(value).change(function () {
                pageLogicTriggered(this);
            });
        }
        else {
            jQuery(value).find(":radio").click(function () {
                pageLogicTriggered(value);
            });
        }
    });
}

function pageLogic_evaluateRule(rule) {
    if (rule == null || rule.ifPart == null) {
        return;
    }

    var result = pageLogic_evaluateIfStatement(rule.ifPart);
    if (result == true) {
        return;
    }

    for (var i = 0; i < rule.elseIfParts.length; i++) {
        result = pageLogic_evaluateIfStatement(rule.elseIfParts[i]);
        if (result == true) {
            return;
        }
    }

    if (rule.elsePart != null) {
        result = pageLogic_evaluateElseStatement(rule.elsePart);
    }
}

function pageLogic_evaluateIfStatement(rulePart) {
    var leftSide = null;
    var rightSide = null;
    var workingResult = null;
    var result = null;

    for (var i = 0; i < rulePart.conditions.length; i++) {
        leftSide = pageLogic_getComparisonValue(rulePart.conditions[i].variable1);
        rightSide = pageLogic_getComparisonValue(rulePart.conditions[i].variable2);
        workingResult = pageLogic_compareValues(leftSide, rightSide, rulePart.conditions[i].operator);

        if (i == 0) {
            result = workingResult;
        }
        else {
            switch (rulePart.conditions[i].keyword) {
                case "and":
                    result = result && workingResult;
                    break;

                case "or":
                    result = result || workingResult;
                    break;
            }
        }
    }

    if (result == true) {
        pageLogic_logMessage('Rule is true');
        pageLogic_logMessage(rulePart);

        // toggle controls
        pageLogic_processRuleActions(rulePart);

        // evaluate nested rules
        pageLogic_evaluateRule(rulePart.nestedRule);
    }
    else {
        pageLogic_logMessage('Rule is false');
        pageLogic_logMessage(rulePart);
    }

    return result;
}

function pageLogic_compareValues(leftSide, rightSide, operator) {
    var result = false;
    switch (operator) {
        case "==":
            result = (leftSide == rightSide);
            break;

        case "!=":
            result = (leftSide != rightSide);
            break;
    }
    return result;
}

function pageLogic_evaluateElseStatement(rulePart) {
    pageLogic_processRuleActions(rulePart);

    // evaluate nested rules
    pageLogic_evaluateRule(rulePart.nestedRule);
}

function pageLogic_getComparisonValue(variable) {
    var value = "";
    switch (variable.sourceType.toLowerCase()) {
        case "fixed":
            value = variable.fixedValue;
            break;
        case "currentpageoption":
            value = variable.fixedValue;
            break;
        case "currentpagequestion":
            value = pageLogic_getControlValue(variable.clientID);
            break;
    }
    return value;
}

function pageLogic_logMessage(message) {
    try {
        console.info(message);
    }
    catch (ex) { }
}

function pageLogic_processRuleActions(rulePart) {
    if (rulePart.actions == null) {
        return;
    }

    for (var i = 0; i < rulePart.actions.length; i++) {
        var action = rulePart.actions[i];
        if (action.name == "show") {
            for (var j = 0; j < action.data.length; j++) {
                pageLogic_showControl(action.data[j].clientID);
            }
        }
        else if (action.name == "hide") {
            for (var j = 0; j < action.data.length; j++) {
                pageLogic_hideControl(action.data[j].clientID);
            }
        }
        else if (action.name == "hidePages") {
            for (var j = 0; j < action.pageData.length; j++) {
                pageLogic_hidePage(action.pageData[j].uniqueID);
            }
        }
    }
}

function pageLogic_hideControl(controlId) {
    var ctrl = $("#" + controlId);

    ctrl.hide();

    jQuery(ctrl).find("*").hide();
    jQuery(ctrl).find("*").attr("enabled", false);
    jQuery(ctrl).find("*").attr("disabled", true);

    jQuery(ctrl).find("span").each(function () {
        if (this.evaluationfunction != undefined) {
            ValidatorEnable(this, false);
        }
    });
}

function pageLogic_showControl(controlId) {
    var ctrl = $("#" + controlId);

    ctrl.attr("enabled", true);
    ctrl.attr("disabled", false);
    ctrl.removeClass('aspNetDisabled');
    ctrl.show();

    jQuery(ctrl).find("*").not('.readonly_exempt').removeClass('aspNetDisabled');
    jQuery(ctrl).find("*").not('.readonly_exempt').show();
    jQuery(ctrl).find("*").not('.readonly_exempt').attr("enabled", true);
    jQuery(ctrl).find("*").not('.readonly_exempt').attr("disabled", false);

    jQuery(ctrl).find("span").each(function () {
        if (this.evaluationfunction != undefined) {
            ValidatorEnable(this, true);
        }
    });
}

function pageLogic_hidePage(pageId) {
    jQuery(".navigation_item").each(function () {
        if (jQuery(this).attr("data-PageName") == pageId) {
            jQuery(this).hide();
        }
    });
}

function pageLogic_getControlValue(controlId) {
    var control = $("#" + controlId);
    if (control.length == 0) {
        return null;
    }

    if (control[0].tagName == "SELECT" || control[0].tagName == "INPUT") {
        return control.val();
    }

    // control id is usually for a parent control e.g. containing div, so need to look inside
    var select = control.find("select");
    if (select.length > 0) {
        return select.val();
    }

    var input = control.find("input");
    if (input.length > 0) {
        for (var i = 0; i < input.length; i++) {
            // ignore buttons (e.g. inline help buttons)
            if (input[i].type != "submit" && input[i].type != "button") {
                if (input[i].type == "radio") {
                    if (input[i].checked) {
                        return input[i].value;
                    }
                }
                else {
                    return input.value;
                }
            }
        }
    }

    // TODO: checkboxes, and checkbox lists?

    return null;
}

function pageLogicTriggered(sender) {
    if (ieg4_page_logic_mode == 'client') {

        // page rules only hide, so need to show all pages in nav bar before rules run, as eligible pages previously hidden won't be toggled back on without this,
        jQuery(".navigation_item").each(function () {
            jQuery(this).show();
        });

        var pageRules = ieg4_page_rules;
        for (var i = 0; i < pageRules.length; i++) {
            pageLogic_evaluateRule(pageRules[i]);
        }
    }
    else {
        var triggers = new Array();

        jQuery(".page_logic_trigger").each(function (index, value) {
            var trigger = jQuery(value);

            var r = new DesignerRuleTrigger();

            if (value.tagName == "SELECT") {
                r.ControlValue = trigger.val();
            }
            else {
                //if (child != null) {
                // so this would get the child control but for everything on the page which is a radio button 
                //r.ControlValue = jQuery(child).val();

                //only get controlvalue for checked controls
                //you dont need to know about the others as the above code will do for dropdowns already
                //this will loop though looking for the "checked" control and set the controlvalue based on the value from html
                //backend then does a script which should only trigger the correct elements based on guid vs pagelogic
                //jQuery('#' + trigger.context.id + ' input').each(function (item) {
                jQuery('#' + trigger[0].id + ' input').each(function (item) {
                    if (jQuery(this).is(':checked')) {
                        r.ControlValue = jQuery(this).val();
                    }

                });
                //};
            }
            r.ClientID = trigger.parents(".page_logic_trigger_container").first().attr("id");
            //r.DecisionVariable = JSON.parse(trigger.data("designer_decision_variable"));

            triggers.push(r);

            console.log('New Rule to services : ' + JSON.stringify(triggers));
        });

        var postPage = { pageID: ieg4_eGovHub_pageId }

        jQuery.ajax({
            type: "POST",
            url: "legacy-services/DesignerRuleTriggered/" + ieg4_eGovHub_pageId,
            //data: "{'triggers':" + JSON.stringify(pageData) + ",'pageID':'" + ieg4_eGovHub_pageId + "'}", // postData, //'{"somedata":"aaa"}', ;'pageID':" + ieg4_eGovHub_pageId + "
            data: JSON.stringify(triggers),

            contentType: "application/json; charset=utf-8",
            dataType: "json",
            success: function (data, textStatus, jqXHR) {

                console.log("post success" + JSON.stringify(data));

                //first show all the links for the applicable pages in the navbar
                jQuery(".navigation_item").each(function () {
                    jQuery(this).show();
                });

                if (data != null) {

                    for (var i = 0; i < data.length; i++) {

                        for (var j = 0; j < data[i].showControls.length; j++) {

                            var ctrl = jQuery("#" + data[i].showControls[j].clientID);

                            ctrl.attr("enabled", true);
                            ctrl.attr("disabled", false);
                            ctrl.removeClass('aspNetDisabled');
                            ctrl.show();

                            jQuery(ctrl).find("*").not('.readonly_exempt').removeClass('aspNetDisabled');
                            jQuery(ctrl).find("*").not('.readonly_exempt').show();
                            jQuery(ctrl).find("*").not('.readonly_exempt').attr("enabled", true);
                            jQuery(ctrl).find("*").not('.readonly_exempt').attr("disabled", false);

                            jQuery(ctrl).find("span").each(function () {
                                if (this.evaluationfunction != undefined) {
                                    ValidatorEnable(this, true);
                                }
                            });
                        }

                        for (var j = 0; j < data[i].hideControls.length; j++) {

                            var ctrl = jQuery("#" + data[i].hideControls[j].clientID);

                            ctrl.hide();

                            jQuery(ctrl).find("*").hide();
                            jQuery(ctrl).find("*").attr("enabled", false);
                            jQuery(ctrl).find("*").attr("disabled", true);

                            jQuery(ctrl).find("span").each(function () {
                                if (this.evaluationfunction != undefined) {
                                    ValidatorEnable(this, false);
                                }
                            });
                        }

                        //hide any navbar items that should be hidden
                        for (var j = 0; j < data[i].hidePages.length; j++) {

                            jQuery(".navigation_item").each(function () {
                                var p = jQuery(this).attr("data-PageName");

                                if (jQuery(this).attr("data-PageName") == data[i].hidePages[j].uniqueID) {
                                    jQuery(this).hide();
                                }
                            });

                            var p = data[i].hidePages[j].uniqueID
                            var page = jQuery("." + data[i].hidePages[j].uniqueID);

                        }

                    }
                }
            },

            error: function (xhr, ajaxOptions) {
                /*alert(xhr.status);*/
                alert(xhr.responseText);
            }
        });
    }
};
jQuery(document).ready(function () {
	jQuery('.contrast_option').click(function () {

		var style = jQuery(this).attr("rel");
		switchStyle(style, 'contrast_css');

		return false;
	});

	jQuery('.text_size_option').click(function () {

		var style = jQuery(this).attr("rel");
		switchStyle(style, 'textsize_css');

		return false;
	});

	var c = readCookie('contrast_css');
	if (c) switchStyle(c, 'contrast_css');

	c = readCookie('textsize_css');
	if (c) switchStyle(c, 'textsize_css');
});

function switchStyle(styleName, styleId) {
	
	jQuery('head link[id=' + styleId + ']').remove();

	if (styleName != "default") {
		// need a special case for ie7 - without this it takes about a minute to refresh the apearance based on the style
		//if (jQuery.browser.msie && parseInt(jQuery.browser.version, 10) <= 7) { 
		//	document.createStyleSheet('stylesheets/' + styleName);
		//	document.documentElement.firstChild.lastChild.id = styleId;	
		//}
		//else {
			jQuery('head').append('<link rel="stylesheet" id="' + styleId + '" href="/content/' + styleName + '" type="text/css" rel="stylesheet" />');
		//}	
	}

	createCookie(styleId, styleName, 365);
}

function createCookie(name, value, days) {
	if (days) {
		var date = new Date();
		date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
		var expires = "; expires=" + date.toGMTString();
	}
	else var expires = "";
	document.cookie = name + "=" + value + expires + "; path=/";
}

function readCookie(name) {
	var nameEQ = name + "=";
	var ca = document.cookie.split(';');
	for (var i = 0; i < ca.length; i++) {
		var c = ca[i];
		while (c.charAt(0) == ' ') c = c.substring(1, c.length);
		if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length, c.length);
	}
	return null;
};
function pageLoad() {
	// remove the aspnet onsubmit postback function, and also trigger validation
	jQuery('.btn-q-help').removeAttr("onclick");

	jQuery('.btn-q-help').click(function () {
		let fieldId = jQuery(this).data('field-id');
		let field = jQuery('#' + fieldId);
		let innerTemplate = '';
		let resourceKey = jQuery(this).data('resource-key');
		let editMode = jQuery(this).data('edit-mode');

		if (resourceKey && resourceKey.length > 0 && editMode && editMode.toLowerCase() == "standard") {
			innerTemplate = '<div class="tooltip-inner editable" onclick="window.appController.editorController.openWordingEditor(\'' + resourceKey + '\')"></div>'
		}
		else {
			innerTemplate = '<div class="tooltip-inner"></div>'
		}

		let template = '<div class="tooltip" role="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-outer"><i class="fa fa-times-circle" onclick="closeToolTip(\'' + jQuery(this).attr('id') + '\', \'' + fieldId + '\')"></i>' + innerTemplate + '</div><div>'

		if (jQuery(this).hasClass('tooltip-open')) {
			jQuery(field).tooltip('hide');
			jQuery(this).removeClass('tooltip-open');
		}
		else {
			jQuery(field).tooltip({ template: template }).tooltip('show');
			jQuery(field).tooltip({ template: template }).tooltip('show');
			jQuery(this).addClass('tooltip-open');
		}
		
		return false;
	});
}

function closeToolTip(btnId, fieldId) {
	let field = jQuery('#' + fieldId);
	jQuery(field).tooltip('hide');

	let btn = jQuery('#' + btnId);
	jQuery(btn).removeClass('tooltip-open');
};
var addressFields_ErrorMessageId;
var addressFields_ProgressTextId;
var addressFields_ProgressImageId;

var addressFields_ResultGridButtonsClicked;

Sys.Application.add_load(ApplicationLoadHandler)

function ApplicationLoadHandler(sender, args) {
    Sys.WebForms.PageRequestManager.getInstance().add_endRequest(CheckStatus);
    InitializeAddressFieldControls();
}

function CheckStatus(sender, args) {
    if (args.get_error() && args.get_error().name == 'Sys.WebForms.PageRequestManagerTimeoutException') {
        args.set_errorHandled(true);
        $get(addressFields_ErrorMessageId).innerHTML = "Sorry, the call to the address lookup service has timed out. Please close the window and try again.";
        $get(addressFields_ErrorMessageId).style.display = "block";
        $get(addressFields_ProgressTextId).style.display = "none";
        $get(addressFields_ProgressImageId).style.display = "none";
        $get(addressFields_ChosenTextId).style.display = "none";
    }
}

function CancelAddressSearch() {
    var prm = Sys.WebForms.PageRequestManager.getInstance();
    if (prm.get_isInAsyncPostBack()) {
        prm.abortPostBack();
        return false;
    }
}

var AddressFieldControls = [];


jQuery(document).ready(function () {
    InitializeAddressFieldControls();
});

function InitializeAddressFieldControls() {
    var GetAddressFieldControls = $('.ieg4_address_fields_control_webapi');

    if (GetAddressFieldControls && GetAddressFieldControls.length > 0) {
        AddressFieldControls = [];
        GetAddressFieldControls.each(function (index) {
            let control = new AddressFieldsControl(this, index);
            AddressFieldControls.push(control);
        });
    }
}

function AddressFieldsControl(controlHtml, index) {

    this.controlHtml = controlHtml;

    let style = $(controlHtml).data("address-lookup-style");
    this.style = style;

    if (style == "AddressLineAndPostcode") {
        let addLineSearchText = $(controlHtml).find(".add_line_search_text_box input");
        this.addLineSearchText = addLineSearchText[0];
    }

    let searchText = $(controlHtml).find(".search_text_box input");
    this.searchText = searchText[0];

    let searchButton = $(controlHtml).find(".button.postcode-search");
    this.searchButtton = searchButton[0];
    this.searchButtonText = searchButton.html();

    let addressResultContainer = $(controlHtml).find(".address-result-container");
    this.addressResultContainer = addressResultContainer[0];

    let addressResult = $(controlHtml).find(".address-result");
    this.addressResult = addressResult[0];

    this.searchResults = [];

    let type = $(controlHtml).data("address-lookup-type");
    this.type = type;

    if (style == null || style == "TypeAheadSearch") {
        $(this.searchText).keyup(function (e) {
            queryChanged(index, type, e);
        });
    }
    else if (style == "Postcode") {
        $(this.searchButtton).click(function (e) {
            queryChanged(index, type, e, searchButton);
        });
    }
    else if (style == "AddressLineAndPostcode") {
        $(this.searchButtton).click(function (e) {
            queryChanged(index, type, e, searchButton);
        });
    }
}


function queryChanged(controlIndex, type, e, searchButton) {
    //$('#addlookupDown').hide();
    //$('#addlookupUp').hide();
    //$('#addlookUpWorking').show();

    $("results_list").show();
    let text = $(AddressFieldControls[controlIndex].searchText).val();
    let addressLine = $(AddressFieldControls[controlIndex].addLineSearchText).val();

    if (text && text.length > 0) {
        addressPartialLookup(text, addressLine, controlIndex, type, null, searchButton);
        console.log(text);
    }
}

function addressPartialLookup(query, addressLine, controlIndex, type, containerId, searchButton) {
    var finalQuery = query;
    //finalQuery = finalQuery.replace(/\//g, '$FS$');
    //finalQuery = finalQuery.replace(/\\/g, '$BS$');
    //finalQuery = encodeURIComponent($.trim(finalQuery))
    //var url = 'api/address-lookup/addresspartiallookup/' + encodeURIComponent($.trim(finalQuery)) + '/';

    var dto = {
        provider: type,
        searchQuery: query,
        addressLine: addressLine,
        containerId: containerId,
        addressId: null
    }

    // need this to work with product forms where form type is not part of url, but form type is needed for the context of the api call
    let formType = document.querySelector('#_FormType').value;
    var url = '/' + formType + '/api/address-lookup/addresspartiallookup/query/';

    if (containerId != null) {
        url += 'container';
    }

    if (searchButton != null) {
        searchButton.prop("disabled", true);
        searchButton.html('Searching...')
    }

    $.ajax({
        type: "POST",
        url: url,
        data: dto,
        success: function (result) {
            if (searchButton != null) {
                searchButton.prop("disabled", false);
                searchButton.html(AddressFieldControls[controlIndex].searchButtonText);
            }
            AddressFieldControls[controlIndex].searchResults = result;
            generateHtmlResult(controlIndex);
        },
        error: function (e) {
            if (searchButton != null) {
                searchButton.prop("disabled", false);
                searchButton.html(AddressFieldControls[controlIndex].searchButtonText);
            }
            errorHtmlResult();
            //console.log(e);
        }
    });
}

function generateHtmlResult(controlIndex) {
    if (AddressFieldControls[controlIndex].searchResults.length === 0) {
        console.log('no results');
        return;
    }
    else {
        console.log(AddressFieldControls[controlIndex].searchResults);
    }

    $(AddressFieldControls[controlIndex].addressResultContainer).slideDown();

    let output = '<ul class="results_list list-group">';
    for (let i = 0; i < AddressFieldControls[controlIndex].searchResults.length; i++) {
        output += '<li class="results_list_item list-group-item"><a href="#" target="_self" onclick="return AddUpSearchResultClicked(' + controlIndex + ',' + i + ');">';
        output += AddressFieldControls[controlIndex].searchResults[i].text;

        let description = AddressFieldControls[controlIndex].searchResults[i].description;
        if (description != null && description.length > 0) {
            output += ' , ' + AddressFieldControls[controlIndex].searchResults[i].description;
        }

        output += '</a></li>';
    }
    output += '</ul>';

    $(AddressFieldControls[controlIndex].addressResult).html(output);
}


function AddUpSearchResultClicked(controlIndex, resultIndex) {
    let selectedResult = AddressFieldControls[controlIndex].searchResults[resultIndex];
    $("results_list").hide();
    //let type = AddressFieldControls[controlIndex].searchResults[resultIndex].type;
    //let addressId = AddressFieldControls[controlIndex].searchResults[resultIndex].id;
    selectedAddressResult(controlIndex, selectedResult);

    $(AddressFieldControls[controlIndex].addressResult).html('');

}


function selectedAddressResult(controlIndex, selectedResult) {
    if (selectedResult.fields != null) {
        selectedAddress(controlIndex, selectedResult.fields);
    }
    else if (selectedResult.type == "Address") {
        addressRetrieve(controlIndex, selectedResult.id);
    }
    else {
        let addressFieldsControl = AddressFieldControls[controlIndex];
        let query = $(addressFieldsControl.searchText).val();
        addressPartialLookup(query, controlIndex, addressFieldsControl.type, selectedResult.id);
        console.log('not address type');
    }
}


function addressRetrieve(controlIndex, id) {
    var dto = {
        provider: 'addupapi',
        searchQuery: null,
        addressLine: null,
        containerId: null,
        addressId: id
    }

    let formType = document.querySelector('#_FormType').value;
    let url = '/' + formType + '/api/address-lookup/addressretrieve/id';

    $.ajax({
        url: url,
        type: "POST",
        data: dto,
        success: function (result) {
            //console.log(result);
            selectedAddress(controlIndex, result);
        },
        error: function (e) {
            errorHtmlResult();
            //console.log(e);
        }
    });
}


function selectedAddress(controlIndex, address) {

    if (address.line1 && address.line1.length > 0) {
        $(AddressFieldControls[controlIndex].controlHtml).find("div[id$='AddressLine1'] input").val(address.line1);
    }

    if (address.line2 && address.line2.length > 0) {
        $(AddressFieldControls[controlIndex].controlHtml).find("div[id$='AddressLine2'] input").val(address.line2);
    }

    if (address.line3 && address.line3.length > 0) {
        $(AddressFieldControls[controlIndex].controlHtml).find("div[id$='AddressLine3'] input").val(address.line3);
    }

    if (address.city && address.city.length > 0) {
        $(AddressFieldControls[controlIndex].controlHtml).find("div[id$='AddressLine4'] input").val(address.city);
    }

    if (address.postalCode && address.postalCode.length > 0) {
        $(AddressFieldControls[controlIndex].controlHtml).find("div[id$='Postcode'] input").val(address.postalCode);
    }

    if (address.uprn && address.uprn.length > 0) {
        $(AddressFieldControls[controlIndex].controlHtml).find("input[id$='PropertyUniqueReference']").val(address.uprn);
    }

    if (address.systemId && address.systemId.length > 0) {
        $(AddressFieldControls[controlIndex].controlHtml).find("input[id$='PropertySystemId']").val(address.systemId);
    }

    if (address) {
        $(AddressFieldControls[controlIndex].controlHtml).find("input[id$='WebApiSearchResultsModeSelectedAddress']").val(JSON.stringify(address));
    }

    $(AddressFieldControls[controlIndex].addressResultContainer).slideUp();
    $(AddressFieldControls[controlIndex].searchText).val("");

    if (AddressFieldControls[controlIndex].style == "AddressLineAndPostcode") {
        $(AddressFieldControls[controlIndex].addLineSearchText).val("");
    }
}

function errorHtmlResult() {
    $(".address-result-container").slideDown();

    $(".address-result").empty();

    $(".address-container").slideDown();

    var newdiv1 = $("<a class='list-group-item list-group-item-action'>We are really sorry, something has gone wrong</a>")
    $(".address-result").append(newdiv1);
}

if (typeof (Sys) !== "undefined") Sys.Application.notifyScriptLoaded();;
var activeCalls = 0;

jQuery(document).ready(function () {

	jQuery(".toggle_errors").click(function () {
	    if (jQuery(this).hasClass('popover-open')) {
	        jQuery(this).removeClass('popover-open');
	        jQuery(this).popover('destroy');
		}
		else {
	        jQuery(this).addClass('popover-open');

	        let errors = jQuery(this).siblings('.navigation_validation_tip');
	        let title = jQuery(errors).find('.header');
	        let content = jQuery(errors).find('.content');
			let template = '<div class="popover validation-errors" role="tooltip"><div class="arrow"></div><h3 class="popover-title"></h3><div class="popover-content"></div></div>'

			jQuery(this).popover({ trigger: 'manual', content: content.html(), html: true, title: title.text(), placement: 'bottom', container: 'body', template: template }).popover('show');
		}

		return false;
	});

	// Add the page method call as an onclick handler for the div.
	jQuery(".page_trigger select").change(function () {
		UpdateNavigation(this);
	});

	jQuery(".page_trigger :radio").click(function () {
		UpdateNavigation(this);
	});
});

function UpdateNavigation(trigger) {

	jQuery("div.navigation_progress").show();

	var results = new Array();

	jQuery(".page_trigger select").add(".page_trigger :radio[checked]").each(function () {

		var o = new Object()
		o.PropertyName = jQuery(this).data('PropertyName');
		o.PropertyValue = jQuery(this).val();
		results.push(o);
	});

	activeCalls++;


	var ctx = getUrlVars()["ctx"];

	jQuery.ajax({
		type: "POST",
		url: "Services.aspx/CalculatePages?ctx=" + ctx,
		data: "{'triggers':" + JSON.stringify(results) + "}", // postData, //'{"somedata":"aaa"}',
		contentType: "application/json; charset=utf-8",
		dataType: "json",
		success: function (data, textStatus, jqXHR) {

			activeCalls--;

			if (activeCalls == 0) {
				jQuery("div.navigation_progress").hide();
			}

			if (data.d != null) {
				var applicablePages = data.d;

				jQuery(".navigation_item").each(function () {
					//alert(jQuery(this).data('PageName'));
					if (jQuery.inArray(jQuery(this).data('PageName'), applicablePages) > -1) {

						if (jQuery(this).data('InitialColor') == null) {
							jQuery(this).data('InitialColor', jQuery(this).css("backgroundColor"));
						}

						if (jQuery(this).data('FadeInColor') == null) {
							jQuery(this).addClass("navigation_item_dynamic");
							jQuery(this).data('FadeInColor', jQuery(this).css("backgroundColor"));
							jQuery(this).removeClass("navigation_item_dynamic");
						}

						jQuery(this).css({ backgroundColor: jQuery(this).data('FadeInColor') });

						jQuery(this).fadeIn(1000, function () {
							try {
								if (jQuery(this).data('InitialColor').toLowerCase() != "transparent") {
									jQuery(this).animate({ backgroundColor: jQuery(this).data('InitialColor') }, 500);
								}
								jQuery(this).css({ backgroundColor: '' });
							}
							catch (x) {
								jQuery(this).css({ backgroundColor: jQuery(this).data('InitialColor') });
							}

						});
						if (jQuery(this).data('InitialColor').toLowerCase() == "transparent") {
							jQuery(this).css({ backgroundColor: jQuery(this).data('InitialColor') });
						}

					} else {
						jQuery(this).fadeOut(600)
					}
				});
			}
		},

		error: function (xhr, ajaxOptions) {
			activeCalls--;

			if (activeCalls == 0) {
				jQuery("div.navigation_progress").hide();
			}


			/*alert(xhr.status);*/

            // really strange error, only seen on the memset hosted server? where you get a really intermittent error
            // seems to be  Activator.CreateInstance line in the PageFlowManager constructor
            // alert(xhr.responseText);


		}
	});
}

//var _validationErrors = false;

//function FormValidate() {
//	var valid = Page_ClientValidate();

//	// need to make sure the page is valid to stop the progress dialog displaying when navigating backwards
//	if (valid) {
//		_submitClicked = true;
//	}
//	else {
//		if (!_validationErrors) {
//			// show the list of pages that require further action
//			$find('FailedValidationDialogExtender').show();
//		}
//	}
//}

function FormIsValid(sender, args) {
    if (_formIsValid === false) {
        args.IsValid = false;
        _validationErrors = true;

        // show the list of pages that require further action
        $find('FailedValidationDialogExtender').show();
    }
	else {
		args.IsValid = true;
		_validationErrors = false;
	}
};
function ControlValueChanged(contextParameter, compareTo, disableOrHide, controlsToDisable, compareOperator, innerTriggerControls, compareSourceType)
{	
	var readOnly = false;
	
	switch (compareSourceType)
	{
		case "control" :
			
			// get the control that will trigger the read only
			var control = document.getElementById(contextParameter);
			
			if (control == null)
			{
				return;
			}

			// get the controls value, this may be a sub element of the passed in control...
			var controlValue = GetControlValueRecursive(control);

			// see if we have a match. It's important that we pass in the controls value as the second argument
			readOnly = CompareValues(compareTo, controlValue, compareOperator);
			
			break;
			
		case "previouspagecontrol" :
		
			// get the array of previous page values and see if any match
			for (var i=0; i<contextParameter.length; i++)
			{
				readOnly = CompareValues(compareTo, contextParameter[i], compareOperator);
				
				if (readOnly)
				{
					break;
				}
			}
		
			break;
	}
	
	if (!readOnly)
	{
		// we're enabling
		ChangeControlsReadOnlyState(readOnly, disableOrHide, controlsToDisable)
		for (var i=innerTriggerControls.length-1; i>=0; i--)
		{
			var f = eval("window." + innerTriggerControls[i]);
			eval(f);
		}
	}
	else
	{
//		// we're disabling
//		for (var i=0; i<innerTriggerControls.length; i++)
//		{
//			var f = eval("window." + innerTriggerControls[i]);
//			eval(f);
//		}
		ChangeControlsReadOnlyState(readOnly, disableOrHide, controlsToDisable)
	}
}

function GetControlValueRecursive(control)
{
	var controlValue;

	if (control.valuearray != undefined && control.valuecount != undefined) {
		var valueArray = eval(control.valuearray);

		// it should be a checkboxlist or radiobuttonlist
		var controls = jQuery("#" + control.id).find(":checkbox, :radio");

		for (var i = 0; i < controls.length; i++) {
			if (controls[i].checked) {
				// get the value of this checkbox
				return valueArray[i];
			}
		}
	}
	else if (control.checked != undefined) {
			controlValue = control.checked;
			return controlValue;
	}
	else if (control.value != undefined)
	{
		controlValue = control.value;	
		return controlValue;	
	}
	else
	{
		for (var i=0; i<control.childNodes.length; i++)
		{
			controlValue = GetControlValueRecursive(control.childNodes[i])
			if (controlValue != null)
			{
				return controlValue;	
			}
		}
	}
}

function CompareValues(value1, value2, compareOperator)
{
	var numericValue1
	var numericValue2
	var numeric = false;
	
	if (value1 == null)
	{
		value1 = "";
	}
	value1 = value1.toString()
	if (value2 == null)
	{
		value2 = "";
	}
	value2 = value2.toString()
	
	// Determine if the values are numeric or alphanumeric
	numericValue1 = parseFloat(value1);
	if (!(isNaN(numericValue1)))
	{
		numeric = true;
	}
	if (numeric)
	{
		numericValue2 = parseFloat(value2);
		if (isNaN(numericValue2))
		{
			numeric = false;
		}
		
	}
	
	// Perform comparison based on the operator passed in
	switch (compareOperator.toLowerCase())
	{
		case "equal" : 
			if (numeric) 
				return (numericValue1 == numericValue2);
			else
				return (value1 == value2);

		case "greaterthan" : 
			if (numeric)
				return (numericValue1 > numericValue2);
			else
				if (value2.length == 0)
					return false;
				else if (value1 == value2)
					return true;
				else
					return (value1 > value2);

		case "greaterthanorequal" : 
			if (numeric)
			{
				return (numericValue1 >= numericValue2);
			}
			else
				return (value1 >= value2);

		case "lessthan" : 
			if (numeric)
				return (numericValue1 < numericValue2);
			else
				if (value2.length == 0)
					return true;
				else if (value1 == value2)
					return true;
				else
					return (value1 < value2);

		case "lessthanorequal" : 
			if (numeric)
				return (numericValue1 <= numericValue2);
			else
				if (value2.length == 0 && value1.length > 0)
					return true;
				else
					return (value1 <= value2);

		case "notequal" : 
			if (numeric)
				return (numericValue1 != numericValue2);
			else
				return (value1 != value2);

		case "startswith" :
			if (value2.length == 0)
				return false;
			else
				return (value2.indexOf(value1) == 0);

		case "endswith" :
			if (value1.length > value2.length)
			{
				return false;
			}
			else
			{
				return (value2.lastIndexOf(value1) == (value2.length - value1.length));
			}

		case "notstartswith":
			if (value2.length == 0)
				return false;
			else
				return (!(value2.indexOf(value1) == 0));

		case "notendswith":
			if (value1.length > value2.length) {
				return false;
			}
			else {
				return (!(value2.lastIndexOf(value1) == (value2.length - value1.length)));
			}

		default :
			return false;
	}
}

function ChangeControlsReadOnlyState(readOnly, disableOrHide, controlsToDisable)
{
	for (var i=0; i<controlsToDisable.length; i++)
	{
		var control = document.getElementById(controlsToDisable[i])
		
		DisableControlsRecursive(control, readOnly, disableOrHide);
	}
}

function DisableControlsRecursive(control, readOnly, disableOrHide)
{
	if (control == null)
	{
		return;
	}

	// if (control.id != undefined && control.id != "" && jQuery("#" + control.id).is(".readonly_exempt"))
	if (jQuery(control).is(".readonly_exempt")) 
	{
		return;
	}
	
	// always diasable / enable
	try
	{
		//if (control.disabled != undefined) {
			control.disabled = readOnly;
		//}
	}
	catch(e) {}

	if (!readOnly) {
		jQuery(control).removeClass('aspNetDisabled');
	}

	// this will turn off any client side validators that have been hidden / disabled
	try
	{
		//if (control.enabled != undefined) {
			control.enabled = !readOnly;
		//}
	}
	catch(e) {}
	
	if (disableOrHide.toLowerCase() == "hide")
	{	
		if (readOnly)
		{
			try
			{
				if (control.style != undefined)
				{ 
					control.style.display = "none";
				}
			}
			catch(e) {}
		}
		else
		{
			try
			{
				if (control.style != undefined)
				{ 
					// if we set this to block in firefox then elements appear underneath each other 
					// when re-displayed. If set to inline the element appears to move down a few pixels 
					// (firefox only)
					control.style.display = "";
				}
			}
			catch(e) {}
		}
	}

//	if (!readOnly) {
//		// the control may be a standard asp.net validator or a validator from the eFormStudio standard toolbox
//		// if so, the error message for the validator will be shown regardless of the value of the control.
//		if (control.tagName && control.tagName.toUpperCase() == "SPAN") {
//			if (Page_Validators) {
//				for (var i = 0; i < Page_Validators.length; i++) {
//					if (Page_Validators[i].id == control.id) {
//						ValidatorValidate(Page_Validators[i], null, null);
//						break;
//					}
//				}
//			}
//		}
//	}
	
	for (var i=0; i<control.childNodes.length; i++)
	{
		DisableControlsRecursive(control.childNodes[i], readOnly, disableOrHide);
	}
};
function IEG4_Field_EvaluateIsValid(sender, args)
{
	// get the checkbox
	var control = document.getElementById(sender.controltovalidate);
	
	// if the control doesn't exist pass validation
	if (!control) 
	{
		args.IsValid = true;
		return;
	}
	
	// if there is no error message property, create one
	if (!sender.errormessage)
	{
		sender.setAttribute("errormessage", "")
	}
	
	// get the value to validate
	var value = control.value;

	// trim the string			
	while (value.substring(0,1) == ' ')
	{
		value = value.substring(1, value.length);
	}
	while (value.substring(value.length-1, value.length) == ' ')
	{
		value = value.substring(0, value.length-1);
	}
	
	// set the trimmed value back to be the controls value so long as the control is an input
	if (control.tagName.toUpperCase() == "INPUT")
	{
		control.value = value;
	}

	// if we've got a non blank error message then the field is required
	if (sender.requirederrormessage.length > 0)
	{
		// if no value has been entered or the value equals the initial value
		if (value.length == 0 || value.toUpperCase() == sender.initialvalue.toUpperCase())
		{
			// nothing has been entered so set the error message, this could be text or an image
			if (sender.requirederrorimage && sender.requirederrorimage != "")
			{
				sender.innerHTML = "<img src=\"" + sender.requirederrorimage.replace("~/", "") + "\" alt=\"" + sender.requirederrormessage + "\" title=\"" + sender.requirederrormessage + "\"  />";
			}
			else
			{
				sender.innerHTML = sender.requirederrormessage;
			}
			
			// also set the error message property so the message is shown in any summaries
			sender.errormessage = sender.requirederrormessage;
			
			IEG4_AddFailedValidationClassToControl(control);

			// fail validation
			args.IsValid = false;
			return;
		}
	}

    // if no value has been entered or the value equals the initial value
	if (value.length > 0) {
	    if (value.length < sender.minlength) {

	        // set the invalid error message, this could be text or an image
	        if (sender.invaliderrorimage && sender.invaliderrorimage != "") {
	            sender.innerHTML = "<img src=\"" + sender.invaliderrorimage.replace("~/", "") + "\" alt=\"" + sender.invaliderrormessage + "\" title=\"" + sender.invaliderrormessage + "\" />";
	        }
	        else {
	            sender.innerHTML = sender.invaliderrormessage;
	        }

	        // also set the error message property so the message is shown in any summaries
	        sender.errormessage = sender.invaliderrormessage;

	        IEG4_AddFailedValidationClassToControl(control);
 
	        // fail validation
	        args.IsValid = false;
	        return;
	    }
	}
	
	// if some data has been entered and it's not the initial value, test it passes validation
	if (value.length > 0 && value.toUpperCase() != sender.initialvalue.toUpperCase())
	{
		// we only need to do this fields that can be invalid e.g. drop down lists can't contain invalid items
		// and so will have no customclientvalidationfunction property
		if (sender.customclientvalidationfunction && sender.customclientvalidationfunction != "")
		{
			var valid = eval(sender.customclientvalidationfunction + "(\"" + value.replace("\"", "\\\"") + "\", '" + sender.controltovalidate + "', sender)");
			
			if (!valid)
			{
				// set the invalid error message, this could be text or an image
				if (sender.invaliderrorimage && sender.invaliderrorimage != "")
				{
					sender.innerHTML = "<img src=\"" + sender.invaliderrorimage.replace("~/", "") + "\" alt=\"" + sender.invaliderrormessage + "\" title=\"" + sender.invaliderrormessage + "\" />";
				}
				else
				{
					sender.innerHTML = sender.invaliderrormessage;
				}
				
				// also set the error message property so the message is shown in any summaries
				sender.errormessage = sender.invaliderrormessage;
				
				IEG4_AddFailedValidationClassToControl(control);

				// fail validation
				args.IsValid = false;
				return;
			}
		}
	}
	
	// else the value is either blank or the initial value and not required or of a valid format
	args.IsValid = true;
	IEG4_RemoveFailedValidationClassFromControl(control);
}

function IEG4_CheckBoxValidator_EvaluateIsValid(sender, args)
{
	// get the checkbox
	var checkbox = document.getElementById(sender.controltovalidate);
	
	// if the control doesn't exist pass validation
	if (!checkbox) 
	{
		args.IsValid = true;
		return;
	}
	
	// if there is no error message property, create one
	if (!sender.errormessage)
	{
		sender.setAttribute("errormessage", "")
	}
	
	// if we've got a non blank error message then the field is required
	if (sender.requirederrormessage.length > 0)
	{
		// if it's not checked it's not valid
		if (checkbox.checked == false) 
		{
			// nothing has been entered so set the error message, this could be text or an image
			if (sender.requirederrorimage && sender.requirederrorimage != "")
			{
				sender.innerHTML = "<img src=\"" + sender.requirederrorimage.replace("~/", "") + "\" alt=\"" + sender.requirederrormessage + "\" title=\"" + sender.requirederrormessage + "\" />";
			}
			else
			{
				sender.innerHTML = sender.requirederrormessage;
			}
			
			// also set the error message property so the message is shown in any summaries
			sender.errormessage = sender.requirederrormessage;
			
			IEG4_AddFailedValidationClassToControl(checkbox);

			// fail validation
			args.IsValid = false;
			return;
		}
	}

	IEG4_RemoveFailedValidationClassFromControl(checkbox);
	// else we're valid
	args.IsValid = true;
}

function IEG4_ListItemCollectionValidator_EvaluateIsValid(sender, args)
{
	// get the element with the options - check box list,
	var element = document.getElementById(sender.controltovalidate);

	// if the control doesn't exist pass validation
	if (!element) 
	{
		args.IsValid = true;
		return;
	}
	
	if (!sender.errormessage)
	{
		sender.setAttribute("errormessage", "")
	}
	
	// if we've got a non blank error message then the field is required
	if (sender.requirederrormessage.length > 0)
	{
		var controls = jQuery("#" + sender.controltovalidate).find(":checkbox, :radio");

		for (var i = 0; i < controls.length; i++) {
			if (controls[i].checked) {
			    // an item is checkd, so pass validation
			    IEG4_RemoveFailedValidationClassFromControl(element);
				args.IsValid = true;
				return;
			}
		}

		// nothing has been entered so set the error message, this could be text or an image
		if (sender.requirederrorimage && sender.requirederrorimage != "")
		{
			sender.innerHTML = "<img src=\"" + sender.requirederrorimage.replace("~/", "") + "\" alt=\"" + sender.requirederrormessage + "\" title=\"" + sender.requirederrormessage + "\" />";
		}
		else
		{
			sender.innerHTML = sender.requirederrormessage;
		}
		
		// also set the error message property so the message is shown in any summaries
		sender.errormessage = sender.requirederrormessage;
		
		IEG4_AddFailedValidationClassToControl(element);

		// fail validation
		args.IsValid = false;
		return;
	}
	
    IEG4_RemoveFailedValidationClassFromControl(element);
	// else we're valid
	args.IsValid = true;
}


function IEG4_ListBoxValidator_EvaluateIsValid(sender, args)
{
	// get the listbox
	var listbox = document.getElementById(sender.controltovalidate);
	
	// if the control doesn't exist pass validation
	if (!listbox) 
	{
		args.IsValid = true;
		return;
	}
	
	// if there is no error message property, create one
	if (!sender.errormessage)
	{
		sender.setAttribute("errormessage", "")
	}
	
	// if we've got a non blank error message then the field is required
	if (sender.requirederrormessage.length > 0)
	{
		// go through the options to see if any have been selected
		for (var i=0; i<listbox.options.length; i++)
		{
			if (listbox.options[i].selected == true)
			{
				args.IsValid = true;
				return;
			}
		}
		
		// nothing has been entered so set the error message, this could be text or an image
		if (sender.requirederrorimage && sender.requirederrorimage != "")
		{
			sender.innerHTML = "<img src=\"" + sender.requirederrorimage.replace("~/", "") + "\" alt=\"" + sender.requirederrormessage + "\" title=\"" + sender.requirederrormessage + "\" />";
		}
		else
		{
			sender.innerHTML = sender.requirederrormessage;
		}
		
		// also set the error message property so the message is shown in any summaries
		sender.errormessage = sender.requirederrormessage;
		
		// fail validation
		args.IsValid = false;
		return;
	}
	
	// else we're valid
	args.IsValid = true;
}

function IEG4_PostCodeTextBox_EvaluateIsValid(value, id, sender)
{
	var valid = false;
	var regExp 
	
	// convert the value to upper case and remove all spaces
	value = value.toUpperCase().replace(" ", "");
	
	regExp = new RegExp(sender.regexp);
	if (regExp.test(value))
	{
		valid = true;
	}
	
	if (valid)
	{
		// Insert a space 3 chars from the end of the postcode if one isn't already there
		if (value.substring(value.length - 4, 1) != " ")
		{
			value = value.substring(0, value.length - 3) + " " + value.substring(value.length - 3);
		}
		
		// update the control with this converted value 
		if (document.getElementById(id))
		{
			document.getElementById(id).value = value;
		}
	}
	return valid;
}

function IEG4_SortCodeTextBox_EvaluateIsValid(value, id, sender)
{
	var valid = false;
	var regExp 
	
	// convert the value to upper case and remove all spaces
	value = value.toUpperCase();
	
	regExp = new RegExp(sender.regexp);
	if (regExp.test(value))
	{
		valid = true;
	}
	
	return valid;
}

function IEG4_MultiFieldSortCode_EvaluateIsValid(sender, args)
{
	console.log('multi-sort-code');
	var valid = false;
	var regExp;
	var value = "";

	// Get the Validator parent control

	var parent = jQuery(sender.parentElement).closest("div.question");

	// Get sub control values

	var leftCtrl = $(parent).find("[id$=SortCodeLeft_InputField]");
	value = value.concat(leftCtrl[0].value);
	value = value.concat("-");

	var middleCtrl = $(parent).find("[id$=SortCodeMiddle_InputField]");
	value = value.concat(middleCtrl[0].value);
	value = value.concat("-");

	var rightCtrl = $(parent).find("[id$=SortCodeRight_InputField]");
	value = value.concat(rightCtrl[0].value);

	value = value.toUpperCase();

	// Check the sort code

	regExp = new RegExp(sender.regexp);
	if (regExp.test(value)) {

		IEG4_RemoveFailedValidationClassFromControl(parent);

		args.IsValid = true;

	}
	else {

		sender.innerHTML = sender.invaliderrormessage;
		sender.errormessage = sender.invaliderrormessage;
		IEG4_AddFailedValidationClassToControl(parent);

		args.IsValid = false;

    }

	return;

}
 
function IEG4_BankAccountCodeTextBox_EvaluateIsValid(value, id, sender)
{
	var valid = false;
	var regExp 
	
	// convert the value to upper case and remove all spaces
	value = value.toUpperCase().replace(" ", "");

	if (value.length != 8) 
	{
		valid = false;
		return;
	}
	
	regExp = new RegExp(sender.regexp);
	if (regExp.test(value))
	{
		valid = true;
	}

	return valid;
}

function IEG4_UKTelephoneNumberTextBox_EvaluateIsValid(value, id, sender)
{
	var valid = true;
	var telephoneNumber;
	
	// convert the value to upper case and remove all spaces
	telephoneNumber = value.toUpperCase().replace(" ", "");
	
	if( telephoneNumber.length > 0 )
	{
	    valid = IEG4_CheckUKTelephone(telephoneNumber) ;
	}

	return valid;
}

function IEG4_CheckUKTelephone (telephoneNumber) {

  // Convert into a string and check that we were provided with something
  var telnum = telephoneNumber + " ";
  if (telnum.length == 1)  {
     return false
  }
  telnum.length = telnum.length - 1;
  
  // Don't allow country codes to be included (assumes a leading "+")
  var exp = /^(\+)[\s]*(.*)$/;
  if (exp.test(telnum) == true) {
     return false;
  }
  
  // Remove spaces from the telephone number to help validation
  while (telnum.indexOf(" ")!= -1)  {
    telnum = telnum.slice (0,telnum.indexOf(" ")) + telnum.slice (telnum.indexOf(" ")+1)
  }
  
  // Remove hyphens from the telephone number to help validation
  while (telnum.indexOf("-")!= -1)  {
    telnum = telnum.slice (0,telnum.indexOf("-")) + telnum.slice (telnum.indexOf("-")+1)
  }  
  
  // Now check that all the characters are digits
  exp = /^[0-9]{10,11}$/;
  if (exp.test(telnum) != true) {
     return false;
  }
  
  // Now check that the first digit is 0
  exp = /^0[0-9]{9,10}$/;
  if (exp.test(telnum) != true) {
     return false;
  }

    // Disallow numbers allocated for dramas.
	 
  // Array holds the regular expressions for the drama telephone numbers
  var tnexp = new Array ();
	tnexp.push (/^(0113|0114|0115|0116|0117|0118|0121|0131|0141|0151|0161)(4960)[0-9]{3}$/);
	tnexp.push (/^02079460[0-9]{3}$/);
	tnexp.push (/^01914980[0-9]{3}$/);
	tnexp.push (/^02890180[0-9]{3}$/);
	tnexp.push (/^02920180[0-9]{3}$/);
	tnexp.push (/^01632960[0-9]{3}$/);
	tnexp.push (/^07700900[0-9]{3}$/);
	tnexp.push (/^08081570[0-9]{3}$/);
	tnexp.push (/^09098790[0-9]{3}$/);
	tnexp.push (/^03069990[0-9]{3}$/);
	
	for (var i=0; i<tnexp.length; i++) {
    if ( tnexp[i].test(telnum) ) {
      return false;
    }
	}
  
  // Finally check that the telephone number is appropriate.
  exp = (/^(01|02|03|05|070|071|072|073|074|075|07624|077|078|079|08)[0-9]+$/);
	if (exp.test(telnum) != true) {
     return false;
  }
  
  // Telephone number seems to be valid - return the stripped telehone number  
  return true;
}

function IEG4_UKDrivingLicenceTextBox_EvaluateIsValid(value, id, sender) {
    var valid = false;
    var regExp = "\d{4}";

    // convert the value to upper case and remove all spaces
    value = value.toUpperCase().replace(" ", "");

    regExp = new RegExp(sender.regexp);
    if (regExp.test(value)) {
        valid = true;
    }

    return valid;
}

function IEG4_AUSPostCodeTextBox_EvaluateIsValid(value, id, sender) {
    
    var valid = false;
    var regExp;

    // convert the value to upper case and remove all spaces
    value = value.replace(" ", "");

    regExp = new RegExp("^[0-9]{4}$");
    if (regExp.test(value)) {
        valid = true;
    }

    return valid;
}

function IEG4_AUSTelephoneNumberTextBox_EvaluateIsValid(value, id, sender) {
    var valid = true;
    var telephoneNumber;

    // convert the value to upper case and remove all spaces
    telephoneNumber = value.toUpperCase().replace(" ", "");

    if (telephoneNumber.length > 0) {
        valid = IEG4_CheckUKTelephone(telephoneNumber);
    }

    return valid;
}

function IEG4_ValidatedTextbox_EvaluateIsValid(value, id, sender) {

    var valid = false;
    var regExp;

    // convert the value to upper case and remove all spaces
    value = value.replace(" ", "");

    if (value.length > 0) {

        regExp = new RegExp(sender.regexp);
        if (regExp.test(value)) {
            valid = true;
        }
    }
    else {
        valid = true;
    }

    return valid;
}

function IEG4_CheckAUSTelephone(telephoneNumber) {

    // Convert into a string and check that we were provided with something
    var telnum = telephoneNumber + " ";
    if (telnum.length == 1) {
        return false
    }
    telnum.length = telnum.length - 1;

    // Don't allow country codes to be included (assumes a leading "+")
    var exp = /^(\+)[\s]*(.*)$/;
    if (exp.test(telnum) == true) {
        return false;
    }

    // Remove spaces from the telephone number to help validation
    while (telnum.indexOf(" ") != -1) {
        telnum = telnum.slice(0, telnum.indexOf(" ")) + telnum.slice(telnum.indexOf(" ") + 1)
    }

    // Remove hyphens from the telephone number to help validation
    while (telnum.indexOf("-") != -1) {
        telnum = telnum.slice(0, telnum.indexOf("-")) + telnum.slice(telnum.indexOf("-") + 1)
    }

    // Now check that all the characters are digits
    exp = /^[0-9]{10,11}$/;
    if (exp.test(telnum) != true) {
        return false;
    }

    // Array holds the regular expressions for the drama telephone numbers
    var tnexp = new Array();
    tnexp.push(/^[0-9]{10}|[0-9]{6}$/);
    
    for (var i = 0; i < tnexp.length; i++) {
        if (!tnexp[i].test(telnum)) {
            return false;
        }
    }

    // Telephone number seems to be valid - return the stripped telehone number  
    return true;
}

function IEG4_NationalInsuranceNumber_EvaluateIsValid(value, id) {
	var valid = false;
	var regExp

	// convert the value to upper case
	value = value.toUpperCase()
	regExp = new RegExp("(^[A-CEGHJ-NOPR-TW-Z]{1}[A-CEGHJ-NPR-TW-Z]{1}[0-9]{6}[ABCD\\s]{1})|(^[T]{1}[N]{1}[0-9]{6}[MF\\s]{1})");
	if (regExp.test(value)) {
		valid = true;
	}

	regExp = new RegExp("(^GB)|(^BG)|(^NK)|(^KN)|(^NT)|(^ZZ).+");
	if (regExp.test(value)) {
		valid = false;
	}

	if (value.length > 9){
	    valid = false;
	}

	if (valid) {
		// update the control with this converted value
		if (document.getElementById(id)) {
			document.getElementById(id).value = value;
		}
	}
	return valid;
}

function IEG4_MultiFieldNationalInsuranceNumber_EvaluateIsValid(sender, args) {
	var valid = false;
	var regExp
	var value = "";

	// Get the Validator parent control

	var parent = jQuery(sender.parentElement).closest("div.question")

	// Get sub control values

	var numberOneCtrl = $(parent).find("[id$=NationalInsuranceNumberOne_InputField]");
	value = value.concat(numberOneCtrl[0].value);

	var numberTwoCtrl = $(parent).find("[id$=NationalInsuranceNumberTwo_InputField]");
	value = value.concat(numberTwoCtrl[0].value);

	var numberThreeCtrl = $(parent).find("[id$=NationalInsuranceNumberThree_InputField]");
	value = value.concat(numberThreeCtrl[0].value);

	var numberFourCtrl = $(parent).find("[id$=NationalInsuranceNumberFour_InputField]");
	value = value.concat(numberFourCtrl[0].value);

	var numberFiveCtrl = $(parent).find("[id$=NationalInsuranceNumberFive_InputField]");
	value = value.concat(numberFiveCtrl[0].value);

	value = value.toUpperCase();

	// Check the NINO Value
	regExp = new RegExp("(^[A-CEGHJ-NOPR-TW-Z]{1}[A-CEGHJ-NPR-TW-Z]{1}[0-9]{6}[ABCD\\s]{1})|(^[T]{1}[N]{1}[0-9]{6}[MF\\s]{1})");
	if (regExp.test(value)) {
		valid = true;
	}

	regExp = new RegExp("(^GB)|(^BG)|(^NK)|(^KN)|(^NT)|(^ZZ).+");
	if (regExp.test(value)) {
		valid = false;
	}

	if (value.length > 9) {
		valid = false;
	}

	if (valid) {

		IEG4_RemoveFailedValidationClassFromControl(parent);

		args.IsValid = true;

	}
	else {

		sender.innerHTML = sender.invaliderrormessage;
		sender.errormessage = sender.invaliderrormessage;
		IEG4_AddFailedValidationClassToControl(parent);

		args.IsValid = false;

    }

	return;

}

function IEG4_PikaDateTextBox_EvaluateIsValid(value, id, sender) {
    //if this is called after a date has been selected but before we have applied formatting, it should already be a valid date

    var now = moment(value);

    console.log(now);

    if (moment(value).isValid()) {
        console.log('valid');
    
    }
    else {
        console.log('invalid');
    }
    return true;
}


function IEG4_DateTextBox_EvaluateIsValid(value, id, sender) {

	var strSeparator = value.substring(2, 3);
	var dateArray = value.split(strSeparator);
	if (dateArray.length != 3) {
		return false;
	}

	if (sender.dateorder == "ymd") {
		var year = dateArray[0];
		var month = dateArray[1];
		var day = dateArray[2];
	}
	else if (sender.dateorder == "dmy") {
		var year = dateArray[2];
		var month = dateArray[1];
		var day = dateArray[0];
	}
	else if (sender.dateorder == "mdy") {
		var year = dateArray[2];
		var month = dateArray[0];
		var day = dateArray[1];
	}

	var dateInput = new Date(year, month - 1, day);

	if (dateInput.getDate() != day || dateInput.getMonth() != month - 1 || dateInput.getFullYear() != year) {
		return false;
	}

	else {
		var op = value;
		var yearFirstExp = new RegExp("^\\s*((\\d{4})|(\\d{2}))([-/]|\\. ?)(\\d{1,2})\\4(\\d{1,2})\\.?\\s*$");
		m = op.match(yearFirstExp);
		var day, month, year;
		if (m != null && (m[2].length == 4 || sender.dateorder == "ymd")) {
			day = m[6];
			month = m[5];
			year = (m[2].length == 4) ? m[2] : GetFullYear(parseInt(m[3], 10))
		}
		else {
			if (sender.dateorder == "ymd") {
				return null;
			}
			var yearLastExp = new RegExp("^\\s*(\\d{1,2})([-/]|\\. ?)(\\d{1,2})(?:\\s|\\2)((\\d{4})|(\\d{2}))(?:\\s\u0433\\.)?\\s*$");
			m = op.match(yearLastExp);
			if (m == null) {
				return null;
			}
			if (sender.dateorder == "mdy") {
				day = m[3];
				month = m[1];
			}
			else {
				day = m[1];
				month = m[3];
			}
			year = (m[5].length == 4) ? m[5] : GetFullYear(parseInt(m[6], 10))
		}
		month -= 1;
		var date = new Date(year, month, day);
		if (year < 100) {
			date.setFullYear(year);
		}

		if (sender.minimumvalidday && sender.minimumvalidmonth && sender.minimumvalidyear) {
          
            var minValidMonth = sender.minimumvalidmonth - 1;
            //month is zero based
            var minDate = new Date(sender.minimumvalidyear, minValidMonth, sender.minimumvalidday, 0, 0, 0, 0);

			if (date < minDate) {
				return false;
			}
		}

		if (sender.maximumvalidday && sender.maximumvalidmonth && sender.maximumvalidyear) {
            var maxValidMonth = sender.maximumvalidmonth - 1;
            //month is zero based
            var maxDate = new Date(sender.maximumvalidyear, maxValidMonth, sender.maximumvalidday, 0, 0, 0, 0)
			if (date > maxDate) {
				return false;
			}
		}

		return true;
	}
}


function IEG4_MultiFieldDateTextBox_EvaluateIsValid(sender, args) {

	//value, id, sender

	var value = "";

	// Get the Validator parent control

	var parent = jQuery(sender.parentElement).closest("div.question");

	//var strSeparator = value.substring(2, 3);
	//var dateArray = value.split(strSeparator);
	//if (dateArray.length != 3) {
	//	IEG4_AddFailedValidationClassToControl(parent);
	//	args.IsValid = false;
	//	return;
	//}

	var dateDay = $(parent).find("[id$=DateDay_InputField]");
	var dateMonth = $(parent).find("[id$=DateMonth_InputField]");
	var dateYear = $(parent).find("[id$=DateYear_InputField]");

	var dateDayValue = dateDay[0].value;
	var dateMonthValue = dateMonth[0].value;
	var dateYearValue = dateYear[0].value;

	if (!(!isNaN(dateDayValue) && !isNaN(dateMonthValue) && !isNaN(dateYearValue))) {

		IEG4_AddFailedValidationClassToControl(parent);
		args.IsValid = false;
		return;

    }

	if (sender.dateorder == "ymd") {
		//var year = dateArray[0];
		//var month = dateArray[1];
		//var day = dateArray[2];
		var year = dateYearValue;
		var month = dateMonthValue;
		var day = dateDayValue;

		value.concat(dateYearValue);
		value = value.concat("/");
		value = value.concat(dateMonthValue);
		value = value.concat("/");
		value = value.concat(dateDayValue);

	}
	else if (sender.dateorder == "dmy") {
		//var year = dateArray[2];
		//var month = dateArray[1];
		//var day = dateArray[0];
		var year = dateYearValue;
		var month = dateMonthValue;
		var day = dateDayValue;

		value = value.concat(dateDayValue);
		value = value.concat("/");
		value = value.concat(dateMonthValue);
		value = value.concat("/");
		value = value.concat(dateYearValue);

	}
	else if (sender.dateorder == "mdy") {
		//var year = dateArray[2];
		//var month = dateArray[0];
		//var day = dateArray[1];
		var year = dateYearValue;
		var month = dateDayValue;
		var day = dateMonthValue;

		value = value.concat(dateMonthValue);
		value = value.concat("/");
		value = value.concat(dateDayValue);
		value = value.concat("/");
		value = value.concat(dateYearValue);

	}

	var dateInput = new Date(year, month - 1, day);

	if (dateInput.getDate() != day || dateInput.getMonth() != month - 1 || dateInput.getFullYear() != year) {
		IEG4_AddFailedValidationClassToControl(parent);
		args.IsValid = false;
		return;
	}

	else {
		var op = value;
		var yearFirstExp = new RegExp("^\\s*((\\d{4})|(\\d{2}))([-/]|\\. ?)(\\d{1,2})\\4(\\d{1,2})\\.?\\s*$");
		m = op.match(yearFirstExp);
		var day, month, year;
		if (m != null && (m[2].length == 4 || sender.dateorder == "ymd")) {
			day = m[6];
			month = m[5];
			year = (m[2].length == 4) ? m[2] : GetFullYear(parseInt(m[3], 10))
		}
		else {
			if (sender.dateorder == "ymd") {
				IEG4_AddFailedValidationClassToControl(parent);
				args.IsValid = false;
				return;
			}
			var yearLastExp = new RegExp("^\\s*(\\d{1,2})([-/]|\\. ?)(\\d{1,2})(?:\\s|\\2)((\\d{4})|(\\d{2}))(?:\\s\u0433\\.)?\\s*$");
			m = op.match(yearLastExp);
			if (m == null) {
				IEG4_AddFailedValidationClassToControl(parent);
				args.IsValid = false;
				return;
			}
			if (sender.dateorder == "mdy") {
				day = m[3];
				month = m[1];
			}
			else {
				day = m[1];
				month = m[3];
			}
			year = (m[5].length == 4) ? m[5] : GetFullYear(parseInt(m[6], 10))
		}
		month -= 1;
		var date = new Date(year, month, day);
		if (year < 100) {
			date.setFullYear(year);
		}

		if (sender.minimumvalidday && sender.minimumvalidmonth && sender.minimumvalidyear) {

			var minValidMonth = sender.minimumvalidmonth - 1;
			//month is zero based
			var minDate = new Date(sender.minimumvalidyear, minValidMonth, sender.minimumvalidday, 0, 0, 0, 0);

			if (date < minDate) {
				IEG4_AddFailedValidationClassToControl(parent);

				args.IsValid = false;
				return;
			}
		}

		if (sender.maximumvalidday && sender.maximumvalidmonth && sender.maximumvalidyear) {
			var maxValidMonth = sender.maximumvalidmonth - 1;
			//month is zero based
			var maxDate = new Date(sender.maximumvalidyear, maxValidMonth, sender.maximumvalidday, 0, 0, 0, 0)
			if (date > maxDate) {
				IEG4_AddFailedValidationClassToControl(parent);
				args.IsValid = false;
				return;
			}
		}

		IEG4_RemoveFailedValidationClassFromControl(parent);

		args.IsValid = true;
		return;

	}

}

function IEG4_NativeDatePickerTextBox_EvaluateIsValid(value, id, sender) {

    //format will always be YYYY-MM-DD

    //var strSeparator = value.substring(2, 3);
    var dateArray = value.split("-");
    if (dateArray.length != 3) {
        return false;
    }

    var year = dateArray[0];
    var month = dateArray[1];
    var day = dateArray[2];


    var dateInput = new Date(year, month - 1, day);

    var d = dateInput.getDate();
    var m = dateInput.getMonth();
    var y = dateInput.getFullYear();

    if (dateInput.getDate() != day) {
        console.log("day not match");
    }
    if (dateInput.getMonth() != month - 1) {
        console.log("month not match");
    }
            
    if (dateInput.getFullYear() != year) {
        console.log("year not match");
    }


        var op = value;
        var yearFirstExp = new RegExp("^\\s*((\\d{4})|(\\d{2}))([-/]|\\. ?)(\\d{1,2})\\4(\\d{1,2})\\.?\\s*$");
        m = op.match(yearFirstExp);
        var day, month, year;
        if (m != null && (m[2].length == 4 || sender.dateorder == "ymd")) {
            day = m[6];
            month = m[5];
            year = (m[2].length == 4) ? m[2] : GetFullYear(parseInt(m[3], 10))
        }
        else {
            if (sender.dateorder == "ymd") {
                return null;
            }
            var yearLastExp = new RegExp("^\\s*(\\d{1,2})([-/]|\\. ?)(\\d{1,2})(?:\\s|\\2)((\\d{4})|(\\d{2}))(?:\\s\u0433\\.)?\\s*$");
            m = op.match(yearLastExp);
            if (m == null) {
                return null;
            }
            if (sender.dateorder == "mdy") {
                day = m[3];
                month = m[1];
            }
            else {
                day = m[1];
                month = m[3];
            }
            year = (m[5].length == 4) ? m[5] : GetFullYear(parseInt(m[6], 10))
        }
        month -= 1;
        var date = new Date(year, month, day);
        if (year < 100) {
            date.setFullYear(year);
        }

        if (sender.minimumvalidday && sender.minimumvalidmonth && sender.minimumvalidyear) {

            var minValidMonth = sender.minimumvalidmonth - 1;
            //month is zero based
            var minDate = new Date(sender.minimumvalidyear, minValidMonth, sender.minimumvalidday, 0, 0, 0, 0);

            if (date < minDate) {
                return false;
            }
        }

        if (sender.maximumvalidday && sender.maximumvalidmonth && sender.maximumvalidyear) {
            var maxValidMonth = sender.maximumvalidmonth - 1;
            //month is zero based
            var maxDate = new Date(sender.maximumvalidyear, maxValidMonth, sender.maximumvalidday, 0, 0, 0, 0)
            if (date > maxDate) {
                return false;
            }
        }

        return true;
    
}

function IEG4_Numeric_EvaluateIsValid(value, id, sender) {
	var valid = false;
	var number;

	// check the value doesn't contain an 'e'. if this is in the right place e.g 12e4 then it is a valid
	// float. But on the server we're using decimals instead of floats so technically it's not valid. We
	// might also run into problems with the MaxLength property when the value is expanded to a 'real' number
	if (value.toUpperCase().indexOf("E") == -1) {
		if (sender.allowdecimal == "True") {
			// if we're allowing decimals see if the string can be converted to a float
			number = parseFloat(value).toFixed(parseInt(sender.decimalplaces));

			if (!isNaN(number)) {
				// check it's not negative
				if (!(sender.allownegative != "True" && number < 0)) {
					valid = true;
				}
			}
		}
		else {
			number = parseInt(value)

			if (!isNaN(number)) {
				// check it's not negative
				if (!(sender.allownegative != "True" && number < 0)) {
					valid = true;
				}
			}
		}

		if (valid) {
			// check max
			if (sender.maximumvalue != null) {
				if (number > parseFloat(sender.maximumvalue)) {
					valid = false;
				}
			}

			if (sender.minimumvalue != null) {
				if (number < parseFloat(sender.minimumvalue)) {
					valid = false;
				}
			}
		}
	}

	// update the control with this converted value as the parse methods will except anything
	// so long as it starts with a number e.g. 123abc would parse ok
	if (valid) {
		if (document.getElementById(id)) {
			document.getElementById(id).value = number;
		}
	}

	return valid;
}

function IEG4_USTelephone_EvaluateIsValid(value, id) {
    var valid = false;
    var regExpBrackets, regExpDots, regExpDashes, regExpSpaces;

    //var res = str.replace(/blue/g, "red");
    value = value.replace(/ /g, '');

    if (value.indexOf('(') > -1 || value.indexOf(')') > -1) {
        //if has brackets must be (123)1231234 or (123)123-1234
        regExpBrackets = new RegExp("^[(][0-9]{3}[)]([0-9]{3})(-)?([0-9]{4})$");
        if (regExpBrackets.test(value)) {
            valid = true;
        }
    }
    else if (value.indexOf('.') > -1) {
        //if has dots must be 123.123.1234
        regExpDots = new RegExp("^([0-9]{3})[.]([0-9]{3})[.]([0-9]{4})$");
        if (regExpDots.test(value)) {
            valid = true;
        }
    }

    else {
        //if has spaces must be 1231231234 or 121231231234 
        regExpSpaces = new RegExp("^([0-9]{2})?([0-9]{10})$");
        if (regExpSpaces.test(value)) {
            valid = true;
        }
    }

    if (valid) {
        // update the control with this converted value
        if (document.getElementById(id)) {
            document.getElementById(id).value = value;
        }
    }
    return valid;
}

function GetFullYear(year) {
	var twoDigitCutoffYear = 2029 % 100;
	var cutoffYearCentury = 2029 - twoDigitCutoffYear;
	return ((year > twoDigitCutoffYear) ? (cutoffYearCentury - 100 + year) : (cutoffYearCentury + year));
}


function IEG4_Hidden_Required_Field_EvaluateIsValid(sender, args) {
    // get the checkbox
    var control = document.getElementById(sender.controltovalidate);

    // if the control doesn't exist pass validation
    if (!control) {
        args.IsValid = true;
        return;
    }

    // if there is no error message property, create one
    if (!sender.errormessage) {
        sender.setAttribute("errormessage", "")
    }

    // get the value to validate
    var value = control.value;

    // trim the string			
    while (value.substring(0, 1) == ' ') {
        value = value.substring(1, value.length);
    }
    while (value.substring(value.length - 1, value.length) == ' ') {
        value = value.substring(0, value.length - 1);
    }

    // set the trimmed value back to be the controls value so long as the control is an input
    if (control.tagName.toUpperCase() == "INPUT") {
        control.value = value;
    }

    // if we've got a non blank error message then the field is required
    if (sender.requirederrormessage.length > 0) {
        // if no value has been entered or the value equals the initial value
        if (value.length == 0 || value.toUpperCase() == sender.initialvalue.toUpperCase()) {
            // nothing has been entered so set the error message, this could be text or an image
            if (sender.requirederrorimage && sender.requirederrorimage != "") {
                sender.innerHTML = "<img src=\"" + sender.requirederrorimage.replace("~/", "") + "\" alt=\"" + sender.requirederrormessage + "\" title=\"" + sender.requirederrormessage + "\"  />";
            }
            else {
                sender.innerHTML = sender.requirederrormessage;
            }

            // also set the error message property so the message is shown in any summaries
            sender.errormessage = sender.requirederrormessage;

            IEG4_HiddenRequiredFieldFailedValidation(control);

            // fail validation
            args.IsValid = false;
            return;
        }
    }

    // if no value has been entered or the value equals the initial value
    if (value.length > 0) {
        if (value.length < sender.minlength) {

            // set the invalid error message, this could be text or an image
            if (sender.invaliderrorimage && sender.invaliderrorimage != "") {
                sender.innerHTML = "<img src=\"" + sender.invaliderrorimage.replace("~/", "") + "\" alt=\"" + sender.invaliderrormessage + "\" title=\"" + sender.invaliderrormessage + "\" />";
            }
            else {
                sender.innerHTML = sender.invaliderrormessage;
            }

            // also set the error message property so the message is shown in any summaries
            sender.errormessage = sender.invaliderrormessage;

            IEG4_HiddenRequiredFieldFailedValidation(control);

            // fail validation
            args.IsValid = false;
            return;
        }
    }

    // if some data has been entered and it's not the initial value, test it passes validation
    if (value.length > 0 && value.toUpperCase() != sender.initialvalue.toUpperCase()) {
        // we only need to do this fields that can be invalid e.g. drop down lists can't contain invalid items
        // and so will have no customclientvalidationfunction property
        if (sender.customclientvalidationfunction && sender.customclientvalidationfunction != "") {
            var valid = eval(sender.customclientvalidationfunction + "(\"" + value.replace("\"", "\\\"") + "\", '" + sender.controltovalidate + "', sender)");

            if (!valid) {
                // set the invalid error message, this could be text or an image
                if (sender.invaliderrorimage && sender.invaliderrorimage != "") {
                    sender.innerHTML = "<img src=\"" + sender.invaliderrorimage.replace("~/", "") + "\" alt=\"" + sender.invaliderrormessage + "\" title=\"" + sender.invaliderrormessage + "\" />";
                }
                else {
                    sender.innerHTML = sender.invaliderrormessage;
                }

                // also set the error message property so the message is shown in any summaries
                sender.errormessage = sender.invaliderrormessage;

                IEG4_HiddenRequiredFieldFailedValidation(control);

                // fail validation
                args.IsValid = false;
                return;
            }
        }
    }

    // else the value is either blank or the initial value and not required or of a valid format
    args.IsValid = true;
    IEG4_HiddenRequiredFieldPassedValidation(control);
}



function IEG4_AddFailedValidationClassToControl(control)
{
    var parent = jQuery(control).closest("div.question").addClass("failed_validation_question");
}

function IEG4_RemoveFailedValidationClassFromControl(control) {
    var parent = jQuery(control).closest("div.question").removeClass("failed_validation_question");
}

function IEG4_HiddenRequiredFieldFailedValidation(control) {
    //add the 'failed validation' class to the container
    var parent = jQuery(control).closest("div.hidden_required_field_container").addClass("failed_validation_question");

    //show the validation error message
    jQuery(control).closest("div.hidden_required_field_container").find(".hidden_required_field_validation_error").removeClass("hidden");
}

function IEG4_HiddenRequiredFieldPassedValidation(control) {
    //add the 'failed validation' class to the container
    var parent = jQuery(control).closest("div.hidden_required_field_container").removeClass("failed_validation_question");

    //hide the validation error message
    jQuery(control).closest("div.hidden_required_field_container").find(".hidden_required_field_validation_error").addClass("hidden");
}

;


function SecureStartButtonClicked(validationGroup) {
    let validationResult = Page_ClientValidate(validationGroup);
    if (validationResult) {
        try {
            jQuery(".secure_start_button").hide();
            jQuery(".set_security_question").hide();
            jQuery(".secure_start_progress").show();
        }
        catch (err) {

        }
    }

    //var answerTextbox = jQuery("input[id*='SetAnswerToSecurityQuestion']")

    //if (jQuery(answerTextbox).val())
    //{
    //    //hide the start button and the security question panel
       
    //        jQuery(".secure_start_button").hide();
    //        jQuery(".set_security_question").hide();
    //        jQuery(".secure_start_progress").show();
    //        //jQuery(".start_progress_image").attr("style", "block");

    //    return true;
    //}
    
    //var warningText = jQuery("div[id$='BeforeYouStartMandatoryWarning']");
    //jQuery(warningText).show();

    //return false;
}

//function SecureRetrieveCheckAnswerButtonClicked() {

//    try {
//        //hide the reference number and buttons

//        jQuery(".secure_continue_progress").show();
//        jQuery(".secured_continue_controls").hide();
        
//    }
//    catch (err) {
//        return true;
//    }
//    return true;
//}

function reCaptchaCallback() {
    alert('recaptcha callback');
}

function isReCaptchaValid(sender, args) {
    let captchaValid = true;
    if (typeof (grecaptcha) != 'undefined') {
        var response = grecaptcha.getResponse();
        (response.length === 0) ? (captchaValid = false) : (captchaValid = true);
    }

    args.IsValid = captchaValid;
}

function SecureRetrieveCheckReferenceButtonClicked(validationGroup) {
    // validation will be triggered twice (once by this, once by aspnet - sacrifice progress indicator?)
    let validationResult = Page_ClientValidate(validationGroup);
    if (validationResult) { // && captchaValid) {
        try {
            jQuery(".secure_continue_progress").show();
            jQuery(".secured_continue_controls").hide();
        }
        catch (err) {

        }
        return true;
    }
    else {
        return false;
    }
};

function IEG4_MutuallyExclusiveCheckbox(sender, count, listId) {
	for (var i = 0; i < count; i++) {
		var id = listId + "_" + i;
		if (id != sender.id) {
			jQuery("#" + id)[0].checked = false;
		}
	}
};
jQuery(document).ready(function () {

    jQuery("hr.design_cursor").click(function () {
        jQuery("hr.design_cursor.selected").removeClass("selected");
        jQuery(this).addClass("selected");
        jQuery("#designercontext").val(jQuery(this).attr("designercontext"));
    });

    jQuery("hr.design_cursor:last").addClass("selected");
    jQuery("#designercontext").val(jQuery("hr.design_cursor:last").attr("designercontext"));

    //jQuery(".designer_delete_control_button").click(function () {
    //    return deleteControl(this);
    //});

    /*
    jQuery(".designer_edit_control_button").click(function () {
        //JB Remove this to disable opening OLD field editors when clicking 'edit' on the control in design mode
        return editControl(this);
    });
    */

    jQuery("hr.design_cursor").droppable({ hoverClass: "design_cursor_drop_hover", tolerance: "touch",
        drop: function (event, ui) {

            // control to insert before
            jQuery("#designermovecontext").val(jQuery(this).attr("designercontext"));

            // id of the control to move
            jQuery("#designercontext").val(ui.draggable.attr("designercontext"));

            window.appController.editorController.openMoveControlDialog(jQuery("#designercontext").val(), jQuery("#designermovecontext").val());

            //$find('DesignerWaitDialogExtender').show();

            //jQuery("#QuestionMoveButton").click();
        },
        accept: function (el) {
          
            //quick check to see where the droppable item is from aka the page we are on or something else
            if (jQuery((el)[0]).hasClass("designer_question") || jQuery((el)[0]).hasClass("designer_section")) {
                console.log("here");
                //if the next item or previous item in the element tree is the same as the hr.design_cursor aka the flashy bit then let it drop
                if ((el).next("hr.design_cursor")[0] == jQuery(this)[0]) {
                    return false;
                }
                else if ((el).prev("hr.design_cursor")[0] == jQuery(this)[0]) {
                    return false;
                }

                return true;
            }
            else {
                return false;
            }
        }
    });

    jQuery(".designer_question, .designer_section").draggable({ cursor: "move", handle: ".designer_move_control_handle", revert: "invalid" });

    setInterval("cursorAnimation()", 600);
});

function cursorAnimation() {
	jQuery("hr.design_cursor.selected").animate({
  		opacity: 0
	}, "fast", "swing").animate({
  		opacity: 1
	}, "fast", "swing");
  }

function deleteControl(sender) {

	jQuery("#designercontext").val(jQuery(sender).attr("designercontext"));

	$find('DesignerWaitDialogExtender').show();

	jQuery("#QuestionDeleteButton").click();

  	return false;
  }

  function editControl(sender) {
  	var controlClientID = jQuery(sender).attr("designercontext");
  	$find('QuestionEditor').openForEdit(controlClientID);
  	return false;
  }

  function ConfirmDeletePage() {
      var confirm_value = document.createElement("INPUT");
      confirm_value.type = "hidden";
      confirm_value.name = "confirm_value";
      if (confirm("Are you sure you want to delete this page?")) {
          confirm_value.value = "Yes";
      } else {
          confirm_value.value = "No";
      }
      document.forms[0].appendChild(confirm_value);
  };


var MapControls = [];
var added = false;

function loadMapScenario() {

    var GetMapControls = $('.ieg4_map_control');

    if (GetMapControls && GetMapControls.length > 0) {
        MapControls = [];
        Microsoft.Maps.loadModule('Microsoft.Maps.SpatialMath', function () {

            //Create the client side models for any map controls on the page
            GetMapControls.each(function (index) {
                let i = new MapControl(this, index);
                MapControls.push(i);
            });

            //call FindIssues, for each map control using selected or default co-ordinates
            MapControls.forEach(function (item, index) {

                if (item.areaBoundaryRequired) {
                    getBoundary(index);
                }
                if (item.selectedLocation && item.selectedLocation.length > 0) {
                    let loc = JSON.parse(item.selectedLocation);
                    FindLocationsToPin(index, loc.latitude, loc.longitude);
                }

            });
        });
    }

}



//Map control, client side model
function MapControl(controlHtml, index) {

    let mapPanel = $(controlHtml).find(".map");
    this.mapPanel = mapPanel[0];

    //my location option
    let myLocationOption = $(controlHtml).find(".my_location_option");
    this.myLocationOption = myLocationOption[0];
    $(this.myLocationOption).click(function (e) { myLocationOptionClick(index, e); });

    //search parts
    let searchOption = $(controlHtml).find(".search_option");
    this.myLocationOption = searchOption[0];
    $(this.myLocationOption).click(function (e) { searchOptionClick(index, e); });

    let searchPanel = $(controlHtml).find(".text_search_panel");
    this.searchPanel = searchPanel[0];

    let searchText = $(controlHtml).find(".text_search_text_box");
    this.searchText = searchText[0];

    let searchResultsView = $(controlHtml).find(".text_search_results_panel");
    this.searchResultsView = searchResultsView[0];

    let searchButton = $(controlHtml).find(".text_search_button");
    this.searchButton = searchButton[0];
    $(this.searchButton).click(function (e) { doAddressLookup(index, e); });

    this.searchResults = [];

    //hidden fields for selected location data
    let selectedAddressFields = $(controlHtml).find(".selected_address_panel input[id$='coords_Answer_InputField']");
    this.selectedLocationField = selectedAddressFields[0];
    let selectedLocJson = $(this.selectedLocationField).val();
    this.selectedLocation = null;
    if (selectedLocJson && selectedLocJson.length > 0) {
        this.selectedLocation = JSON.parse(selectedLocJson);
    }
    let linkField = $(controlHtml).find(".selected_address_panel input[id$='LocationHyperlink']");
    this.selectedLocationLink = linkField[0];
    let customField = $(controlHtml).find(".selected_address_panel input[id$='CustomField']");
    this.selectedLocationCustomField = customField[0];
    let nearestRoadFields = $(controlHtml).find(".selected_address_panel input[id$='NearestStreetOrRoad']");
    this.selectedLocationNearestRoad = nearestRoadFields[0];


    let mapConfig = $(controlHtml).attr("data-map-config");
    this.mapConfig = JSON.parse(mapConfig);

    this.outOfBoundaryMessage = null;
    this.areaBoundaryRequired = false;
    if (this.mapConfig.mapLocationsConfig && this.mapConfig.mapLocationsConfig.areaBoundaryConfigs && this.mapConfig.mapLocationsConfig.areaBoundaryConfigs.length > 0) {
        this.areaBoundaryRequired = true;
        let outOfBoundaryMessage = $(controlHtml).find(".selected_map_point_outside_of_reporting_boundary_text");
        this.outOfBoundaryMessage = $(outOfBoundaryMessage[0]).html();
    }

    this.getNearestStreetFail = null;
    this.streetLookupEnabled = false;
    if (this.mapConfig.mapLocationsConfig) {
        this.streetLookupEnabled = true;
        let getNearestStreetFailMessage = $(controlHtml).find(".selected_map_point_get_nearest_street_fail_text");
        this.getNearestStreetFailMessage = $(getNearestStreetFailMessage[0]).html();
    }

    this.boundaryPolygon = null;
    let action1Message = $(controlHtml).find(".geo_location_asset_action_1_text");
    this.action1Message = $(action1Message[0]).html()

    //set the map mode (road map, satelite image)
    switch (this.mapConfig.defaultMode) {
        case 'aerial':
            this.mapConfig.mapTypeId = Microsoft.Maps.MapTypeId.aerial;
            break;
        case 'road':
            this.mapConfig.mapTypeId = Microsoft.Maps.MapTypeId.road;
            break;
        default:
            this.mapConfig.mapTypeId = Microsoft.Maps.MapTypeId.road;
    }

    //create map
    if (this.selectedLocation) {
        //create map with saved selected location
        let Location = new Microsoft.Maps.Location(this.selectedLocation.latitude, this.selectedLocation.longitude);

        this.map = new Microsoft.Maps.Map(this.mapPanel, {
            center: Location,
            zoom: 17,
            mapTypeId: this.mapConfig.mapTypeId
        });

        if (this.selectedLocation.id !== null) {
            this.map.entities.push(CreateHighlightPin(this.selectedLocation.latitude, this.selectedLocation.longitude));
        }
        else {
            this.map.entities.push(CreatePinForAnyPointSelect(this.selectedLocation.latitude, this.selectedLocation.longitude));
        }
    }
    else {
        //create default map
        let defaultLocation = new Microsoft.Maps.Location(this.mapConfig.defaultCentreLatitude, this.mapConfig.defaultCentreLongitude);

        this.map = new Microsoft.Maps.Map(this.mapPanel, {
            center: defaultLocation,
            zoom: this.mapConfig.defaultZoomLevel,
            mapTypeId: this.mapConfig.mapTypeId
        });
    }

    //create an info box
    this.infobox = new Microsoft.Maps.Infobox(this.map.getCenter(), {
        visible: false,
    });
    this.infobox.setMap(this.map);
    this.infoBoxIsVisible = false;

    //add click handler to map for more manual selection of a location 
    Microsoft.Maps.Events.addHandler(this.map, 'click', function (e) { mapClick(index, e); });
    Microsoft.Maps.Events.addHandler(this.map, 'viewchangeend', function (e) { mapViewChanged(index, e); });

    //add an auto suggest manager to use for location searching
    Microsoft.Maps.loadModule(['Microsoft.Maps.AutoSuggest', 'Microsoft.Maps.Search'], function () {
        let country = MapControls[index].mapConfig.searchFilterCountry;
        if (country && country !== 'null') {
            autosuggestManager = new Microsoft.Maps.AutosuggestManager({ map: MapControls[index].map, countryCode: country });
        }
        else {
            autosuggestManager = new Microsoft.Maps.AutosuggestManager({ map: MapControls[index].map });
        }
        //add a search manager to the map
        MapControls[index].searchManager = new Microsoft.Maps.Search.SearchManager(MapControls[index].map);

        //add the event handler on the text box to search when character pressed
        $(MapControls[index].searchText).keyup(function (e) { doAddressLookup(index, e); });
    });

    this.getLocationTypeConfig = function getLocationTypeConfig(mapIndex, locationTypeId) {

        if (MapControls[mapIndex].mapConfig.mapLocationsConfig && MapControls[mapIndex].mapConfig.mapLocationsConfig.locationTypeConfigs
            && MapControls[mapIndex].mapConfig.mapLocationsConfig.locationTypeConfigs.length > 0) {
            var index = MapControls[mapIndex].mapConfig.mapLocationsConfig.locationTypeConfigs.findIndex(t => t.locationTypeId == locationTypeId)
            if (index > -1) {
                return MapControls[mapIndex].mapConfig.mapLocationsConfig.locationTypeConfigs[index];
            }
        }
        return null;
    }

    ////Get the boundary of an area (dependant on the entity type option) that a specific point is in. 
    //var geoDataRequestOptions = {  entityType: "Neighborhood"};
    //Microsoft.Maps.loadModule('Microsoft.Maps.SpatialDataService', function () {
    //    Microsoft.Maps.SpatialDataService.GeoDataAPIManager.getBoundary(
    //        MapControls[index].map.getCenter(),
    //        geoDataRequestOptions,
    //        MapControls[index].map,
    //        function (data) {
    //            //Add the polygons to the map.
    //            if (data.results && data.results.length > 0) {
    //                MapControls[index].map.entities.push(data.results[0].Polygons);
    //                //console.log(JSON.stringify(data.results[0].Polygons));
    //                MapControls[index].boundary = data.results[0];
    //            }
    //        });
    //});
}


//--------------------------------------------GENERAL METHODS----------------------------------------------------------------------------------------------

function mapViewChanged(mapIndex, e) {

    let center = MapControls[mapIndex].map.getCenter();
    setBoundaryVisibility(mapIndex);

    FindLocationsToPin(mapIndex, center.latitude, center.longitude);
}


function mapClick(mapIndex, e) {
    closeInfobox(mapIndex);

    //can you select any point on the map?
    if (!MapControls[mapIndex].mapConfig.userCanPinAnyMapPoint) {
        return;
    }

    //clear other pins for user selections
    RemoveSpecificPin(mapIndex, "selectedFoundLocation");
    RemoveSpecificPin(mapIndex, "anyPointSelect");
    clearCoordsField(mapIndex);

    //locationWithinBoundsAsync(mapIndex, e.location).then(function (withinBoundary) {
    if (locationWithinBoundsSync(mapIndex, e.location)) {
        if (!MapControls[mapIndex].mapConfig.getNearestRoadToUserPinnedPointEnabled) {
            MapControls[mapIndex].map.entities.push(CreatePinForAnyPointSelect(e.location.latitude, e.location.longitude));
            setCoordsField(mapIndex, e.location, null, null);
        }
        else {
            findAddressforLocation(mapIndex, e.location);
        }
    }
    else {
        showLocationOutOfBoundaryInfoBox(mapIndex, e.location.latitude, e.location.longitude);
    }
    //});

}


function setCoordsField(mapIndex, coords, locationId, road) {
    //update the hidden data fields
    let coordsObj = { latitude: coords.latitude, longitude: coords.longitude, id: locationId, bingRoad: road };

    $(MapControls[mapIndex].selectedLocationField).val(JSON.stringify(coordsObj));
    MapControls[mapIndex].selectedLocation = coordsObj;

    let link = 'https://www.bing.com/maps?';
    link += 'cp=' + coords.latitude + '~' + coords.longitude + '&lvl=18&sp=point.' + coords.latitude + '_' + coords.longitude;
    link += '_Selected%20Location';
    $(MapControls[mapIndex].selectedLocationLink).val(link);
    if (road)
    {
        $(MapControls[mapIndex].selectedLocationNearestRoad).val(road);
    }
}
 
function clearCoordsField(mapIndex) {
    $(MapControls[mapIndex].selectedLocationField).val("");
    MapControls[mapIndex].selectedLocation = null;
    $(MapControls[mapIndex].selectedLocationLink).val("");
    //remove user select existing pin(s)
    RemoveSpecificPin(mapIndex, "selectedFoundLocation");
    RemoveSpecificPin(mapIndex, "anyPointSelect");
}

function getSearchRadius(mapIndex, latitude, longitude) {
    let bounds = MapControls[mapIndex].map.getBounds();
    let northWest = bounds.getNorthwest();
    let searchFrom = new Microsoft.Maps.Location(latitude, longitude)
    let radius = Microsoft.Maps.SpatialMath.getDistanceTo(northWest, searchFrom, Microsoft.Maps.SpatialMath.DistanceUnits.Kilometers);
    console.log('search radius ' + radius);
    return radius;
}

//-------------PUSHPINS METHODS

function FindLocationsToPin(mapIndex, latitude, longitude) {

    getSearchRadius(mapIndex, latitude, longitude);

    let searchRadius = MapControls[mapIndex].mapConfig.issuesSearchRadius;
    let locationSource = MapControls[mapIndex].mapConfig.issuesLocationSource;
    let processTypeCodes = MapControls[mapIndex].mapConfig.issuesProcessTypeCodes;
    let mapLocationsConfig = MapControls[mapIndex].mapConfig.mapLocationsConfig;

    if (locationSource === 'openprocess' && (processTypeCodes === null || processTypeCodes.length === 0)) {
        //no need to search if we don't have a process type code
        return;
    }

    let searchFields = {
        latitude: latitude,
        longitude: longitude,
        serviceType: 'findissues',
        issuesProcessTypeCodes: processTypeCodes,
        radius: searchRadius,
        issuesLocationSource: locationSource,
        mapLocationsConfig: mapLocationsConfig
    };

    if (locationSource != 'openprocess') searchFields.radius = getSearchRadius(mapIndex, latitude, longitude);

    console.log(`FindLocationsToPin : ${latitude} ${longitude}`);

    jQuery.ajax({
        type: "POST",
        url: "api/maps-services/find-geo-locations",
        data: JSON.stringify(searchFields),
        contentType: "application/json; charset=utf-8",
        dataType: "json",
        success:
            function (data) {
                addLocationPinsToMap(mapIndex, data);
            },
        error: function (xhr, ajaxOptions) {
            console.log("GetGeoLocations errored");
        }

    });

}


function addLocationPinsToMap(mapIndex, locations) {

    //remove current found location pins, 
    //get the pin for the user selection if there is one
    let selectedLocationPin = null;
    for (let i = MapControls[mapIndex].map.entities.getLength() - 1; i >= 0; i--) {
        let pushpin = MapControls[mapIndex].map.entities.get(i);
        if (pushpin instanceof Microsoft.Maps.Pushpin) {
            if (pushpin.metadata && pushpin.metadata.pinKind) {
                if (pushpin.metadata.pinKind == "foundLocation") {
                    MapControls[mapIndex].map.entities.removeAt(i);
                }
                else if (pushpin.metadata.pinKind == "selectedFoundLocation") {
                    selectedLocationPin = MapControls[mapIndex].map.entities.removeAt(i);
                }
                else if (pushpin.metadata.pinKind == "anyPointSelect") {
                    selectedLocationPin = MapControls[mapIndex].map.entities.removeAt(i);
                }
            }
        }
    }

    if (locations) {
        console.log("locations found " + locations.length);
    }

    if (locations && locations.length > 0) {

        for (let i = 0; i < locations.length; i++) {

            let pin = createPinForFoundLocation(mapIndex, locations[i])
            if (!pin) continue;
            let infoboxContent = null;
            if (locations[i].action && locations[i].action.actionTypeID) {
                infoboxContent = buildInfoBoxContentWithMessage(mapIndex, MapControls[mapIndex].action1Message);
            }
            else if (locations[i].attributes && locations[i].attributes.length > 0) {
                infoboxContent = buildLocationPinDataAttributesInfoboxContent(locations[i], mapIndex);
            }

            pin.metadata = { "pinKind": "foundLocation" }

            if (infoboxContent) {
                pin.metadata.infoboxContent = infoboxContent
                Microsoft.Maps.Events.addHandler(pin, 'click', function (e) { pushpinClick(mapIndex, e); });
            }
            MapControls[mapIndex].map.entities.push(pin);
        }
    }

    if (selectedLocationPin) {
        MapControls[mapIndex].map.entities.push(selectedLocationPin);
    }

}

function createPinForFoundLocation(mapIndex, foundLocation) {
    let location = new Microsoft.Maps.Location(foundLocation.latitude, foundLocation.longitude);
    let pinColor = Microsoft.Maps.Color.fromHex('#FF0000'); //default colour red

    if (foundLocation.locationKind === "asset") {

        //if (MapControls[mapIndex].map.getZoom() <= 16) {
        //    return null;
        //}

        pinColor = Microsoft.Maps.Color.fromHex('#45818E'); //default asset colour blue
    }

    else if (foundLocation.locationKind === "issue") {
        if (MapControls[mapIndex].map.getZoom() <= 14) {
            return null;
        }
    }

    let locationTypeConfig = MapControls[mapIndex].getLocationTypeConfig(mapIndex, foundLocation.locationTypeId);
    if (locationTypeConfig) {

        if (locationTypeConfig.iconType == "image" && locationTypeConfig.imageSource && locationTypeConfig.imageSource.length > 0) {
            return new Microsoft.Maps.Pushpin(location, { icon: locationTypeConfig.imageSource });
        }
        else if (locationTypeConfig.iconType == "pushpin" && locationTypeConfig.pushpinColour && locationTypeConfig.pushpinColour.length > 0) {
            if (locationTypeConfig.pushpinColour.includes("#")) {
                pinColor = Microsoft.Maps.Color.fromHex(locationTypeConfig.pushpinColour);
                return new Microsoft.Maps.Pushpin(location, { color: pinColor });
            }
            else {
                return new Microsoft.Maps.Pushpin(location, { color: locationTypeConfig.pushpinColour });
            }
        }
    }

    //fallback
    return new Microsoft.Maps.Pushpin(location, { color: pinColor });
}

function pushpinClick(mapIndex, e) {
    //Make sure the infobox has infobox metadata to display.
    if (e.target.metadata && e.target.metadata.infoboxContent) {
        //Set the infobox options with the metadata of the pushpin.
        MapControls[mapIndex].infobox.setOptions({
            location: e.target.getLocation(),
            htmlContent: e.target.metadata.infoboxContent,
            visible: true
        });
    }
}


function clearPushpins(mapIndex) {
    for (let i = MapControls[mapIndex].map.entities.getLength() - 1; i >= 0; i--) {
        let pushpin = MapControls[mapIndex].map.entities.get(i);
        if (pushpin instanceof Microsoft.Maps.Pushpin) {
            MapControls[mapIndex].map.entities.removeAt(i);
        }
    }
}


function CreateHighlightPin(latitude, longitude) {
    let loc = new Microsoft.Maps.Location(latitude, longitude);
    let svg = '<svg xmlns="http://www.w3.org/2000/svg" width="50" height="50"><circle cx="25" cy="25" r="20" stroke="orange" stroke-width="4" fill="yellow" opacity="0.5" /></svg>'
    let pin = new Microsoft.Maps.Pushpin(loc, {
        icon: svg,
        anchor: new Microsoft.Maps.Point(25, 25)
    });
    pin.metadata = { "pinKind": "selectedFoundLocation" }
    return pin;
}


function RemoveSpecificPin(mapIndex, pinKind) {
    for (let i = MapControls[mapIndex].map.entities.getLength() - 1; i >= 0; i--) {
        let pushpin = MapControls[mapIndex].map.entities.get(i);
        if (pushpin instanceof Microsoft.Maps.Pushpin && pushpin.metadata && pushpin.metadata.pinKind == pinKind) {
            MapControls[mapIndex].map.entities.removeAt(i);
        }
    }
}

function CreatePinForAnyPointSelect(latitude, longitude) {
    let loc = new Microsoft.Maps.Location(latitude, longitude);
    let svg = '<svg xmlns="http://www.w3.org/2000/svg" width="30" height="30"><circle cx="15" cy="15" r="10" stroke="grey" stroke-width="1" fill="red" opacity="0.7" /></svg>'
    let pin = new Microsoft.Maps.Pushpin(loc, {
        icon: svg,
        anchor: new Microsoft.Maps.Point(15, 15)
    });
    pin.metadata = { "pinKind": "anyPointSelect" }
    return pin;
}

//-------------INFO BOX METHODS

function showLocationOutOfBoundaryInfoBox(mapIndex, latitude, longitude) {
    let selectedLocation = new Microsoft.Maps.Location(latitude, longitude);

    let html = '<div class="ieg4_map_infobox">';
    html += '<div class="ieg4_map_infobox_title">';
    html += '<i onclick="closeInfobox(' + mapIndex + ')" class="fa fa-times-circle ieg4_map_infobox_closebutton"></i></div>'

    if (MapControls[mapIndex].outOfBoundaryMessage && MapControls[mapIndex].outOfBoundaryMessage.length > 0) {
        html += MapControls[mapIndex].outOfBoundaryMessage;
    }
    else {
        html += '<div>Selected location is outside of the reporting boundary</div>';
    }
    html += '</div>';

    MapControls[mapIndex].infobox.setOptions({
        location: selectedLocation,
        htmlContent: html,
        visible: true
    });
}


//function buildLocationPinActionInfoBoxContent(location, mapIndex) {
//    let html = '<div class="ieg4_map_infobox">';
//    html += '<div class="ieg4_map_infobox_title">';
//    html += '<i onclick="closeInfobox(' + mapIndex + ')" class="fa fa-times-circle ieg4_map_infobox_closebutton"></i></div>'
//    html += MapControls[mapIndex].action1Message;
//    html += '</div>';
//    return html;
//}

function buildInfoBoxContentWithMessage(mapIndex, message) {
    let html = '<div class="ieg4_map_infobox">';
    html += '<div class="ieg4_map_infobox_title">';
    html += '<i onclick="closeInfobox(' + mapIndex + ')" class="fa fa-times-circle ieg4_map_infobox_closebutton"></i></div>'
    html += message;
    html += '</div>';
    return html;
}

function buildLocationPinDataAttributesInfoboxContent(location, mapIndex) {
    let title = location.locationTypeId;
    let locationTypeConfig = MapControls[mapIndex].getLocationTypeConfig(mapIndex, location.locationTypeId);
    if (locationTypeConfig) {
        title = locationTypeConfig.locationTypeName;
    }
    let html = '<div class="ieg4_map_infobox">';

    html += '<div class="ieg4_map_infobox_title">' + title;
    html += '<i onclick="closeInfobox(' + mapIndex + ')" class="fa fa-times-circle ieg4_map_infobox_closebutton"></i></div>';
    html += '<table class="ieg4_map_infobox_table">';

    for (let i = 0; i < location.attributes.length; i++) {
        html += '<tr class="ieg4_map_infobox_tablerow">';
        html += '<td class="ieg4_map_infobox_tabledata_name">';
        html += location.attributes[i].name;
        html += '</td>';
        html += '<td class="ieg4_map_infobox_tabledata_value">';
        html += location.attributes[i].value;
        html += '</td>';
        html += '</tr>';
    }
    html += '</table>';
    if (location.locationKind === "asset") {
        html += `<div><button type="button" onclick="infoBoxSelectPinButtonClicked(${location.latitude},${location.longitude},'${location.id}',${mapIndex})">select this</button></div>`;
    }
    html += '</div>';
    return html;

}



function infoBoxSelectPinButtonClicked(lat, long, locationId, mapIndex) {
    console.log(locationId + ' ' + mapIndex);
    let coords = { latitude: lat, longitude: long }
    setCoordsField(mapIndex, coords, locationId, null);

    //remove user select existing pin(s)
    RemoveSpecificPin(mapIndex, "selectedFoundLocation");
    RemoveSpecificPin(mapIndex, "anyPointSelect");

    MapControls[mapIndex].map.entities.push(CreateHighlightPin(lat, long));
    MapControls[mapIndex].infobox.setOptions({ visible: false });

}


function closeInfobox(mapIndex) {
    MapControls[mapIndex].infobox.setOptions({ visible: false });
}





//--------------------------SEARCH BY TEXT ENTERED OPTION-----------------------------------------------------------

function searchOptionClick(mapIndex) {
    //show text search panel
    $(MapControls[mapIndex].searchPanel).removeClass("hidden");
}

function doAddressLookup(mapIndex) {

    let text = $(MapControls[mapIndex].searchText).val();
    if (text && text.length > 3) {
        autosuggestManager.getSuggestions(text, function (suggestions, query) {
            MapControls[mapIndex].searchResults = suggestions;
            displaySuggestionsReturned(suggestions, query, mapIndex);
        });
    }
}

function displaySuggestionsReturned(suggestions, query, mapIndex) {

    let output = '<ul class="results_list list-group">';
    for (let i = 0; i < suggestions.length; i++) {
        output += '<li class="results_list_item list-group-item" onclick="SearchResultClicked(' + mapIndex + ',' + i + ');">';
        output += MapControls[mapIndex].searchResults[i].formattedSuggestion + '</li>';
    }
    output += '</ul>';
    $(MapControls[mapIndex].searchResultsView).html(output);
}

function SearchResultClicked(mapIndex, searchResultIndex) {

    if (MapControls[mapIndex].searchResults[searchResultIndex].location) {
        //the suggestion has a the location field and thus the coordinates
        let bounds = null;
        clearCoordsField(mapIndex);
        if (MapControls[mapIndex].searchResults[searchResultIndex].bestView) {
            bounds = MapControls[mapIndex].searchResults[searchResultIndex].bestView;
        }

        let location = MapControls[mapIndex].searchResults[searchResultIndex].location;

        if (bounds) {
            MapControls[mapIndex].map.setView({ bounds: bounds });
        } else {
            //If best map view is not known, approximate the zoom level based on the type of entity the result is.
            let zoom = 12;
            if (MapControls[mapIndex].mapConfig.defaultZoomLevel) {
                zoom = MapControls[mapIndex].mapConfig.defaultZoomLevel;
            }
            console.log(MapControls[mapIndex].searchResults[searchResultIndex].entitySubType);
            switch (MapControls[mapIndex].searchResults[searchResultIndex].entitySubType) {
                case 'CountryRegion':
                    zoom = 3;
                    break;
                case 'AdminDivision1':
                    zoom = 6;
                    break;
                case 'AdminDivision2':
                case 'PopulatedPlace':
                    zoom = 10;
                    break;
                case 'Postcode1':
                    zoom = 17;
                    break;
                default:
                    break;
            }
            MapControls[mapIndex].map.setView({ center: location, zoom: zoom });
        }

        //can you select any point on the map?
        if (MapControls[mapIndex].mapConfig.userCanPinAnyMapPoint) {

            if (locationWithinBoundsSync(mapIndex, location)) {
                if (!MapControls[mapIndex].mapConfig.getNearestRoadToUserPinnedPointEnabled) {          
                    MapControls[mapIndex].map.entities.push(CreatePinForAnyPointSelect(location.latitude, location.longitude));
                    setCoordsField(mapIndex, e.location, null, null);
                }
                else {
                    findAddressforLocation(mapIndex, location);
                }
            }
            else {

                showLocationOutOfBoundaryInfoBox(mapIndex, location.latitude, location.longitude);
            }
        }
    }
    else {
        geocodeSuggestion(mapIndex, searchResultIndex);
    }

    //clear the search results
    $(MapControls[mapIndex].searchResultsView).html('');
}

function geocodeSuggestion(mapIndex, searchResultIndex) {
    var searchRequest = {
        where: MapControls[mapIndex].searchResults[searchResultIndex].formattedSuggestion,

        callback: function (r) {

            if (r && r.results && r.results.length > 0) {

                let location = r.results[0].location;

                clearCoordsField(mapIndex);
                //can you select any point on the map?
                if (MapControls[mapIndex].mapConfig.userCanPinAnyMapPoint) {

                    //locationWithinBoundsAsync(mapIndex, location).then(function (withinBoundary) {
                    if (locationWithinBoundsSync(mapIndex, location)) {
                        if (!MapControls[mapIndex].mapConfig.getNearestRoadToUserPinnedPointEnabled) {        
                            MapControls[mapIndex].map.entities.push(CreatePinForAnyPointSelect(location.latitude, location.longitude));
                            setCoordsField(mapIndex, location, null, null);
                        }
                        else {
                            findAddressforLocation(mapIndex, location)
                        }
                    }
                    else {

                        showLocationOutOfBoundaryInfoBox(mapIndex, location.latitude, location.longitude);
                    }

                }

                let bounds = r.results[0].bestView;
                MapControls[mapIndex].map.setView({ bounds: bounds });
            }
        },
        errorCallback: function (e) {
            //If there is an error, alert the user about it.
            //alert("No results found.");
        }
    };
    //Make the geocode request.
    MapControls[mapIndex].searchManager.geocode(searchRequest);
}


//-----------------------------------------MY LOCATION OPTION----------------------------------------------------------------------------

function myLocationOptionClick(mapIndex) {
    //hide the text search panel
    $(MapControls[mapIndex].searchPanel).addClass("hidden");
    getMyLocation(mapIndex);
}

function getMyLocation(mapIndex) {

    function success(position) {

        $(MapControls[mapIndex].searchResultsView).html('');
        let location = new Microsoft.Maps.Location(position.coords.latitude, position.coords.longitude);

        //Center the map on the user's location.
        MapControls[mapIndex].map.setView({ center: location, zoom: 15 });

        //can you select any point on the map?
        if (!MapControls[mapIndex].mapConfig.userCanPinAnyMapPoint) {
            return;
        }

        //Add a pushpin at the user's location.
        clearCoordsField(mapIndex);
        if (locationWithinBoundsSync(mapIndex, location)) {
            if (!MapControls[mapIndex].mapConfig.getNearestRoadToUserPinnedPointEnabled) {
                MapControls[mapIndex].map.entities.push(CreatePinForAnyPointSelect(location.latitude, location.longitude));
                setCoordsField(mapIndex, location, null, null);
            }
            else {
                findAddressforLocation(mapIndex, location)
            }
        }
        else {
            showLocationOutOfBoundaryInfoBox(mapIndex, location.latitude, location.longitude);
        }
    }

    function error() {
        $(MapControls[mapIndex].searchResultsView).html('Unable to retrieve your location');
    }

    if (!navigator.geolocation) {
        $(MapControls[mapIndex].searchResultsView).html('Geolocation is not supported by your browser');
    }
    else {
        $(MapControls[mapIndex].searchResultsView).html('Locating…');
        navigator.geolocation.getCurrentPosition(success, error);
    }
}

//-------------ADDRESS LOOKUP METHODS--------------

function findAddressforLocation(mapIndex, location) {
    let apiKey = MapControls[mapIndex].mapConfig.mapsApiKey;

    let url = "https://dev.virtualearth.net/REST/v1/Routes/SnapToRoad?points=";
    url += location.latitude + "," + location.longitude + "&key=" + apiKey;

    jQuery.ajax({
        type: "GET",
        url: url,
        dataType: "json",
        success:
            function (data) {
                let bingRoad = GetRoadFromSnapToRoadResult(data);
                if (bingRoad) {
                    MapControls[mapIndex].map.entities.push(CreatePinForAnyPointSelect(location.latitude, location.longitude));
                    setCoordsField(mapIndex, location, null, bingRoad);
                    matchBingRoadToStoredAddresses(mapIndex, location, bingRoad);
                }
                else {
                    let infoboxContent = buildInfoBoxContentWithMessage(mapIndex, MapControls[mapIndex].getNearestStreetFailMessage);
                    MapControls[mapIndex].infobox.setOptions({
                        location: location,
                        htmlContent: infoboxContent,
                        visible: true
                    });
                }
            },
        error: function (xhr, ajaxOptions) {
            console.log("GetGeoLocations errored");
        }

    });
}

function GetRoadFromSnapToRoadResult(data) {
    let road = null;
    if (data && data.resourceSets && data.resourceSets.length > 0) {
        let rs = data.resourceSets[0];
        if (rs.resources && rs.resources.length > 0) {
            if (rs.resources[0].snappedPoints && rs.resources[0].snappedPoints.length > 0) {
                road = rs.resources[0].snappedPoints[0].name;
            }
        }
    }

    return road;

}



function matchBingRoadToStoredAddresses(mapIndex, location, road) {

    let source = MapControls[mapIndex].mapConfig.issuesLocationSource;
    let coordsObj = { latitude: location.latitude, longitude: location.longitude, id: null, bingRoad: road };
    let json = JSON.stringify(coordsObj);

    let postData = {
        dataToMatchJson: json,
        serviceType: 'dolookup',
        lookupType: 'MatchBingRoadToStoredAddresses',
        lookupSource: source
    }

    jQuery.ajax({
        type: "POST",
        url: "api/maps-services/do-lookup",
        data: JSON.stringify(postData),
        contentType: "application/json; charset=utf-8",
        dataType: "json",
        success:
            function (data) {
                if (data && data.attributes && data.attributes.length > 0) {
                    data.attributes.forEach(function (item, index) {
                        if (item.isOpenProcessCustomMappedField) {
                            $(MapControls[mapIndex].selectedLocationCustomField).val(item.value);
                        }
                    });
                }
            },
        error: function (xhr, ajaxOptions) {
            console.log("matchBingRoadToStoredAddresses errored");
        }

    });
}

//function findAddressforLocationOld(mapIndex, location) {
//    var searchRequest = {
//        location: new Microsoft.Maps.Location(location.latitude, location.longitude),
//        callback: function (r) {
//            setCoordsField(mapIndex, location, null, r.address)
//        },
//        errorCallback: function (e) {
//            //If there is an error, alert the user about it.
//            alert("We could not find an address for the selected point");
//        }
//    };

//    MapControls[mapIndex].searchManager.reverseGeocode(searchRequest);
//}

//-------------BOUNDARY METHODS--------------

function getBoundary(mapIndex) {

    if (MapControls[mapIndex].areaBoundaryRequired === false) return;
    let locationSource = MapControls[mapIndex].mapConfig.issuesLocationSource;

    let postData = {
        serviceType: 'getboundary',
        boundaryId: MapControls[mapIndex].mapConfig.mapLocationsConfig.areaBoundaryConfigs[0].boundaryId,
        boundarySource: locationSource
    }

    jQuery.ajax({
        type: "POST",
        url: "api/maps-services/get-boundary",
        data: JSON.stringify(postData),
        contentType: "application/json; charset=utf-8",
        dataType: "json",
        success:
            function (data) {

                let boundaryLocations = [];
                for (let i = 0; i < data.points.length; i++) {
                    boundaryLocations.push(new Microsoft.Maps.Location(data.points[i].latitude, data.points[i].longitude));
                }
                let boundaryPolygon = new Microsoft.Maps.Polygon(boundaryLocations, {
                    fillColor: "rgba(90, 255, 150, 0.15)", strokeColor: "rgba(0,100,200,1)", strokeThickness: 1
                });
                MapControls[mapIndex].boundaryPolygon = boundaryPolygon;
                MapControls[mapIndex].boundaryLocations = boundaryLocations;
                MapControls[mapIndex].map.entities.push(boundaryPolygon);

                //addViewPolygon(mapIndex);
                setBoundaryVisibility(mapIndex);
            },
        error: function (xhr, ajaxOptions) {
            console.log("Get Boundary errored");
        }

    });
}


//function addViewPolygon(mapIndex) {

//    if (added) return;

//    let viewbounds = MapControls[mapIndex].map.getBounds()
//    let viewBoundsArr = []
//    viewBoundsArr.push(new Microsoft.Maps.Location(viewbounds.getNorth(), viewbounds.getEast()));
//    viewBoundsArr.push(new Microsoft.Maps.Location(viewbounds.getNorth(), viewbounds.getWest()));
//    viewBoundsArr.push(new Microsoft.Maps.Location(viewbounds.getSouth(), viewbounds.getWest()));
//    viewBoundsArr.push(new Microsoft.Maps.Location(viewbounds.getSouth(), viewbounds.getEast()));
//    let boundaryPolygon = new Microsoft.Maps.Polygon(viewBoundsArr, {
//        fillColor: "rgba(90, 255, 150, 0.15)", strokeColor: "rgba(0,100,200,1)", strokeThickness: 1
//    });
//    MapControls[mapIndex].map.entities.push(boundaryPolygon);
//    added = true;
//}

function getViewBoundsPolygon(mapIndex) {
    let viewbounds = MapControls[mapIndex].map.getBounds()
    let viewBoundsArr = []
    viewBoundsArr.push(new Microsoft.Maps.Location(viewbounds.getNorth(), viewbounds.getEast()));
    viewBoundsArr.push(new Microsoft.Maps.Location(viewbounds.getNorth(), viewbounds.getWest()));
    viewBoundsArr.push(new Microsoft.Maps.Location(viewbounds.getSouth(), viewbounds.getWest()));
    viewBoundsArr.push(new Microsoft.Maps.Location(viewbounds.getSouth(), viewbounds.getEast()));
    let viewBoundsPolygon = new Microsoft.Maps.Polygon(viewBoundsArr, {
        fillColor: "rgba(90, 255, 150, 0.15)", strokeColor: "rgba(0,100,200,1)", strokeThickness: 1
    });
    return viewBoundsPolygon;
}


function setBoundaryVisibility(mapIndex) {
    //check if the boundary is within the map view bounds. if so show it else hide it

    let boundaryPolygon = MapControls[mapIndex].boundaryPolygon;

    if (boundaryPolygon) {
        let viewbounds = getViewBoundsPolygon(mapIndex)

        for (let i = MapControls[mapIndex].map.entities.getLength() - 1; i >= 0; i--) {
            let entity = MapControls[mapIndex].map.entities.get(i);
            if (entity == boundaryPolygon) {
                if (Microsoft.Maps.SpatialMath.Geometry.intersects(viewbounds, boundaryPolygon)) {
                    entity.setOptions({ visible: true });
                } else {
                    entity.setOptions({ visible: false });
                }
            }
        }
    }
 
}



//async function locationWithinBoundsAsync(mapIndex, location) {
//    return new Promise((resolve, reject) => {
//        if (MapControls[mapIndex].areaBoundaryRequired) {
//            let polygon = MapControls[mapIndex].boundaryPolygon;
//            let sellocation = new Microsoft.Maps.Location(location.latitude, location.longitude);

//            //Load the Spatial Math module
//            Microsoft.Maps.loadModule('Microsoft.Maps.SpatialMath', function () {
//                //Check to see if the shapes intersect.
//                var intersects = Microsoft.Maps.SpatialMath.Geometry.intersects(sellocation, polygon);
//                if (intersects) {
//                    resolve(true);
//                } else {
//                    resolve(false);
//                }
//            });
//        }

//        else {
//            //no boundary, therefore location is within bounds
//            resolve(true);
//        }
//    });
//}

function locationWithinBoundsSync(mapIndex, location) {

    if (MapControls[mapIndex].areaBoundaryRequired) {
        let polygon = MapControls[mapIndex].boundaryPolygon;
        let sellocation = new Microsoft.Maps.Location(location.latitude, location.longitude);

        //Check to see if the shapes intersect.
        var intersects = Microsoft.Maps.SpatialMath.Geometry.intersects(sellocation, polygon);
        if (intersects) {
            return true;
        } else {
            return false;
        }
    }

    else {
        //no boundary, therefore location is within bounds
        return true;
    }
}
;

jQuery(document).ready(function () {
    //debugger;
    booking_section_initializeBookingSectionControls();
});

var BookingControls = [];

async function booking_section_initializeBookingSectionControls() {
    var GetBookingControls = $('.ieg4_make_booking_control');

    if (GetBookingControls && GetBookingControls.length > 0) {
        BookingControls = [];

        GetBookingControls.each(function (index) {
            let i = new BookingControl(this, index);
            BookingControls.push(i);
        });

        BookingControls.forEach(async function (item, index) {
            if (item.reservedTimeSlot) {     
                await booking_section_checkReservedTimeslotAsync(index);
            }
            else {
                await booking_section_getAvailableBookingSlotsAsync(index);
            }
        });
    }
}

// Booking control, client side model
function BookingControl(controlHtml, index) {
    this.controlHtml = controlHtml;
    this.avallableTimeSlots = [];

    this.busyPanel = $(controlHtml).find(".busy_panel")[0];
    this.availableTimeSlotPickerPanel = $(controlHtml).find(".available_time_slot_picker_panel")[0];
    this.availableTimeSlotsPanel = $(controlHtml).find(".available_time_slots_panel")[0];   
    this.selectedTimeSlotPanel = $(controlHtml).find(".selected_time_slot_panel")[0];
    this.reservedTimeSlotPanel = $(controlHtml).find(".reserved_time_slot_panel")[0];
    this.errorPanel = $(controlHtml).find(".error_panel")[0];
    this.reservedTimeSlotHiddenField = $(controlHtml).find(".reserved_time_slot_hidden_field input[id$='ReservedTimeSlot_Answer_InputField']")[0];
    let reservedTimeSlotJson = $(this.reservedTimeSlotHiddenField).val();
    this.reservedTimeSlot = null;
    if (reservedTimeSlotJson && reservedTimeSlotJson.length > 0) {
        this.reservedTimeSlot = JSON.parse(reservedTimeSlotJson);
    }
    let config = $(controlHtml).attr("data-control-config");
    this.config = JSON.parse(config);
    let busyLabel = $(controlHtml).find(".busy_label_text");
    this.busyLabel = $(busyLabel[0]).html();
    let selectSlotLabel = $(controlHtml).find(".select_slot_label_text");
    this.selectSlotLabel = $(selectSlotLabel[0]).html();
    let reserveSlotLabel = $(controlHtml).find(".reserve_slot_label_text");
    this.reserveSlotLabel = $(reserveSlotLabel[0]).html();
    let slotReservedLabel = $(controlHtml).find(".slot_reserved_label_text");
    this.slotReservedLabel = $(slotReservedLabel[0]).html();
    let slotUnavailableLabel = $(controlHtml).find(".slot_unavailable_label_text");
    this.slotUnavailableLabel = $(slotUnavailableLabel[0]).html();
    let systemErrorLabel = $(controlHtml).find(".system_error_label_text");
    this.systemErrorLabel = $(systemErrorLabel[0]).html();
    let noSlotsAvailableLabel = $(controlHtml).find(".no_slot_available_label_text");
    this.noSlotsAvailableLabel = $(noSlotsAvailableLabel[0]).html();
}


function booking_section_clear_ui(controlIndex) {
    $(BookingControls[controlIndex].busyPanel).html('');
    $(BookingControls[controlIndex].availableTimeSlotsPanel).html('');
    $(BookingControls[controlIndex].selectedTimeSlotPanel).html('');
    $(BookingControls[controlIndex].reservedTimeSlotPanel).html('');
    $(BookingControls[controlIndex].errorPanel).html('');
}

function booking_section_toggle_busy(controlIndex,busy) {
    if (busy) {
        $(BookingControls[controlIndex].busyPanel).html(BookingControls[controlIndex].busyLabel);
    }
    else {
        $(BookingControls[controlIndex].busyPanel).html('');
    }
}

async function booking_section_checkReservedTimeslotAsync(controlIndex) {
    booking_section_clear_ui(controlIndex)
    booking_section_toggle_busy(controlIndex, true);
    let reservationId = BookingControls[controlIndex].reservedTimeSlot.uniqueId
    let qry = {
        reservationId: reservationId,
        minimumLeadTimeHours: BookingControls[controlIndex].config.minimumLeadTimeHours
    }
    let data;
    try {
        data = await $.ajax({  url: 'api/bookings-services/reservation/check', type: 'POST',   data: qry,dataType: 'json' });
    } catch (error) {
        bookings_section_systemError(controlIndex,error)
        return;
    }

    booking_section_toggle_busy(controlIndex, false);
    if (data == null) {
        bookings_section_systemError(controlIndex)
    }

    else if (data.isAvailable == false) {
        await booking_section_cancelReservation(controlIndex, reservationId);
        $(BookingControls[controlIndex].reservedTimeSlotHiddenField).val('');
        BookingControls[controlIndex].reservedTimeSlot = null;
        let html = '<div><div>' + BookingControls[controlIndex].slotUnavailableLabel +'</div>';
        html += '<button type="button" class="button" onclick="return booking_section_getAvailableBookingSlotsAsync(' + controlIndex + ')">OK</button></div>';
        $(BookingControls[controlIndex].reservedTimeSlotPanel).html(html);
    }
    else {
        booking_section_buildReservedTimeSlotPanel(controlIndex);
    }
}

function bookings_section_systemError(controlIndex,error) {
    if(error)console.log(error);
    booking_section_toggle_busy(controlIndex, false);
    booking_section_clear_ui(controlIndex);
    let html = '<div>' + BookingControls[controlIndex].systemErrorLabel + '</div>';
    $(BookingControls[controlIndex].errorPanel).html(html);
}

async function booking_section_getAvailableBookingSlotsAsync(controlIndex) {
    booking_section_clear_ui(controlIndex)
    booking_section_toggle_busy(controlIndex, true);
    let qry = {
        serviceId: BookingControls[controlIndex].config.serviceId,
        minimumLeadTimeHours: BookingControls[controlIndex].config.minimumLeadTimeHours,
        maximumLeadTimeDays: BookingControls[controlIndex].config.maximumLeadTimeDays,
    }
    let data;
    try {
        data = await $.ajax({  url: 'api/bookings-services/get-available-time-slots',  type: 'POST',  data: qry, dataType: 'json'});
    } catch (error) {
        bookings_section_systemError(controlIndex, error);
        return;
    }

    if (data == null || data.length == 0) {
        bookings_section_noAvailableSlots(controlIndex);
    }
    else {
        BookingControls[controlIndex].avallableTimeSlots = data;
        booking_section_toggle_busy(controlIndex, false);

        let slotDatesHtml = booking_section_buildAvailableSlotDatesHtml(controlIndex, data);
        $(BookingControls[controlIndex].availableTimeSlotsPanel).html(slotDatesHtml);
    }
}

function bookings_section_noAvailableSlots(controlIndex) {
    booking_section_toggle_busy(controlIndex, false);
    booking_section_clear_ui(controlIndex);
    let label = BookingControls[controlIndex].noSlotsAvailableLabel;
    let html = '<div>' + label + '</div>';
    $(BookingControls[controlIndex].errorPanel).html(html);
}

function booking_section_buildAvailableSlotDatesHtml(controlIndex, availableDates) {
    let output = '<label class=" control-label" for="make_booking_slot_dates_' + controlIndex +'">' + BookingControls[controlIndex].selectSlotLabel + '</label>'
    output += '<div class="form-group">';
    output += '<select id="make_booking_slot_dates_' + controlIndex +'" class="results_list form-control" onchange="return booking_section_availableSlotDateChanged(' + controlIndex + ')">';
    output += '<option value="selectDay">Choose a date</option>';
    for (let i = 0; i < availableDates.length; i++) {
        output += '<option value="' + i + '">' + availableDates[i].formattedDate +'</option>';
    }
    output += '</select></div><div id="make_booking_slot_times_div_'+ controlIndex +'"></div>'
    return output;
}

function booking_section_availableSlotDateChanged(controlIndex) {
    let selectedDate = $('#make_booking_slot_dates_' + controlIndex).val();
    if (selectedDate === 'selectDay') {
        $('#make_booking_slot_times_div_' + controlIndex).html('');
        return false;
    }
    let html = booking_section_buildAvailableSlotTimesHtml(controlIndex, selectedDate);
    $('#make_booking_slot_times_div_' + controlIndex).html(html);
}


function booking_section_buildAvailableSlotTimesHtml(controlIndex, dateIndex)
{   
    let timeSlots = BookingControls[controlIndex].avallableTimeSlots[parseInt(dateIndex)].bookingSlots;
    let output = '<div class="form-group">';
    output += '<select id="make_booking_slot_times_' + controlIndex + '" class="results_list form-control" onchange="return booking_section_availableSlotTimeChanged(' + controlIndex + ')">';
    output += '<option value="selectTime">Choose a time</option>';
    for (let i = 0; i < timeSlots.length; i++) {
        output += '<option value="' + dateIndex + '|' + i.toString() + '">' + timeSlots[i].formattedStartTime + '</option>';
    }
    output += '</select></div>'
    return output;
}

function booking_section_availableSlotTimeChanged(controlIndex) {
    let selectedDateTimeKey = $('#make_booking_slot_times_' + controlIndex).val();
    if (selectedDateTimeKey === 'selectTime') {
        return false;
    }
    let slotDetails = booking_section_getSlotDataForKey(controlIndex, selectedDateTimeKey);
    let label = BookingControls[controlIndex].reserveSlotLabel;
    label = label.replace("{{date}}", slotDetails.formattedDate);
    label = label.replace("{{time}}", slotDetails.formattedStartTime);

    let html = '<div><div>' +  label + '</div>';
    html += `<button type="button" class="button" onclick="return booking_section_reserveTimeSlotClicked(${controlIndex},'${selectedDateTimeKey}')">Reserve</button></div>`;
    $(BookingControls[controlIndex].selectedTimeSlotPanel).html(html);
    return false;
}

function booking_section_getSlotDataForKey(controlIndex, key) {
    let pipeIndex = key.indexOf('|');
    let dateIndex = parseInt(key.substr(0, pipeIndex));
    let timeIndex = parseInt(key.substr(pipeIndex + 1, key.length - 1));
    return BookingControls[controlIndex].avallableTimeSlots[dateIndex].bookingSlots[timeIndex];
}

async function booking_section_reserveTimeSlotClicked(controlIndex, selectedDateTimeKey) {
    let slotData = booking_section_getSlotDataForKey(controlIndex, selectedDateTimeKey);
    booking_section_clear_ui(controlIndex)
    booking_section_toggle_busy(controlIndex, true);

    let qry = {
        serviceId: BookingControls[controlIndex].config.serviceId,
        startDateTimeLocal: slotData.startDateTime,
        endDateTimeLocal: slotData.endDateTime,
        minimumLeadTimeHours: BookingControls[controlIndex].config.minimumLeadTimeHours
    }
    let checkResult;
    try {
        checkResult = await $.ajax({ url: 'api/bookings-services/check-reservation-available', type: 'POST',  data: qry,  dataType: 'json' });
    } catch (error) {
        bookings_section_systemError(controlIndex, error)
        return;
    }

    console.log(checkResult);

    if (checkResult.isAvailable == false) {
        booking_section_toggle_busy(controlIndex, false);
        let html = '<div><div>' + BookingControls[controlIndex].slotUnavailableLabel + '</div>';
        html += '<button type="button" class="button" onclick="return booking_section_getAvailableBookingSlotsAsync(' + controlIndex + ')">OK</button></div>';
        $(BookingControls[controlIndex].reservedTimeSlotPanel).html(html);
        return;
    }

    let dto = {
        startDateTimeLocal: slotData.startDateTime,
        endDateTimeLocal: slotData.endDateTime,
        formReference: BookingControls[controlIndex].config.formReference,
        bookingReference: BookingControls[controlIndex].config.formReference + BookingControls[controlIndex].config.bookingCode,
        secondsToHold: BookingControls[controlIndex].config.holdReservationForMinutes * 60,
        serviceId: BookingControls[controlIndex].config.serviceId
     }

    $.ajax({
        type: "POST",
        url: "api/bookings-services/reservation",
        data:dto,
        success:
            function (data) {
                console.log(data);
                if (data != null) {
                    BookingControls[controlIndex].reservedTimeSlot = data;
                    $(BookingControls[controlIndex].reservedTimeSlotHiddenField).val(JSON.stringify(BookingControls[controlIndex].reservedTimeSlot));
                    booking_section_toggle_busy(controlIndex, false);
                    booking_section_buildReservedTimeSlotPanel(controlIndex);
                    return false;
                }

            },
        error: function (xhr) {
            bookings_section_systemError(controlIndex, xhr)
            return;
        }

    });
}

function booking_section_buildReservedTimeSlotPanel(controlIndex) {
    let bookingReference = BookingControls[controlIndex].config.formReference + BookingControls[controlIndex].config.bookingCode;
    let label = BookingControls[controlIndex].slotReservedLabel;
    let slotDetails = BookingControls[controlIndex].reservedTimeSlot;
    label = label.replace("{{date}}", slotDetails.formattedDate);
    label = label.replace("{{time}}", slotDetails.formattedStartTime);
    label = label.replace("{{expiryTime}}", slotDetails.formattedExpiresOnTime);
    label = label.replace("{{bookingReference}}", bookingReference);

    let html = '<div><div>' + label + '</div>';
    html += '<button type="button" class="button" onclick="return booking_section_changeReservationClicked(' + controlIndex + ')">Change reservation</button></div>';
    $(BookingControls[controlIndex].reservedTimeSlotPanel).html(html);
}

async function booking_section_changeReservationClicked(controlIndex) {
    await booking_section_cancelReservation(controlIndex, BookingControls[controlIndex].reservedTimeSlot.uniqueId);
    $(BookingControls[controlIndex].reservedTimeSlotHiddenField).val('');
    BookingControls[controlIndex].reservedTimeSlot = null;
    await booking_section_getAvailableBookingSlotsAsync(controlIndex);
}

async function booking_section_cancelReservation(controlIndex, reservationId) {
    booking_section_clear_ui(controlIndex)
    booking_section_toggle_busy(controlIndex, true);
    try {
        await $.ajax({ url: 'api/bookings-services/reservation/' + reservationId, type: 'DELETE' });
    } catch (error) {
        bookings_section_systemError(controlIndex, error)
        return;
    }
    booking_section_toggle_busy(controlIndex, false);
}



//function booking_section_create_timer(controlIndex) {
//    let countDownDate = new Date("Sep 30, 2022 16:03:25").getTime();

//    // Update the count down every 1 second
//    let x = setInterval(function () {

//        let now = new Date().getTime();
//        let distance = countDownDate - now;


//        let minutes = Math.floor((distance % (1000 * 60 * 60)) / (1000 * 60));
//        let seconds = Math.floor((distance % (1000 * 60)) / 1000);

//        if (distance < 0) {
//            clearInterval(x);
//            //clear hidden field
//            //show timeout view with a button to get available appointments
//        }
//    }, 1000);
//}

;
jQuery(document).ready(function () {
    cancel_booking_section_initializeControls();
});

var CancelBookingControls = [];

async function cancel_booking_section_initializeControls() {
    var GetCancelBookingControls = $('.ieg4_cancel_booking_control');

    if (GetCancelBookingControls && GetCancelBookingControls.length > 0) {
        CancelBookingControls = [];

        GetCancelBookingControls.each(function (index) {
            let i = new CancelBookingControl(this, index);
            CancelBookingControls.push(i);
        });

        CancelBookingControls.forEach(async function (item, index) {
            if (item.bookingToCancel) {
                cancel_booking_section_buildFoundPanel(index);
            }
            else {
                cancel_booking_section_buildSearchPanel(index);
            }
        });
    }
}

// Cancel Booking control, client side model
function CancelBookingControl(controlHtml, index) {
    this.controlHtml = controlHtml;

    this.uiPanel = $(controlHtml).find(".ui_panel")[0];

    this.bookingToCancelHiddenField = $(controlHtml).find(".booking_to_cancel_hidden_field input[id$='BookingToCancel_Answer_InputField']")[0];
    let bookingToCancelJson = $(this.bookingToCancelHiddenField).val();
    this.bookingToCancel = null;
    if (bookingToCancelJson && bookingToCancelJson.length > 0) {
        this.bookingToCancel = JSON.parse(bookingToCancelJson);
    }
    let config = $(controlHtml).attr("data-control-config");
    this.config = JSON.parse(config);
    let busyLabel = $(controlHtml).find(".busy_label_text");
    this.busyLabel = $(busyLabel[0]).html();
    let findBookingLabel = $(controlHtml).find(".find_booking_label_text");
    this.findBookingLabel = $(findBookingLabel[0]).html();
    let bookingFoundLabel = $(controlHtml).find(".booking_found_label_text");
    this.bookingFoundLabel = $(bookingFoundLabel[0]).html();
    let noBookingFoundLabel = $(controlHtml).find(".no_booking_found_label_text");
    this.noBookingFoundLabel = $(noBookingFoundLabel[0]).html();
    let systemErrorLabel = $(controlHtml).find(".system_error_label_text");
    this.systemErrorLabel = $(systemErrorLabel[0]).html();
}

function cancel_booking_section_clear_ui(controlIndex) {
    $(CancelBookingControls[controlIndex].uiPanel).html('');
}

function cancel_booking_section_toggle_busy(controlIndex) {
    $(CancelBookingControls[controlIndex].uiPanel).html(CancelBookingControls[controlIndex].busyLabel);
}

function cancel_booking_section_buildSearchPanel(controlIndex) {
    //let html = '<div><input ID="FindBooking" type="text" class="find_booking"><div>'
    //html += '<div><button type="button" onclick="return cancel_booking_section_checkBookingRefClicked(' + index + ')">Search</button></div>';

    let label = CancelBookingControls[controlIndex].findBookingLabel;

    let output = '<div class="form-group"><label class=" control-label" for="cancel_booking_' + controlIndex + '">' + label + '</label>'
    output += '<input type="text" id="cancel_booking_' + controlIndex + '" class="form-control"></input></div>';
    output += '<div><button type="button" class="button" onclick="return cancel_booking_section_checkBookingRefClicked(' + controlIndex + ')">Search</button></div>';

    $(CancelBookingControls[controlIndex].uiPanel).html(output);
}

async function cancel_booking_section_checkBookingRefClicked(controlIndex) {
    let bookingRef = $("#cancel_booking_" + controlIndex).val();
    if (bookingRef == null || bookingRef.length == 0) return;

    cancel_booking_section_toggle_busy(controlIndex);

    let result;
    try {
        result = await $.ajax({
            url: 'api/bookings-services/appointment/' + bookingRef,
            type: 'GET',
            dataType: 'json'
        });
    }
    catch (error) {
        cancel_booking_section_buildErrorPanel(controlIndex, 'exception')
        return false;
    }

    if (result != null) {
        $(CancelBookingControls[controlIndex].bookingToCancelHiddenField).val(JSON.stringify(result));
        CancelBookingControls[controlIndex].bookingToCancel = result;
        cancel_booking_section_buildFoundPanel(controlIndex);
    }
    else {
        cancel_booking_section_buildErrorPanel(controlIndex, 'not_found', bookingRef);
    }
    return false;
}

function cancel_booking_section_buildFoundPanel(controlIndex) {
    let label = CancelBookingControls[controlIndex].bookingFoundLabel;
    let apt = CancelBookingControls[controlIndex].bookingToCancel;

    label = label.replace("{{date}}", apt.formattedDate);
    label = label.replace("{{time}}", apt.formattedStartTime);
    label = label.replace("{{bookingReference}}", apt.bookingReference);

    let html = '<div><div>' + label + '</div>'
    html += '<button type="button"  class="button" onclick="return cancel_booking_section_retryFindClicked(' + controlIndex + ')">Change cancellation</button></div>';
    $(CancelBookingControls[controlIndex].uiPanel).html(html);
}

function cancel_booking_section_retryFindClicked(controlIndex) {
    $(CancelBookingControls[controlIndex].bookingToCancelHiddenField).val('');
    CancelBookingControls[controlIndex].bookingToCancel = null;
    cancel_booking_section_buildSearchPanel(controlIndex);
}

function cancel_booking_section_buildErrorPanel(controlIndex, errorKind, bookingRef) {
    let html = '<div>';
    let label = '';
    switch (errorKind) {
        case 'exception':
            html = '<div>' + CancelBookingControls[controlIndex].systemErrorLabel + '</div>';
            break;
        case 'not_found':
            label = CancelBookingControls[controlIndex].noBookingFoundLabel;
            label = label.replace("{{bookingReference}}", bookingRef);
            html = '<div>' + label + ' </div>';
            break;
        case 'already_cancelled':
            html = '<div>This booking has already been cancelled</div>';
            break;
    }
    html += '<div><button type="button" class="button" onclick="return cancel_booking_section_retryFindClicked(' + controlIndex + ')">Retry</button></div>';
    html += '</div>';
    $(CancelBookingControls[controlIndex].uiPanel).html(html);
};

jQuery(document).ready(function () {
    directory_section_initializeControls();
});


var DirectorySectionControls = [];

async function directory_section_initializeControls() {
  //  debugger;
    var GetDirectorySectionControls = $('.ieg4_directory_section_control');
    if (GetDirectorySectionControls && GetDirectorySectionControls.length > 0) {
        DirectorySectionControls = [];

        GetDirectorySectionControls.each(function (index) {
            let i = new DirectorySectionControl(this, index);
            DirectorySectionControls.push(i);
        });
    }
}

function DirectorySectionControl(controlHtml, controlIndex) {
    this.controlHtml = controlHtml;
    this.uiPanel = $(controlHtml).find(".ui_panel")[0];

    let config = $(controlHtml).attr("data-control-config");
    this.config = JSON.parse(config);

    //search fields
    this.searchFieldsPanel = $(controlHtml).find(".search_fields_panel")[0];
    this.searchFieldsButtonPanel = $(controlHtml).find(".search_fields_button_panel")[0];
    this.searchMessagePanel = $(controlHtml).find(".search_message_panel")[0];
    let searchBtn = '<button type="button" class="button" onclick="return directory_section_searchRecordsAsync(' + controlIndex + ')">Search</button></div>';
    $(this.searchFieldsButtonPanel).html(searchBtn);

    //record dropdown list
    this.recordSelector = $(controlHtml).find(".record_selector select")[0];
    this.recordSelectorId = $(this.recordSelector).attr("id");
    $(controlHtml).on('change', '.record_selector select', async function () { await directory_section_selectedRecord(this, controlIndex); });

    //handlebars template
    let templateSource = $(controlHtml).find(".record_display_template script");
    this.templateSource = $(templateSource[0]).html();
    this.compiledTemplate = Handlebars.compile(this.templateSource);

    //editable fields
    this.editableFieldsPanel = $(controlHtml).find(".editable_fields_panel")[0];
}

async function directory_section_searchRecordsAsync(controlIndex) {

    console.log('clicked ' + controlIndex);
    let searchParams = [];
    let searchFields = $(DirectorySectionControls[controlIndex].searchFieldsPanel).find("input");
    searchFields.each(function(){
        let f = $(this).val();
        let parent = $(this).closest("[data-path]");
        let p = $(parent).attr("data-path");
        if (f && f.length > 0) {
            searchParams.push({ 'value': f, 'path': p });
        }
    });

    console.log(searchParams);
    let data;
    if (searchParams.length > 0) {
        let qry = {
            "directoryId": DirectorySectionControls[controlIndex].config.directoryDefinitionId,
            "filters": searchParams
        }
        data = await $.ajax({ url: 'api/directories/record/search', type: 'POST', data: qry, dataType: 'json' })
        console.log(data);
        directory_section_populateRecordSelector(controlIndex, data);
    }


}


function directory_section_populateRecordSelector(controlIndex, records) {
    let dropDownId = DirectorySectionControls[controlIndex].recordSelectorId;
    let dropdown = document.getElementById(dropDownId);
    $('#' + dropDownId).html('');
    if (records && records.length > 0) {
        let option = document.createElement("option");
        option.text = "Select an option...";
        dropdown.add(option);

        records.forEach(function (item, index) {
            let val = directory_section_resolveRecordPath("data." + DirectorySectionControls[controlIndex].config.dropdownFieldDataPath, item);
            option = document.createElement("option");
            option.value = item.id;
            option.text = val;
            dropdown.add(option);
        });
    }
}



async function directory_section_selectedRecord(elem, controlIndex) {
    console.log('selected record');
    var selection = $(elem).val();
    console.log(selection);
    $(DirectorySectionControls[controlIndex].uiPanel).html("");
    if (!selection) return;

    //get the record
    let record = await $.ajax({
        url: 'api/directories/record/' + selection,
        type: 'GET',
        dataType: 'json'
    });

    console.log(record);

    //populate the template
    let output = DirectorySectionControls[controlIndex].compiledTemplate(record);
    $(DirectorySectionControls[controlIndex].uiPanel).html(output);

    //populate any editable fields
    directory_section_populateEditableFields(controlIndex, record);
}

function directory_section_populateEditableFields(controlIndex, record) {
    //get all the editable fields
    let editableFields = $(DirectorySectionControls[controlIndex].editableFieldsPanel).find("input");
    editableFields.each(function () {
        //get it's data value from the record and populate the field
        let parent = $(this).closest("[data-path]");
        let p = $(parent).attr("data-path");
        let val = directory_section_resolveRecordPath("data." + p, record)
        $(this).val(val);
    });
}

function directory_section_resolveRecordPath(path, obj) {
    return path.split('.').reduce(function (prev, curr) {
        return prev ? prev[curr] : null
    }, obj || self)
}
;
var activeCalls = 0;

jQuery(document).ready(function () {
    var uploadControls = jQuery('.upload-control');
    for (var i = 0; i < uploadControls.length; i++) {
        var uploadClientId = uploadControls[i].id;
        getUploadList(uploadClientId);
    }
});

function showFileUploadDialog(uploadClientId) {
    window.activeUploadClientId = uploadClientId;
    $('#FileUploadDialog').modal({});
}

function closeFileUploadDialog() {
    $('#FileUploadDialog').modal('hide');
    getUploadList(window.activeUploadClientId);

    var control = jQuery("#" + window.activeUploadClientId);
    focusUploadButton(control);
}

function fileUpload(form, action_url, div_id) {


    //Validate input
    var flag = false;
    var filename = jQuery("#IEG4_UploadInput").val();
    var message;

    if (filename == "") {
        message = "Please select a file for upload.";
        document.getElementById("uploadMessage").innerText = message;
        return;
    }

    //White list
    if (window.whiteList != "" && filename != "") {

        var fileExtension = filename.substr(filename.indexOf(".") + 1, filename.length - filename.indexOf(".") - 1);
        var typeArray = window.whiteList.split(',');

        for (var index = 0; index < typeArray.length; index++) {
            if (fileExtension.toLowerCase() == typeArray[index].toString().toLowerCase()) {
                flag = true;
            }
        }

        if (flag == false) {
            message = "Invalid file type, please upload a file of type " + window.whiteList + ".";
            document.getElementById("uploadMessage").innerText = message;
            return flag;
        }

    }

    //Black list
    if (window.blackList != "" && filename != "") {

        var fileExtension = filename.substr(filename.indexOf(".") + 1, filename.length - filename.indexOf(".") - 1);

        flag = (window.blackList.indexOf(fileExtension) == -1);

        if (flag == false) {
            message = "Files of type " + fileExtension + " can not be uploaded.";
            document.getElementById("uploadMessage").innerText = message;
            return flag;
        }

    }

    //does this mean we miss other files added to the control?
    var fileSize = jQuery("#IEG4_UploadInput")[0].files[0].size;

    if (window.sizeLimit != "" && !((window.sizeLimit * 1000) >= fileSize)) {
        message = "File size must be less than " + window.sizeLimit / 1000 + "Mb";
        document.getElementById("uploadMessage").innerText = message;
        return;
    }

    jQuery("#uploadThrobber").show();

    // Create the iframe...
    var iframe = document.createElement("iframe");
    iframe.setAttribute("id", "upload_iframe");
    iframe.setAttribute("name", "upload_iframe");
    iframe.setAttribute("width", "0");
    iframe.setAttribute("height", "0");
    iframe.setAttribute("border", "0");
    iframe.setAttribute("style", "width: 0; height: 0; border: none;");

    // Add to document...
    form.parentNode.appendChild(iframe);
    window.frames['upload_iframe'].name = "upload_iframe";

    iframeId = document.getElementById("upload_iframe");

    function getCookie(name) {
        var value = "; " + document.cookie;
        var parts = value.split("; " + name + "=");
        if (parts.length == 2) return parts.pop().split(";").shift();
    }
    // Add event...
    var eventHandler = function () {
        if (iframeId.detachEvent) iframeId.detachEvent("onload", eventHandler);
        else iframeId.removeEventListener("load", eventHandler, false);

        var foundUploadCookie = getCookie("upload-state");
        var actualFileName = filename.substring(filename.lastIndexOf('\\') + 1);

        if (foundUploadCookie != null && foundUploadCookie != actualFileName) {
            content = "Sorry, looks like somethign went wrong."
        }
        else {
            // Message from server...
            if (iframeId.contentDocument) {
                content = iframeId.contentDocument.body.innerHTML;
            } else if (iframeId.contentWindow) {
                content = iframeId.contentWindow.document.body.innerHTML;
            } else if (iframeId.document) {
                content = iframeId.document.body.innerHTML;
            }
        }

        // __doPostBack('UploadUpdatePanel', '');

        document.getElementById(div_id).innerHTML = content;

        //syncUploads();

        jQuery("#uploadThrobber").hide();

        jQuery("#uploadButton").attr("disabled", false);

        jQuery("#uploadMessage").delay(6000).slideUp("slow");

        form.reset();

        // Del the iframe...
        setTimeout('if (iframeId.parentNode != null) iframeId.parentNode.removeChild(iframeId);', 250);
    }

    if (iframeId.addEventListener) iframeId.addEventListener("load", eventHandler, true);
    if (iframeId.attachEvent) iframeId.attachEvent("onload", eventHandler);

    var control = jQuery("#" + window.activeUploadClientId);
    var serverId = control.data("server-id");

    var actionString = action_url + "?controlID=" + serverId + "&pageID=" + uploadPageID;

    if (typeof (ctx) != 'undefined') {
        actionString += "&ctx=" + ctx;
    }

    // Set properties of form...
    form.setAttribute("target", "upload_iframe");
    form.setAttribute("action", actionString);
    form.setAttribute("method", "post");
    form.setAttribute("enctype", "multipart/form-data");
    form.setAttribute("encoding", "multipart/form-data");

    form.submit();

    jQuery("#uploadButton").attr("disabled", true);

    document.getElementById(div_id).innerHTML = "Uploading...";

    jQuery("#uploadMessage").slideDown("fast");

}

function getUploadList(uploadClientId) {
    if (uploadClientId == null) {
        return;
    }

    var control = jQuery("#" + uploadClientId);
    var uploadControlDisplayContent = control.find("[id*='UploadList_UploadUpdatePanel']");

    jQuery(uploadControlDisplayContent).empty();
    jQuery(uploadControlDisplayContent).append("<div class='item'><img src='/content/App/Images/progress.gif'>Refreshing list...</div>");

    var serverId = control.data("server-id");

    jQuery.ajax({
        type: "GET",
        url: "legacy-services/GetUploadList/" + serverId + "/" + uploadPageID,
        dataType: "json",
        success: function (data, textStatus, jqXHR) {

            var filesArray = data;
            var uploadListItemElement = "";

            jQuery(uploadControlDisplayContent).empty();

            if (filesArray.length > 0) {
                jQuery.each(filesArray, function (i, file) {
                    uploadListItemElement = "<div class='item'><a href='legacy-services/download/" + file.id;
                    uploadListItemElement += "/" + uploadPageID;
                    uploadListItemElement += "/" + serverId + "' target='_blank'>";

                    uploadListItemElement += file.fileName + "</a>";
                    uploadListItemElement += "<button type='button' class='icon-button' onclick=\"deleteSingleUpload(" + file.id + ", '" + uploadClientId + "')\"><img src='../content/App/Images/icon_16x16_bin.png' alt='Delete this uploaded file' title='Delete this uploaded file' /></button>";
                    uploadListItemElement += "</div>";
                    
                    jQuery(uploadControlDisplayContent).append(uploadListItemElement);
                });
            }
            else {
                jQuery(uploadControlDisplayContent).append("<div class='item'>No files uploaded</div>");
            }
        },
        error: function (xhr, ajaxOptions) {
            jQuery(uploadControlDisplayContent).empty();
            jQuery(uploadControlDisplayContent).append("<div class='item'>Error getting upload list</div>");
        }
    });

}

function deleteSingleUpload(uploadID, uploadClientId) {
    var control = jQuery("#" + uploadClientId);
    var serverId = control.data("server-id");

    jQuery.ajax({
        type: "POST",
        url: "legacy-services/DeleteUpload/" + serverId + "/" + uploadPageID + "/" + uploadID,
        dataType: "json",
        success: function (data, textStatus, jqXHR) {
            getUploadList(uploadClientId);
            focusUploadButton(control);
        },
        error: function (xhr, ajaxOptions) {
            jQuery(uploadControlDisplayContent).empty();
            jQuery(uploadControlDisplayContent).append("<div class='item'>Error deleting upload</div>");
            focusUploadButton(control);
        }
    });
}

function focusUploadButton(control) {
     var btn = control.find("[id *= 'UploadButton']");
     btn.focus();
}
;
