var webServerAddress = "";
var tagServerAddress = "";
var isLocalTagServer = "true";
const QualityNotGoodValue = "----";
var tagServiceUri = "";
var tagNamespace = "http://WPFSCADA/WebTagsService";
var tagEndpointName = "IWebTagService";
var tagInterface = "IWebTagsService";

var helpersServiceUri = "";
var helpersNamespace = "http://WPFSCADA/WebHelpersService";
var helpersEndpointName = "IWebHelpersService";
var helpersInterface = "IWebHelpersService";

var uriParts = window.location.href.split("/");
var phiWebFolder = uriParts[3];
var projectName = uriParts[4];
var graphicName = "";
var ScriptTagValues = {};
var indexToPropertyInfo = new Array();
var propertyInfoToIndex = new Array();
var indexToExprList = new Object();
var screenTagToExprList = new Object();
var screenTagToTag = new Array();
var tagToScreenTag = new Array();
var expressionsWithTagIndex = {};
var reloadMethodName = "";
var pageGuid = "";
var screenTagSeparator = "*/*";
var tagValueSeparator = "=/=";
var tagValueSeparatorCombobox = "-/-";
var folderSeparator = "/";
var apiLocation = "";
var tagServiceIsOnline = true;
var connectionErrorMessage = "Connection with the server was lost!</br>Start the runtime server or check for network problems!";
var NumberDecimalSeparator;
var NumberDecimals;
var DateSeparator;
var AppLanguage;
var projectDateTimeFormat = "";
var trendPointsMultipliers = 1;
var executeEventsFallThroughObjects = false;
var currentUser = "";
var activatedTime = "";
var sentLog = false;
var block = false;
//ViewerTags
var viewerTag_CurrentUser = "";
var viewerTag_CurrentUserProfile = "";
var viewerTag_UserActiveTime = 0;
var viewerTag_UserActivatedTime = "";
var viewerTag_InactiveTime = 0;
var activatedTime = new Date()

if ($ != undefined)
    func = $;
else
    func = J;

//Number.prototype.toString = function () {
//    if (Math.floor(this) == this)
//        return this.toFixed();
//    else if ((Math.abs(this) != 0) && (Math.abs(this) < Math.pow(10, -NumberDecimals)))
//        return this.toExponential(NumberDecimals);
//    else
//        return this.toFixed(NumberDecimals);
//};

function Init() {
    if (window.location.href.substr(0, 8) == "file:///")
        webServerAddress = "localhost";
    else
        webServerAddress = window.location.href.substr(7, window.location.href.substring(7).indexOf("/"));
    var cookies = loadCookies();
    if (cookies['isLocalTagServer'] != null)
        isLocalTagServer = cookies['isLocalTagServer'];
    if (isLocalTagServer == "true")
        tagServerAddress = webServerAddress;
    else {
        if (cookies['tagServerAddress'] != null)
            tagServerAddress = cookies['tagServerAddress'];
    }
    tagServiceUri = "http://" + tagServerAddress + "/WPFSCADA/WebTagsService";
    helpersServiceUri = "http://" + tagServerAddress + "/WPFSCADA/WebHelpersService";

    CheckServer();
    RegisterViewer();
    SetInitialScreenTags(cookies);
    GetInitialInformations();
    if (cookies['currentUser'] != null && cookies['currentUser'] !== '') {
        _setCurrentUser(cookies['currentUser']);
    }
    if (cookies['activatedTime'] != null && cookies['activatedTime'] !== '') {
        _setActivatedTime(cookies['activatedTime']);
    } else {
        window.document.cookie = "activatedTime=" + ReformatDate(new Date(), "");
        _setActivatedTime(cookies['activatedTime']);
    }
}

window.addEventListener('load', function () {
    window.onbeforeunload = OnScreenClose;
    window.onmousedown = UpdateLastInteraction;
    window.onmousemove = UpdateLastInteraction;
    window.onkeydown = UpdateLastInteraction;
})
function UpdateLastInteraction() {
    viewerTag_InactiveTime = 0;
}

function InitIntervals() {
    if (document.readyState === 'complete') {
        _webSecurity();
        _systemTagsUpdate();
        CallCheckData();
        CheckEvents();
        setInterval(_webSecurity, 1000);
        setInterval(_systemTagsUpdate, 1000);
        setInterval(CallCheckData, 500);
        setInterval(CheckEvents, 1000);
    }
}

function GetSystemTagsValues() {
    var pl = new SOAPClientParameters();
    pl.add("pageGuid", pageGuid);
    var res = SOAPClient.invoke(tagServiceUri, "GetSystemTagsViewer", pl, tagNamespace, tagEndpointName, tagInterface, false, null);
    if (res != null && res[0] != "") {
        viewerTag_CurrentUser = res[0];
        viewerTag_CurrentUserProfile = res[1];
        viewerTag_UserActivatedTime = ReformatDate(res[2], "");
        viewerTag_UserActiveTime = res[3];
        viewerTag_InactiveTime++;
    } else {
        viewerTag_CurrentUser = "";
        viewerTag_CurrentUserProfile = "";
        viewerTag_UserActivatedTime = ReformatDate(new Date(1970, 0, 1), "");
        viewerTag_UserActiveTime = 0;
        viewerTag_InactiveTime = 0;
    }
}

function GetInitialInformations() {
    var pl = new SOAPClientParameters();
    var res = SOAPClient.invoke(helpersServiceUri, "GetInitialInformations", pl, helpersNamespace, helpersEndpointName, helpersInterface, false, null);
    if (res == null)
        return;
    projectDateTimeFormat = res[0];
    trendPointsMultipliers = res[1];
    executeEventsFallThroughObjects = res[2];
    NumberDecimalSeparator = res[3];
    NumberDecimals = res[4];
    DateSeparator = res[5];
    AppLanguage = res[6];
}

function SetInitialScreenTags(cookies) {
    var initialsScreenTag = '';
    if (cookies['initialScreenTag'] != null && cookies['initialScreenTag'] !== '') {
        initialsScreenTag = cookies['initialScreenTag'];
        deleteCookie('initialScreenTag');
    }
    if (initialsScreenTag !== '' && initialsScreenTag != "undefined" && initialsScreenTag != undefined &&
        initialsScreenTag != "null" && initialsScreenTag != null) {
        var arr = initialsScreenTag.split(screenTagSeparator);
        if (arr.length == 2) {
            var screenTagName = GetScrenTagDefault(arr[0]);
            SetScreenTag(screenTagName, arr[1], false, true);
        }
        else if (arr.length == 3) {
            var arrScreen = arr[1].toString().split("|");
            var arrTags = arr[2].toString().split("|");

            for (i = 0; i < arrTags.length; i++) {
                var screenTag = arrScreen[i];
                var tagName = arrTags[i];
                SetScreenTag(screenTag, tagName, false, true);
            }
        }
        else if (arr.length > 3) {
            var screenTagName = GetScrenTagDefault(arr[0]);
            SetScreenTag(screenTagName, arr[1], false, true);
            for (var i = 2; i < arr.length; i++) {
                SetScreenTag(screenTagName + "#" + arr[i].substring(arr[i].indexOf(".")).toUpperCase(), arr[i], false, true);
            }
        }
    }
}

function _browserAlive() {
    var pl = new SOAPClientParameters();
    SOAPClient.invoke(tagServiceUri, "BrowserAlive", pl, tagNamespace, tagEndpointName, tagInterface, false, null);
    _logPageOpen();
}

function LogPageError() {
    var pl = new SOAPClientParameters();
    pl.add("title", document.title);
    SOAPClient.invoke(helpersServiceUri, "LogPageError", pl, helpersNamespace, helpersEndpointName, helpersInterface, false, null);
}

function RegisterViewer() {
    var pl = new SOAPClientParameters();
    if (pageGuid == "") {
        pageGuid = guid();
    }
    pl.add("pageGuid", pageGuid);
    var canStart = SOAPClient.invoke(tagServiceUri, "RegisterViewer", pl, tagNamespace, tagEndpointName, tagInterface, false, null);
    if (!canStart)
        block = true;
}

function GetDateTimeFormat() {
    if (projectDateTimeFormat === '') {
        var pl = new SOAPClientParameters();
        projectDateTimeFormat = SOAPClient.invoke(helpersServiceUri, "GetDateTimeFormat", pl, helpersNamespace, helpersEndpointName, helpersInterface, false, null);
    }
}

function GetTrendPointsMultipliers() {
    var pl = new SOAPClientParameters();
    trendPointsMultipliers = SOAPClient.invoke(helpersServiceUri, "GetTrendPointsMultipliers", pl, helpersNamespace, helpersEndpointName, helpersInterface, false, null);
}

function GetExecuteEventsFallThroughObjects() {
    var pl = new SOAPClientParameters();
    executeEventsFallThroughObjects = SOAPClient.invoke(helpersServiceUri, "GetExecuteEventsFallThroughObjects", pl, helpersNamespace, helpersEndpointName, helpersInterface, false, null);
}

function GetNumberDecimalSeparator() {
    var pl = new SOAPClientParameters();
    return SOAPClient.invoke(helpersServiceUri, "GetNumberDecimalSeparator", pl, helpersNamespace, helpersEndpointName, helpersInterface, false, null);
}

function GetNumberDecimals() {
    var pl = new SOAPClientParameters();
    return SOAPClient.invoke(helpersServiceUri, "GetNumberDecimals", pl, helpersNamespace, helpersEndpointName, helpersInterface, false, null);
}

function GetDateSeparator() {
    var pl = new SOAPClientParameters();
    return SOAPClient.invoke(helpersServiceUri, "GetDateSeparator", pl, helpersNamespace, helpersEndpointName, helpersInterface, false, null);
}

function GetGraphicInfo(graphicName) {
    var pl = new SOAPClientParameters();
    pl.add("graphicName", graphicName);
    return SOAPClient.invoke(helpersServiceUri, "GetGraphicInfo", pl, helpersNamespace, helpersEndpointName, helpersInterface, false, null);
}

function GetGraphicInfoParameters(screen) {
    var graphicInfo = GetGraphicInfo(screen);
    var parameters = '';
    if ((graphicInfo != undefined) && (graphicInfo.length == 6) && (graphicInfo[0].toString().toUpperCase() == 'TRUE')) {
        var top = graphicInfo[1];
        var left = graphicInfo[2];
        var height = graphicInfo[3]; 6
        var width = graphicInfo[4];
        if (graphicInfo[5] == "CenterScreen") {
            top = (window.screen.height - height) / 2;
            left = (window.screen.width - width) / 2;
        } else if (graphicInfo[5] === "CenterOwner") {
            top = (window.screen.height + window.screenTop - height) / 2;
            left = (window.screen.width + window.screenLeft - width) / 2;
        }

        parameters = 'width=' + width + ', height=' + height + ', top=' + top + ', left=' + left;
    }

    return parameters;
}

function Subscribe(tag, property) {
    var pl = new SOAPClientParameters();
    var tags = new Array();
    tags[0] = tag;
    pl.add("tags", tags);
    var propertyIndexes = new Array();
    propertyIndexes[0] = parseInt(property);
    pl.add("properties", propertyIndexes);
    if (pageGuid == "") {
        pageGuid = guid();
    }
    pl.add("pageGuid", pageGuid);
    var ret = SOAPClient.invoke(tagServiceUri, "SubscribeWithType", pl, tagNamespace, tagEndpointName, tagInterface, false, null);
    if (ret !== null) {
        var type = ret["a:ChangedItemWithType"]["a:Type"];
        if (type === undefined) {
            type = ret["a:ChangedItemWithType"][1];
            if (type === 2) {
                var value = ret["a:ChangedItemWithType"][2];
                if (value !== null && value.toString().endsWith("#"))
                    ret["a:ChangedItemWithType"][2] = value.substr(0, value.toString().length - 1);
            }
        } else {
            if (type === 2) {
                var value = ret["a:ChangedItemWithType"]["a:Value"];
                if (value !== null && value.toString().endsWith("#"))
                    ret["a:ChangedItemWithType"]["a:Value"] = value.substr(0, value.toString().length - 1);
            }
        }
    }
    return ret;
}

function SubscribeMany(tags, properties) {
    var iterationsCount = parseInt(tags.length / 500);
    var tagsToSend = [];
    var propertiesToSend = [];
    for (var iterationIndex = 0; iterationIndex < iterationsCount; iterationIndex++) {
        tagsToSend = [];
        propertiesToSend = [];
        for (var index = 0, tagIndex = 500 * iterationIndex; tagIndex < 500 + (500 * iterationIndex); tagIndex++, index++) {
            tagsToSend[index] = tags[tagIndex];
            propertiesToSend[index] = properties[tagIndex];
        }

        var pl = new SOAPClientParameters();
        pl.add("tags", tagsToSend);
        pl.add("properties", propertiesToSend);
        if (pageGuid == "") {
            pageGuid = guid();
        }
        pl.add("pageGuid", pageGuid);
        var subscribedProperties = SOAPClient.invoke(tagServiceUri, "SubscribeWithType", pl, tagNamespace, tagEndpointName, tagInterface, false, null);

        if ((subscribedProperties != null) && (subscribedProperties != undefined)) {
            for (var propIndex = 0; propIndex < subscribedProperties.length; propIndex++) {
                var key = subscribedProperties[propIndex]["a:Key"];
                var type = subscribedProperties[propIndex]["a:Type"];
                var value = subscribedProperties[propIndex]["a:Value"];
                if (key == undefined) {
                    key = subscribedProperties[propIndex][0];
                    type = subscribedProperties[propIndex][1];
                    value = subscribedProperties[propIndex][2];
                }
                if (key == undefined || propertyInfoToIndex[key.toUpperCase()] == undefined || ScriptTagValues[propertyInfoToIndex[key.toUpperCase()]] == null || ScriptTagValues[propertyInfoToIndex[key.toUpperCase()]] == undefined) {
                    propertyInfoToIndex[key.toString().toUpperCase()] = key.toString().toUpperCase();
                    indexToPropertyInfo[key.toString().toUpperCase()] = key.toString().toUpperCase();
                    switch (type.toString()) {
                        case "1":
                            ScriptTagValues[key.toString().toUpperCase()] = value === QualityNotGoodValue ? QualityNotGoodValue : parseInt(value);
                            break;
                        case "2":
                            if (value == null)
                                ScriptTagValues[key.toString().toUpperCase()] = "";
                            else {
                                if (value.toString().endsWith("#"))
                                    value = value.substr(0, value.toString().length - 1);
                                ScriptTagValues[key.toString().toUpperCase()] = value;
                            }
                            break;
                        case "3":
                            if (value === QualityNotGoodValue)
                                ScriptTagValues[key.toString().toUpperCase()] = QualityNotGoodValue;
                            else {
                                if (value.toString().indexOf(NumberDecimalSeparator) > -1)
                                    ScriptTagValues[key.toString().toUpperCase()] = parseFloat(value.toString().replace(NumberDecimalSeparator, "."));
                                else
                                    ScriptTagValues[key.toString().toUpperCase()] = parseFloat(value);
                            }
                            break;
                        case "5":
                            ScriptTagValues[key.toString().toUpperCase()] = parseDatetime(value);
                            break;
                        default:
                            if (value == null)
                                ScriptTagValues[key.toString().toUpperCase()] = "";
                            else
                                ScriptTagValues[key.toString().toUpperCase()] = value;
                            break;
                    }
                }
            }
        }
    }

    tagsToSend = [];
    propertiesToSend = [];
    for (var index = 0, tagIndex = 500 * iterationsCount; tagIndex < tags.length; tagIndex++, index++) {
        tagsToSend[index] = tags[tagIndex];
        propertiesToSend[index] = properties[tagIndex];
    }

    if (tagsToSend.length > 0) {
        var pl = new SOAPClientParameters();
        pl.add("tags", tagsToSend);
        pl.add("properties", propertiesToSend);
        if (pageGuid == "") {
            pageGuid = guid();
        }
        pl.add("pageGuid", pageGuid);
        var subscribedProperties = SOAPClient.invoke(tagServiceUri, "SubscribeWithType", pl, tagNamespace, tagEndpointName, tagInterface, false, null);

        if ((subscribedProperties != null) && (subscribedProperties != undefined)) {
            for (var propIndex = 0; propIndex < subscribedProperties.length; propIndex++) {
                var key = subscribedProperties[propIndex]["a:Key"];
                var type = subscribedProperties[propIndex]["a:Type"];
                var value = subscribedProperties[propIndex]["a:Value"];
                if (key == undefined) {
                    key = subscribedProperties[propIndex][0];
                    type = subscribedProperties[propIndex][1];
                    value = subscribedProperties[propIndex][2];
                }
                if (key == undefined || propertyInfoToIndex[key.toUpperCase()] == undefined || ScriptTagValues[propertyInfoToIndex[key.toUpperCase()]] == null || ScriptTagValues[propertyInfoToIndex[key.toUpperCase()]] == undefined) {
                    propertyInfoToIndex[key.toString().toUpperCase()] = key.toString().toUpperCase();
                    indexToPropertyInfo[key.toString().toUpperCase()] = key.toString().toUpperCase();
                    switch (type.toString()) {
                        case "1":
                            ScriptTagValues[key.toString().toUpperCase()] = value === QualityNotGoodValue ? QualityNotGoodValue : parseInt(value);
                            break;
                        case "2":
                            if (value == null)
                                ScriptTagValues[key.toString().toUpperCase()] = "";
                            else {
                                if (value.toString().length > 0 && value.toString()[value.toString().length - 1] == "#")
                                    value = value.substr(0, value.toString().length - 1);
                                ScriptTagValues[key.toString().toUpperCase()] = value;
                            }
                            break;
                        case "3":
                            if (value === QualityNotGoodValue)
                                ScriptTagValues[key.toString().toUpperCase()] = QualityNotGoodValue;
                            else {
                                if (value.toString().indexOf(NumberDecimalSeparator) > -1)
                                    ScriptTagValues[key.toString().toUpperCase()] = parseFloat(value.toString().replace(NumberDecimalSeparator, "."));
                                else
                                    ScriptTagValues[key.toString().toUpperCase()] = parseFloat(value);
                            }
                            break;
                        case "5":
                            ScriptTagValues[key.toString().toUpperCase()] = parseDatetime(value);
                            break;
                        default:
                            if (value == null)
                                ScriptTagValues[key.toString().toUpperCase()] = "";
                            else
                                ScriptTagValues[key.toString().toUpperCase()] = value;
                            break;
                    }
                }
            }
        }
    }
}

function Unsubscribe() {
    var pl = new SOAPClientParameters();
    pl.add("pageGuid", pageGuid);
    SOAPClient.invoke(tagServiceUri, "Unsubscribe", pl, tagNamespace, tagEndpointName, tagInterface, true, null);
}

function CheckData(tagCount) {
    var pl = new SOAPClientParameters();
    pl.add("pageGuid", pageGuid);
    SOAPClient.invoke(tagServiceUri, "CheckData", pl, tagNamespace, tagEndpointName, tagInterface, true, CheckDataAll_callBack);
}

function CheckDataAll_callBack(ul) {
    if (ul == null)
        return;

    var changedItems = ul[0];
    var needsReload = ul[1];
    var removedTags = ul[2];
    CheckRemoved(removedTags);
    CheckData_callBack(changedItems);
    CheckReload_callBack(needsReload);
}

function CheckData_callBack(ul) {
    if (ul == null)
        return;
    if (func != undefined && func.isArray(ul)) {
        for (var i = 0; i < ul.length; i++) {
            if (ul[i] != null) {
                var key = ul[i]["a:Key"];
                var type = ul[i]["a:Type"];
                var value = ul[i]["a:Value"];
                if (key == undefined) {
                    key = ul[i][0];
                    type = ul[i][1];
                    value = ul[i][2];
                }
                if (type == 2 && value !== null) {
                    if (value.toString().length > 0 && value.toString()[value.toString().length - 1] == "#") {                 
                        value = value.substr(0, value.toString().length - 1);
                    }
                }
                if (key == null || value == null)
                    continue;
                var removedExpressions = RemoveExpressions(key);
                if (ScriptTagValues[key.toString()] != undefined) {
                    switch (type.toString()) {
                        case "1":
                            ScriptTagValues[key.toString()] = value === QualityNotGoodValue ? QualityNotGoodValue : parseInt(value);
                            break;
                        case "3":
                            if (value === QualityNotGoodValue)
                                ScriptTagValues[key.toString()] = QualityNotGoodValue;
                            else {
                                if (value.toString().indexOf(NumberDecimalSeparator) > -1)
                                    ScriptTagValues[key.toString()] = parseFloat(value.toString().replace(NumberDecimalSeparator, "."));
                                else
                                    ScriptTagValues[key.toString()] = parseFloat(value);
                            }
                            break;
                        case "5":
                            ScriptTagValues[key.toString()] = parseDatetime(value);
                            break;
                        default:
                            ScriptTagValues[key.toString()] = value;
                            break;
                    }
                }
                ResubscribeExpression(removedExpressions);
            }

            ExecuteTagExpressions(key);
            ExecuteScreenTagExpressions(key);
        }
    }
    else {
        var key = ul["a:ChangedItemWithType"]["a:Key"];
        var type = ul["a:ChangedItemWithType"]["a:Type"];
        var value = ul["a:ChangedItemWithType"]["a:Value"];
        if (key == undefined) {
            key = ul["a:ChangedItemWithType"][0];
            type = ul["a:ChangedItemWithType"][1];
            value = ul["a:ChangedItemWithType"][2];
        }
        if (type == 2 && value !== null) {
            if (value.toString().length > 0 && value.toString()[value.toString().length - 1] == "#") {
                value = value.substr(0, value.toString().length - 1);
            }
        }
        if (key == null || value == null)
            return;
        var removedExpressions = RemoveExpressions(key);
        if (ScriptTagValues[key.toString()] != undefined) {
            switch (type.toString()) {
                case "1":
                    ScriptTagValues[key.toString()] = value === QualityNotGoodValue ? QualityNotGoodValue : parseInt(value);
                    break;
                case "3":
                    if (value === QualityNotGoodValue)
                        ScriptTagValues[key.toString()] = QualityNotGoodValue;
                    else {
                        if (value.toString().indexOf(NumberDecimalSeparator) > -1)
                            ScriptTagValues[key.toString()] = parseFloat(value.toString().replace(NumberDecimalSeparator, "."));
                        else
                            ScriptTagValues[key.toString()] = parseFloat(value);
                    }
                    break;
                case "5":
                    ScriptTagValues[key.toString()] = parseDatetime(value);
                    break;
                default:
                    ScriptTagValues[key.toString()] = value;
                    break;
            }
        }
        ResubscribeExpression(removedExpressions);

        ExecuteTagExpressions(key);
        ExecuteScreenTagExpressions(key);
    }
}

function CheckReload_callBack(ul) {
    if (ul && reloadMethodName != "") {
        window[reloadMethodName]();
    }
}

function CheckRemoved(ul) {
    if (ul == null)
        return;
    if (func != undefined && func.isArray(ul)) {
        for (var i = 0; i < ul.length; i++) {
            if (ScriptTagValues[ul[i].toString()] != undefined)
                ScriptTagValues[ul[i].toString()] = null;
        }
    }
}

function CallCheckData() {
    CheckServer();
    if (tagServiceIsOnline)
        CheckData(500);
}

function CheckServer() {
    var func;
    if (jQuery != undefined)
        func = jQuery;
    else
        func = J;
    func.ajax({
        type: 'GET',
        dataType: 'text',
        url: tagServiceUri,
        success: function (data, textStatus) {
            if (!tagServiceIsOnline)
                location.reload();
            if (block)
                BlockScreen("The runtime has the exceeded the number of clients. Please upgrade your license or close others clients.");
            tagServiceIsOnline = true;
        },
        error: function (xhr, textStatus, errorThrown) {
            if (tagServiceIsOnline)
                BlockScreen(connectionErrorMessage);
            tagServiceIsOnline = false;
        }
    });
}

function BlockScreen(message) {
    var htmlToInsert = '<div class="block">&nbsp;</div>';
    htmlToInsert += '<div id="divBlockScreen" class="blockMessage">' + message + '</div>';
    jQuery("body").append(htmlToInsert);
    jQuery("body").addClass('block');
    deleteCookie("currentUser");
}

function GetMTVRowConfiguration(dataSource, columnsPaths, lastIndex) {
    var pl = new SOAPClientParameters();
    pl.add("dataSource", dataSource);
    pl.add("columnsPaths", columnsPaths);
    pl.add("lastIndex", lastIndex);
    var sRet = SOAPClient.invoke(tagServiceUri, "GetMTVRowConfiguration", pl, tagNamespace, tagEndpointName, tagInterface, false, null);
    return sRet;
}

function GetMatrixGridConfiguration(dataSource) {
    var pl = new SOAPClientParameters();
    pl.add("dataSource", dataSource);
    var sRet = SOAPClient.invoke(tagServiceUri, "GetMatrixGridConfiguration", pl, tagNamespace, tagEndpointName, tagInterface, false, null);
    return sRet;
}

function GetTagPropertiesIndexes(tagName, properties) {
    var pl = new SOAPClientParameters();
    pl.add("tagName", tagName);
    pl.add("properties", properties);
    var sRet = SOAPClient.invoke(tagServiceUri, "GetTagPropertiesIndexes", pl, tagNamespace, tagEndpointName, tagInterface, false, null);
    return sRet;
}

function GetTagType(tagName) {
    var pl = new SOAPClientParameters();
    pl.add("tagName", tagName);
    var sRet = SOAPClient.invoke(tagServiceUri, "GetTagType", pl, tagNamespace, tagEndpointName, tagInterface, false, null);
    return sRet;
}

onScreenClose = function () {

}

function OnScreenClose(screenName) {
    try {
        Unsubscribe();
    }
    catch (ex) { }
}

function WriteTag(tagName, tagProperty, id, gObjPropName) {
    tagName = tagName.toUpperCase();
    var startPos = tagName.lastIndexOf("SCRIPTTAGVALUES[GETKEY(");
    while (startPos != -1) {
        endPos = tagName.indexOf(")", startPos);
        innerProp = tagName.substring(startPos + "SCRIPTTAGVALUES[GETKEY(".length, endPos);
        while ((innerProp.charAt(0) == "\"") || (innerProp.charAt(0) == "\'"))
            innerProp = innerProp.substring(1, innerProp.length - 1);
        innerPropValue = ScriptTagValues[GetKey(innerProp)];
        tagName = tagName.replace("SCRIPTTAGVALUES[GETKEY(\"" + innerProp + "\")]", innerPropValue.toString());
        startPos = tagName.lastIndexOf("SCRIPTTAGVALUES[GETKEY(");
    }

    startPos = tagName.indexOf("SCRIPTTAGVALUES[GETKEY(");
    while (startPos != -1) {
        endPos = tagName.indexOf(")", startPos);
        innerProp = tagName.substring(startPos + "SCRIPTTAGVALUES[GETKEY(".length, endPos);
        while ((innerProp.charAt(0) == "\"") || (innerProp.charAt(0) == "\'"))
            innerProp = innerProp.substring(1, innerProp.length - 1);

        innerPropValue = ScriptTagValues[GetKey(innerProp)];
        tagName = tagName.replace("SCRIPTTAGVALUES[GETKEY(\"" + innerProp + "\")]", innerPropValue.toString());
        startPos = tagName.indexOf("SCRIPTTAGVALUES[GETKEY(", endPos);
    }

    var propertyInfo = tagName + ";" + tagProperty;
    if (propertyInfoToIndex[propertyInfo.toString().toUpperCase()] == undefined) {
        ul = Subscribe(tagName, tagProperty);
        if (ul != null && (ul["a:ChangedItemWithType"])["a:Value"] != undefined) {
            indexToPropertyInfo[ul["a:ChangedItemWithType"]["a:Key"]] = propertyInfo.toString().toUpperCase();
            propertyInfoToIndex[propertyInfo.toString().toUpperCase()] = ul["a:ChangedItemWithType"]["a:Key"];
        }
        else if (ul != null && (ul["a:ChangedItemWithType"])[2] != undefined) {
            indexToPropertyInfo[ul["a:ChangedItemWithType"][0]] = propertyInfo.toString().toUpperCase();
            propertyInfoToIndex[propertyInfo.toString().toUpperCase()] = ul["a:ChangedItemWithType"][0];
        }
    }
    if (propertyInfoToIndex[propertyInfo.toString().toUpperCase()] != undefined)  // Tag exists in the Tag Server
    {
        if (document.getElementById(id) !== null) {
            var found = false;
            pl = new SOAPClientParameters();
            pl.add("property", propertyInfo);
            if (document.getElementById(id).nodeName == "DIV")
                pl.add("value", document.getElementById(id).textContent);
            else
                if (gObjPropName == "SelectedIndex") {
                    var sel = document.getElementById(id);
                    for (var i = 0, j = sel.options.length; i < j; ++i) {
                        if (sel.options[i].selected) {
                            pl.add("value", i.toString());
                            found = true;
                            break;
                        }
                    }

                    if (!found)
                        pl.add("value", (-1).toString());
                }
                else {
                    if (document.getElementById(id).value !== undefined)
                        pl.add("value", document.getElementById(id).value);
                    else
                        pl.add("value", document.getElementById(id).innerHTML);
                }

            ul = SOAPClient.invoke(tagServiceUri, "WriteTag", pl, tagNamespace, tagEndpointName, tagInterface, true, null);
        }
    }
}

function DirectWriteTag(tagName, tagProperty, value) {
    var propertyInfo = tagName + ";" + tagProperty;
    if (propertyInfoToIndex[propertyInfo.toString().toUpperCase()] == undefined) {
        ul = Subscribe(tagName, tagProperty);
        if (ul != null && (ul["a:ChangedItemWithType"])["a:Value"] != undefined) {
            indexToPropertyInfo[ul["a:ChangedItemWithType"]["a:Key"]] = propertyInfo.toString().toUpperCase();
            propertyInfoToIndex[propertyInfo.toString().toUpperCase()] = ul["a:ChangedItemWithType"]["a:Key"];
        }
        else if (ul != null && (ul["a:ChangedItemWithType"])[2] != undefined) {
            indexToPropertyInfo[ul["a:ChangedItemWithType"][0]] = propertyInfo.toString().toUpperCase();
            propertyInfoToIndex[propertyInfo.toString().toUpperCase()] = ul["a:ChangedItemWithType"][0];
        }
    }
    if (propertyInfoToIndex[propertyInfo.toString().toUpperCase()] != undefined)  // Tag exists in the Tag Server
    {
        pl = new SOAPClientParameters();
        pl.add("property", propertyInfo);
        pl.add("value", value);
        ul = SOAPClient.invoke(tagServiceUri, "WriteTag", pl, tagNamespace, tagEndpointName, tagInterface, true, null);
    }
}

function WriteScriptTag(propertyIndex, value) {
    if (value !== undefined) {
        pl = new SOAPClientParameters();
        pl.add("property", propertyIndex);
        pl.add("value", value);
        ul = SOAPClient.invoke(tagServiceUri, "WriteTag", pl, tagNamespace, tagEndpointName, tagInterface, true, null);
    }
}

function WriteScreenTag(fullTagName, tagPropertyInfo, id, gObjPropName) {
    if (tagPropertyInfo != null) {
        var parts = tagPropertyInfo.split(";");
        var tagName = fullTagName;
        if (fullTagName.indexOf(".") != -1 && fullTagName.substring(fullTagName.lastIndexOf(".") + 1) == parts[0]) {
            tagName = fullTagName.substring(0, fullTagName.lastIndexOf("."));
        }
        WriteTag(tagName, parts[1], id, gObjPropName);
    }
}

function DirectWriteScreenTag(fullTagName, tagProperty, value) {
    DirectWriteTag(fullTagName, tagProperty, value);
}

function SetScreenTag(screenTag, tag, executeExpression, initialSet) {
    if (!initialSet) {
        for (var key in tagToScreenTag) {
            if (tagToScreenTag[key.toString().toUpperCase()] == screenTag.toString().toUpperCase()) {
                delete tagToScreenTag[key.toString().toUpperCase()];
                break;
            }
        }
    }

    if (screenTag != undefined) {
        screenTagToTag[screenTag.toString().toUpperCase()] = tag.toString().toUpperCase();
        tagToScreenTag[tag.toString().toUpperCase()] = screenTag.toString().toUpperCase();
    }

    if (executeExpression) {
        // Execute screen tag expressions
        for (var key in screenTagToExprList) {
            var parts = key.split("#");
            if (parts[0] == screenTag.toString().toUpperCase()) {
                var exprToExecute = screenTagToExprList[key.toString().toUpperCase()];
                for (var i = 0; i < exprToExecute.length; i++) {
                    window[exprToExecute[i]]();
                }
            }
        }
    }

    for (var key in expressionsWithTagIndex) {
        var parts = expressionsWithTagIndex[key].split("$");
        for (var index in parts) {
            if (parts[index].indexOf("#") != -1 && parts[index].split("#")[0] == screenTag.toString().toUpperCase()) {
                RemoveExpressionToList(expressionsWithTagIndex[key]);
                SubscribeExpression(expressionsWithTagIndex[key]);
                break;
            }
        }
    }
}

function SOAPClientParameters() {
    var _pl = new Array();
    this.add = function (name, value) {
        _pl[name] = value;
        return this;
    }
    this.toXml = function () {
        var xml = "";
        for (var p in _pl)
            xml += "<" + p + ">" + SOAPClientParameters._serialize(_pl[p]) + "</" + p + ">";
        return xml;
    }
}
//The code simply consists of an internal dictionary (associative array) with parameter name (key) and related value
//The add method allows appending new parameters, while toXml method - by calling the recursive function SOAPClientParameters._serialize - provides XML serialization for SOAP request (see SOAPClient._sendSoapRequest):
SOAPClientParameters._serialize = function (o) {
    var s = "";
    switch (typeof (o)) {
        case "string":
            s += o.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;"); break;
        case "number":
        case "boolean":
            s += o.toString(); break;
        case "object":
            // Date
            if (o.constructor.toString().indexOf("function Date()") > -1) {
                var year = o.getFullYear().toString();
                var month = (o.getMonth() + 1).toString(); month = (month.length == 1) ? "0" + month : month;
                var date = o.getDate().toString(); date = (date.length == 1) ? "0" + date : date;
                var hours = o.getHours().toString(); hours = (hours.length == 1) ? "0" + hours : hours;
                var minutes = o.getMinutes().toString(); minutes = (minutes.length == 1) ? "0" + minutes : minutes;
                var seconds = o.getSeconds().toString(); seconds = (seconds.length == 1) ? "0" + seconds : seconds;
                var milliseconds = o.getMilliseconds().toString();
                var tzminutes = Math.abs(o.getTimezoneOffset());
                var tzhours = 0;
                while (tzminutes >= 60) {
                    tzhours++;
                    tzminutes -= 60;
                }
                tzminutes = (tzminutes.toString().length == 1) ? "0" + tzminutes.toString() : tzminutes.toString();
                tzhours = (tzhours.toString().length == 1) ? "0" + tzhours.toString() : tzhours.toString();
                var timezone = ((o.getTimezoneOffset() < 0) ? "+" : "-") + tzhours + ":" + tzminutes;
                s += year + "-" + month + "-" + date + "T" + hours + ":" + minutes + ":" + seconds + "." + milliseconds + timezone;
            }
            // Array
            else if (o.constructor.toString().indexOf("function Array()") > -1) {
                for (var p in o) {
                    if (!isNaN(p)) // linear array
                    {
                        (/function\s+(\w*)\s*\(/ig).exec(o[p].constructor.toString());
                        var type = RegExp.$1;
                        switch (type) {
                            case "":
                                type = typeof (o[p]);
                            case "String":
                                type = "string"; break;
                            case "Number":
                                type = "int"; break;
                            case "Boolean":
                                type = "bool"; break;
                            case "Date":
                                type = "DateTime"; break;
                        }
                        s += '<' + type + ' xmlns="http://schemas.microsoft.com/2003/10/Serialization/Arrays">' + SOAPClientParameters._serialize(o[p]) + "</" + type + ">"
                    }
                    else // associative array
                        s += "<" + p + ">" + SOAPClientParameters._serialize(o[p]) + "</" + p + ">"
                }
            }
            // Object or custom function
            else
                for (var p in o)
                    s += "<" + p + ">" + SOAPClientParameters._serialize(o[p]) + "</" + p + ">";
            break;
        default:
            throw new Error(500, "SOAPClientParameters: type '" + typeof (o) + "' is not supported");
    }
    return s;
}
//Definition of the SOAPClient class, which can only contain static methods in order to allow async calls, and the only "public" method within this class: SOAPClient.invoke
//NOTE: since JavaScript does not foresee access modifiers - such as "public", "private", "protected", etc. - we'll use the "_" prefix to indicate private methods.
function SOAPClient() { }
SOAPClient.invoke = function (url, method, parameters, namespace, endpointName, elementInterface, async, callback) {
    if (async)
        SOAPClient._loadWsdl(url, method, parameters, namespace, endpointName, elementInterface, async, callback);
    else
        return SOAPClient._loadWsdl(url, method, parameters, namespace, endpointName, elementInterface, async, callback);
}
//SOAPClient.invoke method elementInterface is described above; our implementation checks whether the call is async (call result will be passed to the callback method) or sync (call result will be directly returned). The call to the Web Service begins by invoking SOAPClient._loadWsdl method:
SOAPClient._loadWsdl = function (url, method, parameters, namespace, endpointName, elementInterface, async, callback) {
    // load from cache?
    var wsdl = SOAPClient_cacheWsdl[url];
    if (wsdl != undefined)
        return SOAPClient._sendSoapRequest(url, method, parameters, namespace, endpointName, elementInterface, async, callback, wsdl);
    // get wsdl
    var xmlHttp = SOAPClient._getXmlHttp();
    xmlHttp.open("GET", url + "?wsdl", async);
    if (async) {
        xmlHttp.onreadystatechange = function () {
            if (xmlHttp.readyState == 4)
                SOAPClient._onLoadWsdl(url, method, parameters, namespace, endpointName, elementInterface, async, callback, xmlHttp);
        }
    }
    xmlHttp.send(null);
    if (!async)
        return SOAPClient._onLoadWsdl(url, method, parameters, namespace, endpointName, elementInterface, async, callback, xmlHttp);
}
//The method searches the cache for the same WSDL in order to avoid repetitive calls:
SOAPClient_cacheWsdl = new Array();
//If the WSDL is not found in the cache (it's the first call in current context) it is requested to the server by XMLHttpRequest, according the required mode (sync or not). Once an answer is obtained from the server, the SOAPClient._onLoadWsdl method is invoked:
SOAPClient._onLoadWsdl = function (url, method, parameters, namespace, endpointName, elementInterface, async, callback, req) {
    var wsdl = req.responseXML;
    SOAPClient_cacheWsdl[url] = wsdl;    // save a copy in cache
    return SOAPClient._sendSoapRequest(url, method, parameters, namespace, endpointName, elementInterface, async, callback, wsdl);
}
//A WSDL copy is stored into the cache and then the SOAPClient._sendSoapRequest method is executed
SOAPClient._sendSoapRequest = function (url, method, parameters, namespace, endpointName, elementInterface, async, callback, wsdl) {
    // get namespace
    //var ns = (wsdl.documentElement.attributes["targetNamespace"] + "" == "undefined") ? wsdl.documentElement.attributes.getNamedItem("targetNamespace").nodeValue : wsdl.documentElement.attributes["targetNamespace"].value;
    var ns = namespace;
    // build SOAP request
    var sr = '<?xml version="1.0" encoding="utf-8"?>' + '<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" ' + 'xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ' + 'xmlns:xsd="http://www.w3.org/2001/XMLSchema"> ' + '<soap:Body>' + '<' + method + ' xmlns="' + ns + '">' + parameters.toXml() + '</' + method + '></soap:Body></soap:Envelope>';
    // send request
    var xmlHttp = SOAPClient._getXmlHttp();
    xmlHttp.open("POST", url + "/" + endpointName, async);
    var soapaction = ((ns.lastIndexOf("/") != ns.length - 1) ? ns + "/" : ns) + elementInterface + "/" + method;
    xmlHttp.setRequestHeader("SOAPAction", soapaction);
    xmlHttp.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
    if (async) {
        xmlHttp.onreadystatechange = function () {
            if (xmlHttp.readyState == 4)
                SOAPClient._onSendSoapRequest(method, async, callback, wsdl, xmlHttp);
        }
    }
    xmlHttp.send(sr);
    if (!async)
        return SOAPClient._onSendSoapRequest(method, async, callback, wsdl, xmlHttp);
}
//The service namespace is taken out of the WSDL (using different XPath queries for Internet Explorer and Mozilla / FireFox), then a SOAP v. 1.1 request is created and submitted. The SOAPClient._onSendSoapRequest method will be invoked upon receiving the server response:
SOAPClient._onSendSoapRequest = function (method, async, callback, wsdl, req) {
    var o = null;
    var xmlDoc = req.responseXML;
    if (xmlDoc == null) {
        var parser = new DOMParser();
        xmlDoc = parser.parseFromString(req.responseText, "application/xml");
    }
    var nd = SOAPClient._getElementsByTagName(xmlDoc, method + "Result");
    if (nd.length == 0) {
        if (xmlDoc.getElementsByTagName("faultcode").length > 0) {
            if (async || callback)
                o = new Error(500, xmlDoc.getElementsByTagName("faultstring")[0].childNodes[0].nodeValue);
            else
                throw new Error(500, xmlDoc.getElementsByTagName("faultstring")[0].childNodes[0].nodeValue);
        }
    }
    else
        o = SOAPClient._soapresult2object(nd[0], wsdl);
    if (callback)
        callback(o, xmlDoc);
    if (!async)
        return o;
}
//The server response is processed looking for faults: if found, an error is raised. Instead, if a correct result is obtained, a recursive function will generate the return type by using the service description:
SOAPClient._soapresult2object = function (node, wsdl) {
    var wsdlTypes = SOAPClient._getTypesFromWsdl(wsdl);
    return SOAPClient._node2object(node, wsdlTypes);
}
SOAPClient._node2object = function (node, wsdlTypes) {
    // null node
    if (node == null)
        return null;
    // text node
    if (node.nodeType == 3 || node.nodeType == 4)
        return SOAPClient._extractValue(node, wsdlTypes);
    // leaf node
    if (node.childNodes.length == 1 && (node.childNodes[0].nodeType == 3 || node.childNodes[0].nodeType == 4))
        return SOAPClient._node2object(node.childNodes[0], wsdlTypes);
    var isarray = SOAPClient._getTypeFromWsdl(node.nodeName, wsdlTypes).toLowerCase().indexOf("arrayof") != -1;
    if (isarray == false && node.childNodes.length > 1 && node.firstChild.baseName == node.lastChild.baseName)
        isarray = true;
    // object node
    if (!isarray) {
        var obj = null;
        if (node.hasChildNodes())
            obj = new Object();
        for (var i = 0; i < node.childNodes.length; i++) {
            var p = SOAPClient._node2object(node.childNodes[i], wsdlTypes);
            obj[node.childNodes[i].nodeName] = p;
            var v = obj[node.childNodes[i].nodeName];
        }
        return obj;
    }
    // list node
    else {
        // create node ref
        var l = new Array();
        for (var i = 0; i < node.childNodes.length; i++) {
            l[l.length] = SOAPClient._node2object(node.childNodes[i], wsdlTypes);
        }
        return l;
    }
    return null;
}

function parseDatetime(text) {
    if (text == undefined)
        return "";

    if (text.length > 0) {
        if (text[0] == "\"")
            text = text.substring(1);

        if (text.length > 0) {
            if (text[text.length - 1] == "\"")
                text = text.substring(0, text.length - 1);
        }
    }

    if (text.length > 0 && text.search(" ") > -1) {
        var date = text.split(" ")[0];

        if (date == "01-01-0001" || date == "0001-01-01" || date == "01-01-01" ||
            date == "01/01/0001" || date == "0001/01/01" || date == "01/01/01")
            text = "";
    }

    //var format = projectDateTimeFormat.replace('tt', 'a');
    //return Date.parseString(text, format);

    var reformat = ReformatDate(text, "");
    return reformat;
}

SOAPClient._validDate = function (text) {
    var date = Date.parse(text);
    if (isNaN(date)) {
        return null;
    }

    var comp = text.split(DateSeparator);
    if (comp.length !== 3) {
        return null;
    }
    var m = parseInt(comp[0]);
    var d = parseInt(comp[1]);
    var yearTime = comp[2];
    //var time = yearTime.split(" ");
    var y = parseInt((yearTime.length > 0 ? yearTime[0] : yearTime));
    var date = new Date(y, m - 1, d);
    var sTime = null;
    if (yearTime.length > 1) {
        sTime = yearTime[1] + (yearTime.length > 2 ? (" " + yearTime[2]) : "");
    }
    if (date.getFullYear() == y && date.getMonth() + 1 == m && date.getDate() == d)
        return m.toString() + DateSeparator + d.toString() + DateSeparator + y.toString() + (sTime != null ? sTime : "");
    else
        return null;
}
SOAPClient._extractValue = function (node, wsdlTypes) {
    var value = node.nodeValue;
    switch (SOAPClient._getTypeFromWsdl(node.parentNode.nodeName, wsdlTypes).toLowerCase()) {
        default:
        case "s:string":
            if (value.toString().toUpperCase() == "TRUE")
                return true;
            else if (value.toString().toUpperCase() == "FALSE")
                return false;
            else if (SOAPClient._validDate(value) != null)
                return SOAPClient._validDate(value);
            else if (!isNaN(value)) {
                if (value == null)
                    return 0;
                if (value.toString().indexOf(NumberDecimalSeparator) > -1 || value.toString().indexOf("." > -1))
                    return parseFloat(value.toString().replace(NumberDecimalSeparator, "."));
                else
                    return (value != null) ? parseInt(value + "") : 0;
            }
            else
                return (value != null) ? value + "" : "";
        case "s:boolean":
            return value + "" == "true";
        case "s:int":
        case "s:long":
            return (value != null) ? parseInt(value + "") : 0;
        case "s:double":
            if (value != null) {
                if (value.toString().indexOf(NumberDecimalSeparator) > -1)
                    return parseFloat(value.toString().replace(NumberDecimalSeparator, "."));
                else
                    return parseFloat(value);
            }
            else
                return 0;
        case "s:datetime":
            if (value == null)
                return null;
            else {
                value = value + "";
                value = value.substring(0, (value.lastIndexOf(".") == -1 ? value.length : value.lastIndexOf(".")));
                value = value.replace(/T/gi, " ");
                value = value.replace(/-/gi, "/");
                var d = new Date();
                d.setTime(Date.parse(value));
                return d;
            }
    }
}
SOAPClient._getTypesFromWsdl = function (wsdl) {
    var wsdlTypes = new Array();
    // IE
    var ell = wsdl.getElementsByTagName("s:element");
    var useNamedItem = true;
    // MOZ
    if (ell.length == 0) {
        ell = wsdl.getElementsByTagName("element");
        useNamedItem = false;
    }
    for (var i = 0; i < ell.length; i++) {
        if (useNamedItem) {
            if (ell[i].attributes.getNamedItem("name") != null && ell[i].attributes.getNamedItem("type") != null)
                wsdlTypes[ell[i].attributes.getNamedItem("name").nodeValue] = ell[i].attributes.getNamedItem("type").nodeValue;
        }
        else {
            if (ell[i].attributes["name"] != null && ell[i].attributes["type"] != null)
                wsdlTypes[ell[i].attributes["name"].value] = ell[i].attributes["type"].value;
        }
    }
    return wsdlTypes;
}
SOAPClient._getTypeFromWsdl = function (elementname, wsdlTypes) {
    var type = wsdlTypes[elementname] + "";
    return (type == "undefined") ? "" : type;
}
//The SOAPClient._getElementsByTagName method optimizes XPath queries according to the available XML parser:
SOAPClient._getElementsByTagName = function (document, tagName) {
    try {
        // trying to get node omitting any namespaces (latest versions of MSXML.XMLDocument)
        return document.selectNodes(".//*[local-name()=\"" + tagName + "\"]");
    }
    catch (ex) { }
    // old XML parser support
    return document.getElementsByTagName(tagName);
}
//A factory function returns the XMLHttpRequest according to browser type:
SOAPClient._getXmlHttp = function () {
    try {
        if (window.XMLHttpRequest) {
            var req = new XMLHttpRequest();
            // some versions of Moz do not support the readyState property and the onreadystate event so we patch it!
            if (req.readyState == null) {
                req.readyState = 1;
                req.addEventListener("load", function () {
                    req.readyState = 4;
                    if (typeof req.onreadystatechange == "function")
                        req.onreadystatechange();
                }, false);
            }
            return req;
        }
        if (window.ActiveXObject)
            return new ActiveXObject(SOAPClient._getXmlHttpProgID());
    }
    catch (ex) { }
    throw new Error("Your browser does not support XmlHttp objects");
}

SOAPClient._getXmlHttpProgID = function () {
    if (SOAPClient._getXmlHttpProgID.progid)
        return SOAPClient._getXmlHttpProgID.progid;
    var progids = ["Msxml2.XMLHTTP.5.0", "Msxml2.XMLHTTP.4.0", "MSXML2.XMLHTTP.3.0", "MSXML2.XMLHTTP", "Microsoft.XMLHTTP"];
    var o;
    for (var i = 0; i < progids.length; i++) {
        try {
            o = new ActiveXObject(progids[i]);
            return SOAPClient._getXmlHttpProgID.progid = progids[i];
        }
        catch (ex) { };
    }
    throw new Error("Could not find an installed XML parser");
}

function HasTagInIndex(property) {
    return property.toUpperCase().lastIndexOf("SCRIPTTAGVALUES[GETKEY(") != -1;
}

function GetKey(property) {
    var originalProperty = property = property.toUpperCase();
    if (originalProperty.indexOf("#") > -1) {
        var parts = originalProperty.split("#");
        var mainScreenTag = parts[0];
        var qualifiers = parts[1];
        var pl = new SOAPClientParameters();
        if (screenTagToTag[mainScreenTag.toString().toUpperCase()] == undefined) {
            if (screenTagToTag[originalProperty.toString().toUpperCase()] != undefined)
                pl.add("tagName", screenTagToTag[originalProperty.toString().toUpperCase()]);
            else
                return -1;
        }
        else
            pl.add("tagName", screenTagToTag[mainScreenTag.toString().toUpperCase()] + qualifiers);

        var prop = SOAPClient.invoke(tagServiceUri, "GetTagPropertyInfo", pl, tagNamespace, tagEndpointName, tagInterface, false, null);
        if (prop != undefined && prop != null) {
            var parts = prop.split(";");
            property = parts[0] + ";" + parts[2];
        }
    }

    startPos = property.indexOf("SCRIPTTAGVALUES[GETKEY(");
    while (startPos != -1) {
        endPos = property.indexOf(")", startPos);
        innerProp = property.substring(startPos + "SCRIPTTAGVALUES[GETKEY(".length, endPos);
        while ((innerProp.charAt(0) == "\"") || (innerProp.charAt(0) == "\'"))
            innerProp = innerProp.substring(1, innerProp.length - 1);

        innerPropValue = ScriptTagValues[GetKey(innerProp)];
        if ((innerPropValue != undefined) && (innerPropValue != "")) {
            if (property.includes("SCRIPTTAGVALUES[GETKEY(\"" + innerProp + "\")]")) {
                property = property.replace("SCRIPTTAGVALUES[GETKEY(\"" + innerProp + "\")]", innerPropValue.toString());
                startPos = property.indexOf("SCRIPTTAGVALUES[GETKEY(", startPos);
            }
        } else {
            break;
        }
    }
    if (property == undefined || propertyInfoToIndex[property.toUpperCase()] == undefined || ScriptTagValues[propertyInfoToIndex[property.toUpperCase()]] == null || ScriptTagValues[propertyInfoToIndex[property.toUpperCase()]] == undefined) {
        var parts = property.split(";");
        ul = Subscribe(parts[0], parseInt(parts[1]));
        if (ul != undefined) {
            var key = ul["a:ChangedItemWithType"]["a:Key"];
            var type = ul["a:ChangedItemWithType"]["a:Type"];
            var value = ul["a:ChangedItemWithType"]["a:Value"];
            if (key == undefined) {
                key = ul["a:ChangedItemWithType"][0];
                type = ul["a:ChangedItemWithType"][1];
                value = ul["a:ChangedItemWithType"][2];
            }

            if ((key != null) && (key != undefined)) {
                propertyInfoToIndex[property.toString().toUpperCase()] = key.toString().toUpperCase();
                indexToPropertyInfo[key.toString().toUpperCase()] = property.toString().toUpperCase();
                switch (type.toString()) {
                    case "1":
                        ScriptTagValues[key.toString().toUpperCase()] = value === QualityNotGoodValue ? QualityNotGoodValue : parseInt(value);
                        break;
                    case "3":
                        if (value === QualityNotGoodValue)
                            ScriptTagValues[key.toString().toUpperCase()] = QualityNotGoodValue;
                        else {
                            if (value.toString().indexOf(NumberDecimalSeparator) > -1)
                                ScriptTagValues[key.toString().toUpperCase()] = parseFloat(value.toString().replace(NumberDecimalSeparator, "."));
                            else
                                ScriptTagValues[key.toString().toUpperCase()] = parseFloat(value);
                        }
                        break;
                    case "5":
                        ScriptTagValues[key.toString().toUpperCase()] = parseDatetime(value);
                        break;
                    default:
                        if (value == null)
                            ScriptTagValues[key.toString().toUpperCase()] = "";
                        else
                            ScriptTagValues[key.toString().toUpperCase()] = value;
                        break;
                }
            }
        }
        else {
            propertyInfoToIndex[property.toString().toUpperCase()] = parts[0].toString().toUpperCase();
            ScriptTagValues[parts[0].toString().toUpperCase()] = parts[1];
        }
    }
    return propertyInfoToIndex[property.toString().toUpperCase()];
}

function GetTagNames(tagName) {
    var pl = new SOAPClientParameters();
    pl.add("tagName", tagName);
    var prop = SOAPClient.invoke(tagServiceUri, "GetTagNames", pl, tagNamespace, tagEndpointName, tagInterface, false, null);
    return prop;
}

function GetTagFullName(tagName) {
    var pl = new SOAPClientParameters();
    pl.add("tagName", tagName);
    var prop = SOAPClient.invoke(tagServiceUri, "GetTagFullName", pl, tagNamespace, tagEndpointName, tagInterface, false, null);
    return prop;
}

function GetScrenTagDefault(docName) {
    var pl = new SOAPClientParameters();
    pl.add("docName", docName);
    var screenTagName = SOAPClient.invoke(tagServiceUri, "GetScrenTagDefault", pl, tagNamespace, tagEndpointName, tagInterface, false, null);
    return screenTagName;
}
function GetScreenTagExpressions(item_index) {
    if (indexToPropertyInfo == null || indexToPropertyInfo.length <= item_index || tagToScreenTag.length == 0)
        return;
    var parts = indexToPropertyInfo[item_index.toString().toUpperCase()].split(";");
    var tagName = parts[0];
    var exprToExecute = tryGetScreenTagExpression(parts, tagName, true);
    if (exprToExecute == undefined)
        exprToExecute = tryGetScreenTagExpression(parts, tagName, false);
    return exprToExecute;
}

function tryGetScreenTagExpression(parts, tagName, arrayFirst) {
    var mainTagName = "";
    var qualifier = "";
    var exprToExecute;
    if (tagName.indexOf(".") != -1 || tagName.indexOf("[") != -1) {
        var indexDot = tagName.indexOf(".");
        var indexOpenArray = tagName.indexOf("[");
        var index;
        if (indexDot == -1)
            index = indexOpenArray;
        else if (indexOpenArray == -1)
            index = indexDot;
        else if (arrayFirst)
            index = indexOpenArray;
        else
            index = indexDot;
        mainTagName = tagName.substr(0, index);
        qualifier = tagName.substr(index);
    }
    else {
        mainTagName = tagName;
        qualifier = "." + GetTagPropertyName(parts[0], parts[1]);
    }
    qualifier = qualifier.toString().toUpperCase();
    if (tagToScreenTag[mainTagName.toString().toUpperCase()] != undefined) {
        var screenTagInfo = tagToScreenTag[mainTagName.toString().toUpperCase()] + "#" + qualifier;
        exprToExecute = screenTagToExprList[screenTagInfo.toString().toUpperCase()];
    } else if (tagToScreenTag[tagName.toString().toUpperCase()] != undefined) {
        qualifier = "." + GetTagPropertyName(parts[0], parts[1]);
        var screenTagInfo = tagToScreenTag[tagName.toString().toUpperCase()] + "#" + qualifier;
        exprToExecute = screenTagToExprList[screenTagInfo.toString().toUpperCase()];
    }
    return exprToExecute;
}
function RemoveExpressions(index) {
    var expressionsToResubscribe = new Array();
    expressionsToResubscribe = RemoveTagExpressions(index, expressionsToResubscribe);
    expressionsToResubscribe = RemoveScreenTagExpressions(index, expressionsToResubscribe);
    return expressionsToResubscribe;
}

function RemoveTagExpressions(index, removedExpressions) {
    var exprToExecute = indexToExprList[index.toString().toUpperCase()];
    if (exprToExecute != undefined) {
        for (i = exprToExecute.length - 1; i >= 0; i--) {
            var methodToExecute = exprToExecute[i];
            if (exprToExecute[i].indexOf(tagValueSeparator) != -1) {
                var exprSplitted = exprToExecute[i].split(tagValueSeparator);
                methodToExecute = exprSplitted[0];
            }
            if (expressionsWithTagIndex[methodToExecute]) {
                RemoveExpressionToList(expressionsWithTagIndex[methodToExecute]);
                removedExpressions[removedExpressions.length] = methodToExecute;
            }
        }
    }
    return removedExpressions;
}

function RemoveScreenTagExpressions(index, removedExpressions) {
    var exprToExecute = GetScreenTagExpressions(index)
    if (exprToExecute != undefined) {
        for (i = exprToExecute.length - 1; i >= 0; i--) {
            if (expressionsWithTagIndex[exprToExecute[i]]) {
                removedExpressions[removedExpressions.length] = exprToExecute[i];
                RemoveExpressionToList(expressionsWithTagIndex[exprToExecute[i]]);
            }
        }
    }
    return removedExpressions;
}

function ResubscribeExpression(expressions) {
    for (var index in expressions) {
        if (expressionsWithTagIndex[expressions[index]]) {
            console.log("Resubscribing method " + expressions[index] + " !! Args = " + expressionsWithTagIndex[expressions[index]]);
            SubscribeExpression(expressionsWithTagIndex[expressions[index]], false);
        }
    }
}

function ExecuteTagExpressions(index) {
    var exprToExecute = indexToExprList[index.toString().toUpperCase()];
    if (exprToExecute != undefined) {
        for (var i = 0; i < exprToExecute.length; i++) {
            var methodToExecute = exprToExecute[i];
            if (exprToExecute[i].indexOf(tagValueSeparator) != -1) {
                var exprSplitted = exprToExecute[i].split(tagValueSeparator);
                if (exprSplitted[0].indexOf(tagValueSeparatorCombobox) != -1) {
                    var splittedComboboxMethod = exprSplitted[0].split(tagValueSeparatorCombobox);
                    exprSplitted[0] = splittedComboboxMethod[0];
                }
                methodToExecute = exprSplitted[0];
                var parameter = exprSplitted[1]
                window[methodToExecute](parameter);
            } else {
                window[methodToExecute]();
            }
        }
    }
}

function ExecuteScreenTagExpressions(item_index) {
    var exprToExecute = GetScreenTagExpressions(item_index)
    if (exprToExecute != undefined) {
        for (iExec = 0; iExec < exprToExecute.length; iExec++) {
            if (exprToExecute[iExec].indexOf(tagValueSeparator) != -1) {
                var exprSplitted = exprToExecute[iExec].split(tagValueSeparator);
                if (exprSplitted[0].indexOf(tagValueSeparatorCombobox) != -1) {
                    var splittedComboboxMethod = exprSplitted[0].split(tagValueSeparatorCombobox);
                    exprSplitted[0] = splittedComboboxMethod[0];
                }
                methodToExecute = exprSplitted[0];
                var parameter = exprSplitted[1]
                window[methodToExecute](parameter);
            }
            else
                window[exprToExecute[iExec]]();
        }
    }
}

function UpdateAllTagsInServer() {
    for (var property in ScriptTagValues) {
        WriteScriptTag(property, ScriptTagValues[property]);
    }
}

function UpdateTagsInServer() {
    for (var i = 0; i < arguments.length; i++) {
        WriteScriptTag(arguments[i], ScriptTagValues[arguments[i]]);
    }
}

function S4() {
    return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1);
}

function guid() {
    return (S4() + S4() + "-" + S4() + "-" + S4() + "-" + S4() + "-" + S4() + S4() + S4());
}

function AddExpressionToList(arguments, onlySubscribe) {
    var args = arguments.split("$");
    if (args.length < 2)
        return;
    SubscribeExpression(arguments, true);
    if (onlySubscribe)
        return;
    if (args[0].indexOf(tagValueSeparator) != -1) {
        var splittedMethod = args[0].split(tagValueSeparator);
        if (splittedMethod[0].indexOf(tagValueSeparatorCombobox) != -1) {
            var splittedComboboxMethod = splittedMethod[0].split(tagValueSeparatorCombobox);
            splittedMethod[0] = splittedComboboxMethod[0];
        }
        if (window[splittedMethod[0]] !== undefined)
            window[splittedMethod[0]](splittedMethod[1]);
    } else {
        window[args[0]]();
    }
}

function SubscribeExpression(arguments, addToExpressionsWithTag) {
    var args = arguments.split("$");
    var index;
    var exprList;
    for (var i = 1; i < args.length; i++) {
        if (addToExpressionsWithTag && HasTagInIndex(args[i]) && !expressionsWithTagIndex[args[0]]) {
            expressionsWithTagIndex[args[0]] = arguments;
            console.log("Method " + args[0] + " has tag in index!! Args = " + arguments);
        }
        var dictionary;
        var hasScreenTag = args[i].indexOf("#") != -1;
        var screenTagIndex = args[i].indexOf("GetKey") != -1 && args[i].indexOf("GetKey") < args[i].indexOf("#");
        if ((hasScreenTag) && (!screenTagIndex)) {
            dictionary = screenTagToExprList;
            if (args[i].indexOf(".") != -1 || args[i].indexOf("[") != -1)
                index = args[i];
            else
                index = args[i] + ".VALUE";
        } else {
            dictionary = indexToExprList;
            index = GetKey(args[i].toUpperCase());
        }

        if (index != undefined && index != -1) {
            if (dictionary[index.toString().toUpperCase()] == undefined) {
                exprList = new Array();
                dictionary[index.toString().toUpperCase()] = exprList;
            }
            else
                exprList = dictionary[index.toString().toUpperCase()];

            var found = false;
            for (var j = 0; j < exprList.length; j++) {
                if (exprList[j] == args[0]) {
                    found = true;
                    break;
                }
            }

            if (!found)
                exprList[exprList.length] = args[0];
        }
        else if (args[i].toUpperCase().indexOf("GETKEY") > -1) {
            var startIndex = args[i].toUpperCase().indexOf("GETKEY(");
            while (startIndex > -1) {
                startIndex += ("GETKEY(").length;
                var endIndex = args[i].toUpperCase().indexOf(")]", startIndex);
                var tagName = args[i].toUpperCase().substr(startIndex, endIndex - startIndex);
                startIndex = args[i].toUpperCase().indexOf("GETKEY(", endIndex);

                if (((tagName[0] == "\'") || (tagName[0] == "\"")) &&
                    ((tagName[tagName.length - 1] == "\'") || (tagName[tagName.length - 1] == "\"")))
                    tagName = eval(tagName);

                index = GetKey(tagName.toUpperCase());
                if (index != undefined) {
                    if (dictionary[index.toString().toUpperCase()] == undefined) {
                        exprList = new Array();
                        dictionary[index.toString().toUpperCase()] = exprList;
                    }
                    else
                        exprList = dictionary[index.toString().toUpperCase()];

                    var found = false;
                    for (var j = 0; j < exprList.length; j++) {
                        if (exprList[j] == args[0]) {
                            found = true;
                            break;
                        }
                    }

                    if (!found)
                        exprList[exprList.length] = args[0];
                }
            }
        }
    }
}

function RemoveExpressionToList(arguments) {
    var args = arguments.split("$");
    if (args.length < 2)
        return;
    var index;
    var exprList;
    for (var i = 1; i < args.length; i++) {
        var dictionary;
        var hasScreenTag = args[i].indexOf("#") != -1;
        var screenTagIndex = args[i].indexOf("GetKey") != -1 && args[i].indexOf("GetKey") < args[i].indexOf("#");
        if (hasScreenTag && !screenTagIndex) {
            dictionary = screenTagToExprList;
            if (args[i].indexOf(".") != -1)
                index = args[i];
            else
                index = args[i] + ".VALUE";
        } else {
            dictionary = indexToExprList
            index = GetKey(args[i]);
        }
        if (index !== undefined) {
            if (dictionary[index.toString().toUpperCase()] == undefined) {
                exprList = new Array();
                dictionary[index.toString().toUpperCase()] = exprList;
            }
            else
                exprList = dictionary[index.toString().toUpperCase()];
            for (var j = 0; j < exprList.length; j++) {
                if (exprList[j] == args[0]) {
                    exprList.splice(j, 1);
                    break;
                }
            }
        }
    }
}

function GetTagFromScreenTag(screenTag) {
    var parts = screenTag.split("#");
    var tag = screenTagToTag[parts[0].toString().toUpperCase()];
    return tag + parts[1];
}

function GetTagPropertyName(tagName, propertyIndex) {
    var pl = new SOAPClientParameters();
    pl.add("tagName", tagName);
    pl.add("propertyIndex", propertyIndex);
    return SOAPClient.invoke(tagServiceUri, "GetTagPropertyName", pl, tagNamespace, tagEndpointName, tagInterface, false, null);
}

function GetScreenTagPropertyInfo(screenTag) {
    var parts = screenTag.split("#");
    var tag = screenTagToTag[parts[0].toString().toUpperCase()];
    var fullTag = tag + parts[1];
    if (fullTag != "undefined") {
        var pl = new SOAPClientParameters();
        pl.add("tagName", fullTag);
        var propInfo = SOAPClient.invoke(tagServiceUri, "GetTagPropertyInfo", pl, tagNamespace, tagEndpointName, tagInterface, false, null);
        if (propInfo != undefined && propInfo != null) {
            parts = propInfo.split(";");
            return parts[1] + ";" + parts[2];
        }
    }
}

function GetTagValues(tag, member) {
    var pl = new SOAPClientParameters();
    pl.add("tag", tag);
    pl.add("member", member);
    return SOAPClient.invoke(tagServiceUri, "GetTagValues", pl, tagNamespace, tagEndpointName, tagInterface, false, null);
}

function GetTagNamesFromMember(tagName, member) {
    var pl = new SOAPClientParameters();
    pl.add("tagName", tagName);
    pl.add("member", member);
    return SOAPClient.invoke(tagServiceUri, "GetTagNamesFromMember", pl, tagNamespace, tagEndpointName, tagInterface, false, null);
}

function generateAlarmHistItem(ul) {
    if (ul != null) {
        var parameters = new Array();
        parameters['Name'] = ul["a:TagName"];
        parameters['State'] = ul["a:State"];
        parameters['LastState'] = ul["a:LastState"];
        parameters['Type'] = ul["a:Type"];
        parameters['Priority'] = ul["a:Priority"];
        parameters['Message'] = ul["a:Message"];
        parameters['Group'] = ul["a:Group"];
        parameters['StartTime'] = "";
        parameters['AckTime'] = "";
        parameters['ReturnTime'] = "";

        if (parameters['Name'] == undefined) {
            parameters['Name'] = ul[0];
            parameters['State'] = ul[1];
            parameters['LastState'] = ul[2];
            parameters['Type'] = ul[3];
            parameters['Priority'] = ul[4];
            parameters['Message'] = ul[5];
            parameters['Group'] = ul[6];

            switch (parameters['State']) {
                case enumAlarmState.ActiveAck:
                    if (parameters['LastState'] == enumAlarmState.ReturnedUnack) {
                        if (ul.length < 9)
                            return;

                        parameters['StartTime'] = parseAlarmsDateTime(ul[7]);
                        parameters['AckTime'] = parseAlarmsDateTime(ul[8]);
                        parameters['ReturnTime'] = parseAlarmsDateTime(ul[9]);
                    }
                    else {
                        if (ul.length < 8)
                            return;

                        parameters['StartTime'] = parseAlarmsDateTime(ul[7]);
                        parameters['AckTime'] = parseAlarmsDateTime(ul[8]);
                    }
                    break;
                case enumAlarmState.ActiveUnack:
                    if (ul.length < 7)
                        return;

                    parameters['StartTime'] = parseAlarmsDateTime(ul[7]);
                    break;

                case enumAlarmState.ReturnedUnack:
                    if (parameters['LastState'] == enumAlarmState.ActiveAck) {
                        if (ul.length < 9)
                            return;

                        parameters['StartTime'] = parseAlarmsDateTime(ul[7]);
                        parameters['AckTime'] = parseAlarmsDateTime(ul[8]);
                        parameters['ReturnTime'] = parseAlarmsDateTime(ul[9]);
                    }
                    else {
                        if (ul.length < 8)
                            return;

                        parameters['StartTime'] = parseAlarmsDateTime(ul[7]);
                        parameters['ReturnTime'] = parseAlarmsDateTime(ul[9]);
                    }
                    break;

                case enumAlarmState.ReturnedAck:
                    if ((parameters['LastState'] == enumAlarmState.ActiveAck) ||
                        (parameters['LastState'] == enumAlarmState.ReturnedUnack)) {
                        if (ul.length < 9)
                            return;

                        parameters['StartTime'] = parseAlarmsDateTime(ul[7]);
                        parameters['AckTime'] = parseAlarmsDateTime(ul[8]);
                        parameters['ReturnTime'] = parseAlarmsDateTime(ul[9]);
                    }
                    else
                        return;
                    break;

                case enumAlarmState.None:
                    if (ul.length < 9)
                        return;

                    parameters['StartTime'] = parseAlarmsDateTime(ul[7]);
                    parameters['AckTime'] = parseAlarmsDateTime(ul[8]);
                    parameters['ReturnTime'] = parseAlarmsDateTime(ul[9]);
                    break;
                default:
                    return;
            }
        } else {
            switch (parameters['State']) {
                case enumAlarmState.ActiveAck:
                    if (parameters['LastState'] == enumAlarmState.ReturnedUnack) {
                        if (ul.length < 9)
                            return;

                        parameters['StartTime'] = parseAlarmsDateTime(ul["a:StartTime"]);
                        parameters['AckTime'] = parseAlarmsDateTime(ul["a:AckTime"]);
                        parameters['ReturnTime'] = parseAlarmsDateTime(ul["a:ReturnTime"]);
                    }
                    else {
                        if (ul.length < 8)
                            return;

                        parameters['StartTime'] = parseAlarmsDateTime(ul["a:StartTime"]);
                        parameters['AckTime'] = parseAlarmsDateTime(ul["a:AckTime"]);
                    }
                    break;

                case enumAlarmState.ActiveUnack:
                    if (ul.length < 7)
                        return;

                    parameters['StartTime'] = parseAlarmsDateTime(ul["a:StartTime"]);
                    break;

                case enumAlarmState.ReturnedUnack:
                    if (parameters['LastState'] == enumAlarmState.ActiveAck) {
                        if (ul.length < 9)
                            return;

                        parameters['StartTime'] = parseAlarmsDateTime(ul["a:StartTime"]);
                        parameters['AckTime'] = parseAlarmsDateTime(ul["a:AckTime"]);
                        parameters['ReturnTime'] = parseAlarmsDateTime(ul["a:ReturnTime"]);
                    }
                    else {
                        if (ul.length < 8)
                            return;

                        parameters['StartTime'] = parseAlarmsDateTime(ul["a:StartTime"]);
                        parameters['ReturnTime'] = parseAlarmsDateTime(ul["a:ReturnTime"]);
                    }
                    break;

                case enumAlarmState.ReturnedAck:
                    if ((parameters['LastState'] == enumAlarmState.ActiveAck) ||
                        (parameters['LastState'] == enumAlarmState.ReturnedUnack)) {
                        if (ul.length < 9)
                            return;

                        parameters['StartTime'] = parseAlarmsDateTime(ul["a:StartTime"]);
                        parameters['AckTime'] = parseAlarmsDateTime(ul["a:AckTime"]);
                        parameters['ReturnTime'] = parseAlarmsDateTime(ul["a:ReturnTime"]);
                    } else
                        return;
                    break;

                case enumAlarmState.None:
                    if (ul.length < 9)
                        return;

                    parameters['StartTime'] = parseAlarmsDateTime(ul["a:StartTime"]);
                    parameters['AckTime'] = parseAlarmsDateTime(ul["a:AckTime"]);
                    parameters['ReturnTime'] = parseAlarmsDateTime(ul["a:ReturnTime"]);
                    break;

                default:
                    return;
            }
        }

        // Tratamento para quando message ou group possuirem valor "null", para que seja exibido string vazia na coluna
        if (parameters['Message'] == null)
            parameters['Message'] = "";

        if (parameters['Group'] == null)
            parameters['Group'] = "";

        return parameters;
    }

    return null;
}

function parseAlarmsDateTime(date) {
    if (date !== null || date !== undefined)
        return date.replace(/\"/g, "");
}

function generateEventItem(ul) {
    if (ul != null) {
        var parameters = {}
        parameters['isEvent'] = true;
        if (ul["a:AlarmEventHistItem"] == undefined) {
            parameters['Priority'] = ul["a:Priority"];
            parameters['Message'] = ul["a:Message"];
            parameters['Group'] = ul["a:Group"];
            parameters['EventTime'] = ul["a:EventTime"];

            if (parameters['EventTime'] == undefined) {
                parameters['Priority'] = ul[4];
                parameters['Message'] = ul[5];
                parameters['Group'] = ul[6];
                parameters['EventTime'] = parseAlarmsDateTime(ul[10]);
            }
            else
                parameters['EventTime'] = parseAlarmsDateTime(ul["a:EventTime"]);
        }
        else {
            parameters['Priority'] = ul["a:AlarmEventHistItem"]["a:Priority"];
            parameters['Message'] = ul["a:AlarmEventHistItem"]["a:Message"];
            parameters['Group'] = ul["a:AlarmEventHistItem"]["a:Group"];
            parameters['EventTime'] = ul["a:AlarmEventHistItem"]["a:EventTime"];
            if (parameters['EventTime'] == undefined) {
                parameters['Priority'] = ul["a:AlarmEventHistItem"][4];
                parameters['Message'] = ul["a:AlarmEventHistItem"][5];
                parameters['Group'] = ul["a:AlarmEventHistItem"][6];
                parameters['EventTime'] = parseAlarmsDateTime(ul["a:AlarmEventHistItem"][10]);
            }
            else
                parameters['EventTime'] = parseAlarmsDateTime(ul["a:AlarmEventHistItem"]["a:EventTime"]);
        }

        // Tratamento para quando message possuir valor "null", para que seja exibido string vazia na coluna
        if (parameters['Message'] == null)
            parameters['Message'] = "";

        if (parameters['Group'] == null)
            parameters['Group'] = "";

        return parameters;
    }

    return null;
}

function getAlarmHistory(tagName, fields, start, end, isAbsolut, isForward, isBackward, relative, method) {
    pl = new SOAPClientParameters();
    pl.add("tagName", tagName);
    pl.add("fields", fields);
    pl.add("start", start);
    pl.add("end", end);
    pl.add("isAbsolut", isAbsolut);
    pl.add("isForward", isForward);
    pl.add("isBackward", isBackward);
    pl.add("relative", relative);
    ul = SOAPClient.invoke(tagServiceUri, "GetAlarmHistory", pl, tagNamespace, tagEndpointName, tagInterface, false, null);

    if (ul == null)
        return;

    var parameters = [];
    for (var i = 0; i < ul.length; i++) {
        if (ul[i] != null) {
            var parameter = generateAlarmHistItem(ul[i]);
            if (parameter !== null)
                parameters.push(parameter);
        }
    }
    window[method](parameters);
}

function getAlarmAndEventHistory(tagName, fields, start, end, isAbsolut, isForward, isBackward, relative, method) {
    pl = new SOAPClientParameters();
    pl.add("tagName", tagName);
    pl.add("fields", fields);
    pl.add("start", start);
    pl.add("end", end);
    pl.add("isAbsolut", isAbsolut);
    pl.add("isForward", isForward);
    pl.add("isBackward", isBackward);
    pl.add("relative", relative);
    ul = SOAPClient.invoke(tagServiceUri, "GetAlarmAndEventHistory", pl, tagNamespace, tagEndpointName, tagInterface, false, null);

    if (ul == null)
        return;

    var parameters = [];
    for (var i = 0; i < ul.length; i++) {
        if (ul[i] != null) {
            var objectType = ul[i]["a:ObjectType"];
            if (objectType == undefined)
                objectType = ul[i][11];

            if (objectType == "AlarmItem") {
                var parameter = generateAlarmHistItem(ul[i]);
                if (parameter !== null)
                    parameters.push(parameter);
            }
            else if (objectType == "EventItem") {
                var parameter = generateEventItem(ul[i]);
                if (parameter !== null)
                    parameters.push(parameter);
            }
        }
    }
    window[method](parameters);
}

function getEventHistory(method) {
    pl = new SOAPClientParameters();
    //	pl.add("start", start);
    //	pl.add("end", end);
    //	pl.add("isAbsolut", isAbsolut);
    //	pl.add("isForward", isForward);
    //	pl.add("isBackward", isBackward);
    //	pl.add("relative", relative);
    ul = SOAPClient.invoke(tagServiceUri, "GetEventHistory", pl, tagNamespace, tagEndpointName, tagInterface, false, null);

    if (ul == null)
        return;

    var parameters = [];
    for (var i = 0; i < ul.length; i++) {
        if (ul[i] != null) {
            var parameter = generateEventItem(ul[i]);
            if (parameter !== null)
                parameters.push(parameter);
        }
    }
    window[method](parameters);
}

var eventObjectsFunctions = new Array();

function SubscribeEventObject(methodName) {
    if (func.inArray(methodName, eventObjectsFunctions) != 0) {
        eventObjectsFunctions[eventObjectsFunctions.length] = methodName;
        var pl = new SOAPClientParameters();
        var tags = new Array();
        var propertyIndexes = new Array();
        if (pageGuid == "")
            pageGuid = guid();

        pl.add("tags", tags);
        pl.add("properties", propertyIndexes);
        pl.add("pageGuid", pageGuid);
        SOAPClient.invoke(tagServiceUri, "SubscribeWithType", pl, tagNamespace, tagEndpointName, tagInterface, false, null);
    }
}

function CheckEvents() {
    var pl = new SOAPClientParameters();
    pl.add("pageGuid", pageGuid);
    ul = SOAPClient.invoke(tagServiceUri, "CheckEvents", pl, tagNamespace, tagEndpointName, tagInterface, false, null);

    if (ul == null)
        return;

    if (func.isArray(ul)) {
        for (var i = 0; i < ul.length; i++) {
            if (ul[i] != null) {
                for (var j = 0; j < eventObjectsFunctions.length; j++) {
                    var parameter = generateEventItem(ul[i]);
                    if (parameter !== null) {
                        var parameters = [];
                        parameters.push(parameter);
                        window[eventObjectsFunctions[j]](parameters);
                    }
                }
            }
        }
    }
    else {
        for (var j = 0; j < eventObjectsFunctions.length; j++) {
            var parameter = generateEventItem(ul);
            if (parameter !== null) {
                var parameters = [];
                parameters.push(parameter);
                window[eventObjectsFunctions[j]](parameters);
            }
        }
    }
}

function loadCookies() {
    var cr = [];
    if (document.cookie != '') {
        var ck = document.cookie.split('; ');
        for (var i = 0; i < ck.length; i++) {
            var equalsIndex = ck[i].indexOf('=');
            if (equalsIndex > 0) {
                var cv = [ck[i].split('=')[0], ck[i].substring(equalsIndex + 1)];
                if (cr[cv[0]] == undefined || (cv[1] != undefined && cv[1] != ''))
                    cr[cv[0]] = cv[1];
            }
        }
    }
    return cr;
}

var deleteCookie = function (name) {
    document.cookie = name + '=;expires=Thu, 01 Jan 1970 00:00:01 GMT;';
};

function GetStartupScreenName() {
    var pl = new SOAPClientParameters();
    return SOAPClient.invoke(helpersServiceUri, "GetStartupScreenName", pl, helpersNamespace, helpersEndpointName, helpersInterface, false, null);
}

$(document).ready(function () {
    var func;
    if ($ != undefined)
        func = $("#TagServer_ButtonSubmit");
    else
        func = J("#TagServer_ButtonSubmit");
    func.click(function () {
        if (window.location.href.substr(0, 8) == "file:///")
            webServerAddress = "localhost:8080";
        else
            webServerAddress = window.location.href.substr(7, window.location.href.substring(7).indexOf("/"));
        if (document.getElementById("TagServer_TextBoxID").value.length > 0) {
            tagServerAddress = document.getElementById("TagServer_TextBoxID").value + ":8080";
            document.cookie = 'tagServerAddress=' + tagServerAddress;
        }
        isLocalTagServer = "false";
        document.cookie = 'isLocalTagServer=' + isLocalTagServer;
        var startupScreen = GetStartupScreenName() + ".html";
        if (startupScreen != "")
            window.location.href = "http://" + webServerAddress + "/" + phiWebFolder + "/" + projectName + "/" + startupScreen;
    });
});

function FileExitsJS(path) {
    var pl = new SOAPClientParameters();
    pl.add("path", path);
    return SOAPClient.invoke(helpersServiceUri, "FileExists", pl, helpersNamespace, helpersEndpointName, helpersInterface, false, null);
}

function FileCreateJS(path) {
    var pl = new SOAPClientParameters();
    pl.add("path", path);
    SOAPClient.invoke(helpersServiceUri, "FileStreamCreate", pl, helpersNamespace, helpersEndpointName, helpersInterface, false, null);
    var _fileStream = new FileStream();
    return _fileStream;
}

function CloseFileStream() {
    var pl = new SOAPClientParameters();
    return SOAPClient.invoke(helpersServiceUri, "CloseFileStream", pl, helpersNamespace, helpersEndpointName, helpersInterface, false, null);
}

function FileAppendTextJS(path) {
    var pl = new SOAPClientParameters();
    pl.add("path", path);
    SOAPClient.invoke(helpersServiceUri, "FileAppendText", pl, helpersNamespace, helpersEndpointName, helpersInterface, false, null);
    var _streamWriter = new StreamWriter();
    return _streamWriter;
}

function WriteLineJS(text) {
    var pl = new SOAPClientParameters();
    pl.add("text", text);
    return SOAPClient.invoke(helpersServiceUri, "WriteLine", pl, helpersNamespace, helpersEndpointName, helpersInterface, false, null);
}

function CloseStreamWrite() {
    var pl = new SOAPClientParameters();
    return SOAPClient.invoke(helpersServiceUri, "CloseStreamWrite", pl, helpersNamespace, helpersEndpointName, helpersInterface, false, null);
}

//OpenFileDialog and SaveFileDialog
var OpenFileDlgJS = new IOpenFileDialog();
function OpenFileDialogJS() {
    var pl = new SOAPClientParameters();
    SOAPClient.invoke(helpersServiceUri, "OpenFileDialog", pl, helpersNamespace, helpersEndpointName, helpersInterface, false, null);
    return OpenFileDlgJS;
}

function OpenFileDialogShowDialogJS() {
    var pl = new SOAPClientParameters();
    return SOAPClient.invoke(helpersServiceUri, "OpenFileDialogShowDialog", pl, helpersNamespace, helpersEndpointName, helpersInterface, false, null);
}

function GetOpenFileDialogPropertyJS(prop) {
    var pl = new SOAPClientParameters();
    pl.add("prop", prop);
    return SOAPClient.invoke(helpersServiceUri, "GetOpenFileDialogProperty", pl, helpersNamespace, helpersEndpointName, helpersInterface, false, null);
}

function SetOpenFileDialogPropertyJS(prop, value) {
    var pl = new SOAPClientParameters();
    pl.add("prop", prop);
    pl.add("value", value);
    return SOAPClient.invoke(helpersServiceUri, "SetOpenFileDialogProperty", pl, helpersNamespace, helpersEndpointName, helpersInterface, false, null);
}

function StreamWriter() {
    this.WriteLine = StreamWriter_WriteLine;
    this.Close = StreamWriter_CloseFile;
}

function StreamWriter_CloseFile(text) {
    CloseStreamWrite(text);
}

function StreamWriter_WriteLine(text) {
    WriteLineJS(text);
}

function FileStream() {
    this.Close = FileStream_Close;
}

function FileStream_Close() {
    CloseFileStream();
}

function IOpenFileDialog() {
    this.instanceOf = "IOpenFileDialog";
    this.get_FileName = GetFileName;
    this.set_FileName = SetFileName;
    this.get_FileNames = GetFileNames;
    this.set_FileNames = SetFileNames;
    this.get_Filter = GetFilter;
    this.set_Filter = SetFilter;
    this.get_Title = GetTitle;
    this.set_Title = SetTitle;
    this.get_InitialDirectory = GetInitialDirectory;
    this.set_InitialDirectory = SetInitialDirectory;
    this.ShowDialog = ShowDialog;
    this.get_CheckFileExists = GetCheckFileExists;
    this.set_CheckFileExists = SetCheckFileExists;
    this.get_CheckPathExists = GetCheckPathExists;
    this.set_CheckPathExists = SetCheckPathExists;
}

function GetFileName() {
    return GetOpenFileDialogPropertyJS("FileName");
}

function SetFileName(value) {
    SetOpenFileDialogPropertyJS("FileName", value);
}

function GetFileNames() {
    return GetOpenFileDialogPropertyJS("FileNames");
}

function SetFileNames(value) {
    SetOpenFileDialogPropertyJS("FileNames", value);
}

function GetFilter() {
    return GetOpenFileDialogPropertyJS("Filter");
}

function SetFilter(value) {
    SetOpenFileDialogPropertyJS("Filter", value);
}

function GetTitle() {
    return GetOpenFileDialogPropertyJS("Title");
}

function SetTitle(value) {
    SetOpenFileDialogPropertyJS("Title", value);
}

function GetInitialDirectory() {
    return GetOpenFileDialogPropertyJS("InitialDirectory");
}

function SetInitialDirectory(value) {
    SetOpenFileDialogPropertyJS("InitialDirectory", value);
}

function ShowDialog() {
    if (this.instanceOf == "IOpenFileDialog")
        return OpenFileDialogShowDialogJS();
    else
        return FolderBrowserDialogShowDialogJS();
}

function GetCheckFileExists() {
    return GetOpenFileDialogPropertyJS("CheckFileExists");
}

function SetCheckFileExists(value) {
    SetOpenFileDialogPropertyJS("CheckFileExists", value);
}

function GetCheckPathExists() {
    return GetOpenFileDialogPropertyJS("CheckPathExists");
}

function SetCheckPathExists(value) {
    SetOpenFileDialogPropertyJS("CheckPathExists", value);
}

//FolderBrowserDialog
var FolderBrowserDlgJS = new IFolderBrowserDialog();
function FolderBrowserDialogJS() {
    var pl = new SOAPClientParameters();
    SOAPClient.invoke(helpersServiceUri, "FolderBrowserDialog", pl, helpersNamespace, helpersEndpointName, helpersInterface, false, null);
    return FolderBrowserDlgJS;
}

function FolderBrowserDialogShowDialogJS() {
    var pl = new SOAPClientParameters();
    return SOAPClient.invoke(helpersServiceUri, "FolderBrowserDialogShowDialog", pl, helpersNamespace, helpersEndpointName, helpersInterface, false, null);
}

function GetFolderBrowserDialogPropertyJS(prop) {
    var pl = new SOAPClientParameters();
    pl.add("prop", prop);
    return SOAPClient.invoke(helpersServiceUri, "GetFolderBrowserDialogProperty", pl, helpersNamespace, helpersEndpointName, helpersInterface, false, null);
}

function SetFolderBrowserDialogPropertyJS(prop, value) {
    var pl = new SOAPClientParameters();
    pl.add("prop", prop);
    pl.add("value", value);
    return SOAPClient.invoke(helpersServiceUri, "SetFolderBrowserDialogProperty", pl, helpersNamespace, helpersEndpointName, helpersInterface, false, null);
}

function IFolderBrowserDialog() {
    this.instanceOf = "IFolderBrowserDialog";
    this.get_RootFolder = GetRootFolder;
    this.set_RootFolder = SetRootFolder;
    this.get_Description = GetDescription;
    this.set_Description = SetDescription;
    this.ShowDialog = ShowDialog;
    this.get_SelectedPath = GetSelectedPath;
    this.set_SelectedPath = SetSelectedPath;
}

function GetRootFolder() {
    return GetFolderBrowserDialogPropertyJS("RootFolder");
}

function SetRootFolder(value) {
    SetFolderBrowserDialogPropertyJS("RootFolder", value);
}

function GetDescription() {
    return GetFolderBrowserDialogPropertyJS("Description");
}

function SetDescription(value) {
    SetFolderBrowserDialogPropertyJS("Description", value);
}

function GetSelectedPath() {
    return GetFolderBrowserDialogPropertyJS("SelectedPath");
}

function SetSelectedPath(value) {
    SetFolderBrowserDialogPropertyJS("SelectedPath", value);
}

function GetTagHistory(tagName, startDate, endDate, interval) {
    var pl = new SOAPClientParameters();
    pl.add("tagName", tagName);
    pl.add("startDate", startDate);
    pl.add("endDate", endDate);
    pl.add("interval", interval);
    return SOAPClient.invoke(tagServiceUri, "GetTagHistory", pl, tagNamespace, tagEndpointName, tagInterface, false, null);
}

function isFloat(n) {
    return Number(n) === n && n % 1 !== 0;
}

function InitSecurity() {
    _checkProject();
    _webSecurity();
    setInterval(_webSecurity, 1000);
    setInterval(_systemTagsUpdate, 1000);
}

function InitSystemTags() {
    setInterval(_systemTagsUpdate, 1000);
}

function _systemTagsUpdate() {
    GetSystemTagsValues();
}

function _logPageOpen() {
    if (!sentLog) {
        sentLog = true;
        var pl = new SOAPClientParameters();
        pl.add("title", document.title);
        SOAPClient.invoke(helpersServiceUri, "logPageOpen", pl, helpersNamespace, helpersEndpointName, helpersInterface, false, null);
    }
}

function _webSecurity() {
    var pl = new SOAPClientParameters();
    pl.add("project", this.projectId);
    pl.add("lastModified", new Date(document.lastModified));
    pl.add("graphicName", this.docName);
    pl.add("pageGuid", pageGuid);
    var info = SOAPClient.invoke(helpersServiceUri, "GetSecurityInfo", pl, helpersNamespace, helpersEndpointName, helpersInterface, false, null);
    if (info == null) {
        BlockScreen(connectionErrorMessage);
        return;
    }

    var activatedTime = info[0];
    var canOpenGraphic = info[1];
    var checkProject = info[2];
    var currentUser = info[3];
    var disabledObjects = info[4];

    var hasError = !checkProject;
    _updatePermission(hasError, 'The runtime executing is not from this project or this page has been modified manually');
    if (!hasError) {
        if (this.docName !== '')
            _updatePermission(!canOpenGraphic, 'The current user doesn&apos;t have permission to access this page.');

        _updateDisabledObjects(disabledObjects);
        _updateCookies(currentUser, activatedTime, disabledObjects);
    }
}

function _checkProject() {
    var hasError = !CheckProject(this.projectId);
    _updatePermission(hasError, 'The runtime executing is not from this project');// or this page has been modified manually');
    return hasError;
}

function _updatePermission(hasError, message) {
    var mainDiv = document.getElementById('Main_Window');
    var divPrmissions = document.getElementById('div_without_permissions');
    var lblPermission = document.getElementById('lblPermission');
    var divBlockScreen = document.getElementById('divBlockScreen');
    if (mainDiv !== null && divPrmissions !== null) {
        if (divBlockScreen == null && hasError) {
            mainDiv.style.display = 'none';
            divPrmissions.style.display = '';
            lblPermission.innerHTML = message;
            if (!lblPermission.classList.contains('blockMessage'))
                lblPermission.classList.add('blockMessage');
        } else {
            mainDiv.style.display = '';
            divPrmissions.style.display = 'none';
            if (lblPermission.classList.contains('blockMessage'))
                lblPermission.classList.remove('blockMessage');
        }
    }
}

function _updateCurrentUser() {
    var pl = new SOAPClientParameters();
    pl.add("pageGuid", pageGuid);
    currentUser = SOAPClient.invoke(helpersServiceUri, "getCurrentUser", pl, helpersNamespace, helpersEndpointName, helpersInterface, false, null);
    var cookies = loadCookies();
    if (cookies['currentUser'] != null && cookies['currentUser'] !== '' && cookies['currentUser'] !== currentUser) {
        _setCurrentUser(cookies['currentUser']);
        updateDisabledObjects();
    }
}

function _updateActivatedTime() {
    var pl = new SOAPClientParameters();
    pl.add("pageGuid", pageGuid);
    activatedTime = SOAPClient.invoke(helpersServiceUri, "getActivatedTime", pl, helpersNamespace, helpersEndpointName, helpersInterface, false, null);
    var cookies = loadCookies();
    if (cookies['activatedTime'] != null && cookies['activatedTime'] !== '' && cookies['activatedTime'] !== activatedTime) {
        _setActivatedTime(cookies['activatedTime']);
        updateDisabledObjects();
    }
}

function _updateCookies(currentUser, activatedTime, disabledObjects) {
    var cookies = loadCookies();
    if (cookies['currentUser'] != null && cookies['currentUser'] !== '' && cookies['currentUser'] !== currentUser) {
        _setCurrentUser(cookies['currentUser']);
        _updateDisabledObjects(disabledObjects);
    }
    if (cookies['activatedTime'] != null && cookies['activatedTime'] !== '' && cookies['activatedTime'] !== activatedTime) {
        _setActivatedTime(cookies['activatedTime']);
        _updateDisabledObjects(disabledObjects);
    }
}

function _setCurrentUser(newUser) {
    var pl = new SOAPClientParameters();
    if (pageGuid == "") {
        pageGuid = guid();
    }
    pl.add("newUser", newUser);
    pl.add("pageGuid", pageGuid);
    SOAPClient.invoke(helpersServiceUri, "setCurrentUser", pl, helpersNamespace, helpersEndpointName, helpersInterface, false, null);
}

function _setActivatedTime(newTime) {
    var pl = new SOAPClientParameters();
    if (pageGuid == "") {
        pageGuid = guid();
    }
    pl.add("newTime", newTime);
    pl.add("pageGuid", pageGuid);
    SOAPClient.invoke(helpersServiceUri, "setActivatedTime", pl, helpersNamespace, helpersEndpointName, helpersInterface, false, null);
}

function canOpenGraphic(graphicName) {
    var pl = new SOAPClientParameters();
    pl.add("graphicName", graphicName);
    pl.add("pageGuid", pageGuid);
    return SOAPClient.invoke(helpersServiceUri, "CanOpenGraphic", pl, helpersNamespace, helpersEndpointName, helpersInterface, false, null);
}

function updateDisabledObjects() {
    var pl = new SOAPClientParameters();
    pl.add("graphicName", this.docName);
    pl.add("pageGuid", pageGuid);
    var objectsIsEnabled = SOAPClient.invoke(helpersServiceUri, "ObjectsIsEnabled", pl, helpersNamespace, helpersEndpointName, helpersInterface, false, null);
    _updateDisabledObjects(objectsIsEnabled);
}

function _updateDisabledObjects(objectsIsEnabled) {
    var dicObjectsIsEnabled = Object.fromEntries(objectsIsEnabled)
    var disabledObjects = dicObjectsIsEnabled["DisabledObjects"]
    var enabledObjects = dicObjectsIsEnabled["EnabledObjects"]

    if (disabledObjects !== null && disabledObjects.constructor.name == "Object")
        disabledObjects = Object.values(disabledObjects)

    if (enabledObjects !== null && enabledObjects.constructor.name == "Object")
        enabledObjects = Object.values(enabledObjects)

    for (var i = 0; i < Window.listObjects.length; i++) {
        var object = Window.listObjects[i];
        if (disabledObjects !== null) {
            if (disabledObjects.includes(object.id))
                object.set_IsEnabled(false)
        }

        if (enabledObjects !== null) {
            if (enabledObjects.includes(object.id))
                object.set_IsEnabled(true)
        }
    }
}

function canCloseGraphic(graphicName) {
    var pl = new SOAPClientParameters();
    pl.add("graphicName", graphicName);
    pl.add("pageGuid", pageGuid);
    return SOAPClient.invoke(helpersServiceUri, "CanCloseGraphic", pl, helpersNamespace, helpersEndpointName, helpersInterface, false, null);
}

function tryGenerateSymbol(symbolName, symbolFullName, fillcolor, background, mode, flip) {
    var pl = new SOAPClientParameters();
    pl.add("symbolName", symbolName);
    pl.add("symbolFullName", symbolFullName);
    pl.add("fillcolor", fillcolor);
    pl.add("background", background);
    pl.add("mode", mode);
    pl.add("flip", flip);
    return SOAPClient.invoke(helpersServiceUri, "tryGenerateSymbol", pl, helpersNamespace, helpersEndpointName, helpersInterface, false, null);
}

function GetDynamicFillGradient(tagName, orientation, flip) {
    var pl = new SOAPClientParameters();
    pl.add("tagName", tagName);
    pl.add("orientation", orientation);
    pl.add("flip", flip);
    return SOAPClient.invoke(helpersServiceUri, "GetDynamicFillGradient", pl, helpersNamespace, helpersEndpointName, helpersInterface, false, null);
}

function CheckProject(project) {
    var pl = new SOAPClientParameters();
    pl.add("project", project);
    pl.add("lastModified", new Date(document.lastModified));
    return SOAPClient.invoke(helpersServiceUri, "CheckProject", pl, helpersNamespace, helpersEndpointName, helpersInterface, false, null);
}

function ChangeBorderThickness(left, top, right, bottom) {
    var ret = top + "px " + right + "px " + bottom + "px " + left + "px";
    return ret;
}

function PersonalizedToString(obj, format) {
    //Default Formatters
    if (obj == undefined)
        return "";
    if (format == "" || format == undefined)
        return obj.toString();

    var indexOpenQuote = format.indexOf("'");
    var indexCloseQuote = indexOpenQuote >= 0 ? format.indexOf("'", indexOpenQuote + 1) : -1;
    if (foundIndex(format, "C", indexOpenQuote, indexCloseQuote) || foundIndex(format, "c", indexOpenQuote, indexCloseQuote))
        return ReformatCurrency(obj, format);
    if (foundIndex(format, "D", indexOpenQuote, indexCloseQuote) || foundIndex(format, "d", indexOpenQuote, indexCloseQuote))
        return ReformatDecimals(obj, format);
    if (foundIndex(format, "E", indexOpenQuote, indexCloseQuote) || foundIndex(format, "e", indexOpenQuote, indexCloseQuote))
        return ReformatExponential(obj, format);
    if (foundIndex(format, "F", indexOpenQuote, indexCloseQuote) || foundIndex(format, "f", indexOpenQuote, indexCloseQuote))
        return ReformatFixedPoint(obj, format);
    if (foundIndex(format, "N", indexOpenQuote, indexCloseQuote) || foundIndex(format, "n", indexOpenQuote, indexCloseQuote))
        return ReformatWithGroupSeparator(obj, format);
    if (foundIndex(format, "P", indexOpenQuote, indexCloseQuote) || foundIndex(format, "p", indexOpenQuote, indexCloseQuote))
        return ReformatPercentage(obj, format);
    if (foundIndex(format, "X", indexOpenQuote, indexCloseQuote) || foundIndex(format, "x", indexOpenQuote, indexCloseQuote))
        return ReformatHexa(obj, format);

    //Personalized Formatters
    if (format.indexOf('0') >= 0 || format.indexOf('#') >= 0)
        return ReformatZeroPlaceholder(obj, format);
}

function foundIndex(format, c, indexOpenQuote, indexCloseQuote) {
    var index = format.indexOf(c);
    if (indexOpenQuote >= 0 && indexCloseQuote >= 0) {
        while (index >= 0) {
            if (index > indexOpenQuote && index < indexCloseQuote)
                index = format.indexOf(c, index + 1);
            else
                return true;
        }
    }

    return index >= 0;
}

function ReformatCurrency(obj, format) {
    if (format.length > 1) {
        var auxFormat = format;

        if (auxFormat.indexOf('c') >= 0)
            auxFormat = auxFormat.substr(auxFormat.indexOf('c') + 1, auxFormat.length - 1);
        else
            auxFormat = auxFormat.substr(auxFormat.indexOf('C') + 1, auxFormat.length - 1);

        if (auxFormat == "1")
            auxFormat = "2";
        var iAuxFormat = Number.parseInt(auxFormat);
        if (iAuxFormat >= 2 && iAuxFormat <= 20)
            return obj.toLocaleString("en-US", { style: "currency", currency: "USD", maximumFractionDigits: auxFormat });
        //else
        //    alert("The number of fraction digits should be between 2 and 20.");
    }
    else
        return obj.toLocaleString("en-US", { style: "currency", currency: "USD" });
}

function ReformatDecimals(obj, format) {
    if (format.length > 1) {
        var auxFormat = format;

        if (auxFormat.indexOf('d') >= 0)
            auxFormat = auxFormat.substr(auxFormat.indexOf('d') + 1, auxFormat.length - 1);
        else
            auxFormat = auxFormat.substr(auxFormat.indexOf('D') + 1, auxFormat.length - 1);

        if (auxFormat == "1")
            auxFormat = "2";
        var iAuxFormat = Number.parseInt(auxFormat);
        if (iAuxFormat >= 2 && iAuxFormat <= 20)
            if (iAuxFormat >= 1 && iAuxFormat <= 21)
                return obj.toLocaleString("en-US", { style: "decimal", minimumIntegerDigits: auxFormat, maximumFractionDigits: 0 });
            else {
                //   alert("The number of integer digits should be between 1 and 21.");
                return obj.toLocaleString("en-US", { style: "decimal", maximumFractionDigits: 0 });
            }

    }
    else
        return obj.toLocaleString("en-US", { style: "decimal", maximumFractionDigits: 0 });
}

function ReformatExponential(obj, format) {
    if (format.length > 1) {
        var auxFormat = format;

        if (auxFormat.indexOf('e') >= 0)
            auxFormat = auxFormat.substr(auxFormat.indexOf('e') + 1, auxFormat.length - 1);
        else
            auxFormat = auxFormat.substr(auxFormat.indexOf('E') + 1, auxFormat.length - 1);

        return obj.toExponential(auxFormat);
    }
    else
        return obj.toExponential();
}

function ReformatFixedPoint(obj, format) {
    if (format.length > 1) {
        var auxFormat = format;

        if (auxFormat.indexOf('f') >= 0)
            auxFormat = auxFormat.substr(auxFormat.indexOf('f') + 1, auxFormat.length - 1);
        else
            auxFormat = auxFormat.substr(auxFormat.indexOf('F') + 1, auxFormat.length - 1);

        return obj.toFixed(auxFormat);
    }
    else
        return obj.toFixed();
}

function ReformatWithGroupSeparator(obj, format) {
    if (format.length > 1) {
        var auxFormat = format;

        if (auxFormat.indexOf('n') >= 0)
            auxFormat = auxFormat.substr(auxFormat.indexOf('n') + 1, auxFormat.length - 1);
        else
            auxFormat = auxFormat.substr(auxFormat.indexOf('N') + 1, auxFormat.length - 1);

        return obj.toLocaleString("en-US", { useGrouping: "true", maximumFractionDigits: auxFormat });
    }
    else
        return obj.toLocaleString("en-US", { useGrouping: "true" });
}

function ReformatPercentage(obj, format) {
    if (format.length > 1) {
        var auxFormat = format;

        if (auxFormat.indexOf('p') >= 0)
            auxFormat = auxFormat.substr(auxFormat.indexOf('p') + 1, auxFormat.length - 1);
        else
            auxFormat = auxFormat.substr(auxFormat.indexOf('P') + 1, auxFormat.length - 1);

        return obj.toLocaleString("en-US", { style: "percent", maximumFractionDigits: auxFormat });
    }
    else
        return obj.toLocaleString("en-US", { style: "percent", maximumFractionDigits: 2 });
}

function ReformatHexa(obj, format) {
    var hexa = obj.toString(16);

    if (format.length > 1) {
        var auxFormat = format;

        if (auxFormat.indexOf('x') >= 0)
            auxFormat = auxFormat.substr(auxFormat.indexOf('x') + 1, auxFormat.length - 1);
        else
            auxFormat = auxFormat.substr(auxFormat.indexOf('X') + 1, auxFormat.length - 1);

        while (hexa.length < (auxFormat || 2)) { hexa = "0" + hexa; }
    }

    return hexa;
}

function ReformatZeroPlaceholder(obj, format) {
    var numberZeroAfterPeriod = 0;
    var numberHashAfterPeriod = 0;
    var numberZero = 0;
    var numberHash = 0;
    var foundPeriod;
    var startAppend = false;
    var appendValue = "";
    for (var i = 0; i < format.length; i++) {
        var c = format[i];
        if (c == '0') {
            if (foundPeriod)
                numberZeroAfterPeriod++;
            else
                numberZero++;
        }
        else if (c == '#') {
            if (foundPeriod)
                numberHashAfterPeriod++;
            else
                numberHash++;
        }
        else if (c == '.' || c == ',')
            foundPeriod = true;
        else if (c == "'")
            startAppend = !startAppend;
        else if (startAppend)
            appendValue += c;
    }

    var max = numberZeroAfterPeriod > numberHashAfterPeriod ? numberZeroAfterPeriod : numberHashAfterPeriod;
    var v = obj.toFixed(max);
    var numOfDigits = v.indexOf(".") >= 0 ? v.indexOf(".") : v.indexOf(",") >= 0 ? v.indexOf(",") : v.length;
    while (numberZero > numOfDigits) {
        v = "0" + v;
        numOfDigits++;
    }

    if (numberHashAfterPeriod > numberZeroAfterPeriod) {
        while (v.length > 0 && v[v.length - 1] == "0") {
            v = v.substring(0, v.length - 1);
        }
    }
    if (v.endsWith(".") || v.endsWith(","))
        v = v.substring(0, v.length - 1);
    return appendValue === "" ? v : v + appendValue;
}

function ThreatVarToJSDate(obj) {
    if (typeof (obj) == "object") {
        return obj.getMonth() + "/" + obj.getDate() + "/" + obj.getFullYear() + " " + obj.getHours() + ":" + obj.getMinutes() + ":" + obj.getSeconds();
    }
    else if (typeof (obj) == "string") {
        var isPM = false;
        if (obj.indexOf("PM") >= 0) {
            obj = obj.replace(" PM", "");
            isPM = true;
        }
        else if (obj.indexOf("AM") >= 0)
            obj = obj.replace(" AM", "");

        GetDateTimeFormat();
        var format = projectDateTimeFormat.toString();
        var day = format.indexOf("dd");
        var month = format.indexOf("MM");
        var year = format.indexOf("yyyy");
        var hour = format.indexOf("hh");
        var min = format.indexOf("mm");
        var sec = format.indexOf("ss");

        if (year < 0) {
            year = format.indexOf("yy");
            if (year < 0)
                return obj;
            else year = parseInt(obj.substring(year, year + 2));
        }
        else year = parseInt(obj.substring(year, year + 4));

        if (hour < 0) {
            hour = format.indexOf("HH");
            if (hour < 0)
                return obj;
        }

        if (day < 0 || month < 0 || year < 0 || hour < 0 || min < 0 || sec < 0 || Number.isNaN(day) || Number.isNaN(month) ||
            Number.isNaN(year) || Number.isNaN(hour) || Number.isNaN(min) || Number.isNaN(sec))
            return obj;

        day = parseInt(obj.substring(day, day + 2));
        month = parseInt(obj.substring(month, month + 2));
        hour = isPM ? parseInt(obj.substring(hour, hour + 2)) + 12 : parseInt(obj.substring(hour, hour + 2));
        min = parseInt(obj.substring(min, min + 2));
        sec = parseInt(obj.substring(sec, sec + 2));

        return month + "/" + day + "/" + year + " " + hour + ":" + min + ":" + sec;
    }
    else return obj;
}

function ReformatDate(date, format) {
    if (date == "" || date == undefined)
        return "";

    if (format === "o" || format === "O")
        alert("Default date formatter 'o' or 'O' is not a valid format for web. It will be formatted with project's default date configuration.")

    var isPM = false;
    var is24h = false;
    if (typeof (date) == "string") {
        if (date.indexOf(" PM") >= 0)
            isPM = true;
        date = date.replace(" PM", "");
        date = date.replace(" AM", "");
    } else if (date.getHours() >= 12)
        isPM = true;

    var bkpDate = date;
    date = new Date(date);
    if (isNaN(date) || isNaN(date.getDate())) {
        var dateString = bkpDate.split(" ");
        if (dateString.length == 2) {
            var dateOnly = dateString[0].split('/');
            var timeOnly = dateString[1].split(':');
            if (dateOnly.length == 3 && timeOnly.length == 3)
                date = new Date(Date.parse(dateOnly[2] + '-' + dateOnly[1] + '-' + dateOnly[0] + 'T' + timeOnly[0] + ':' + timeOnly[1] + ':' + timeOnly[2]));
        }
        if (isNaN(date) || isNaN(date.getDate()))
            return bkpDate;
    }

    GetDateTimeFormat();
    if (!projectDateTimeFormat.includes("tt"))
        is24h = true;

    if (!is24h && isPM && date.getHours() > 12)
        date.setHours(date.getHours() - 12);
    else if (date.getHours() == 0)
        date.setHours(12);

    var rascDate = "";
    if (format == "" || format == undefined)
        rascDate = projectDateTimeFormat.toString();
    else
        rascDate = format;

    var defaultFormats = GetDataDefaultFormats();

    if (defaultFormats.get(format) !== undefined)
        rascDate = defaultFormats.get(format);

    date.toLocaleDateString("en-US", { weekday: "long" });

    rascDate = rascDate.replace("MMMM", date.getMonthName());
    rascDate = rascDate.replace("MMM", date.getMonthAbbreviation());
    rascDate = rascDate.replace("dddd", date.getDayName());
    rascDate = rascDate.replace("ddd", date.getDayAbbreviation());
    rascDate = rascDate.replace("dd", date.getDate().toString().padStart(2, '0'));
    rascDate = rascDate.replace("DD", date.getDate().toString().padStart(2, '0'));
    rascDate = rascDate.replace("MM", ((date.getMonth() + 1).toString()).padStart(2, '0'));
    if (projectDateTimeFormat.includes("yyyy") || projectDateTimeFormat.includes("YYYY")) {
        rascDate = rascDate.replace("yyyy", date.getFullYear().toString());
        rascDate = rascDate.replace("YYYY", date.getFullYear().toString());
    }
    else {
        rascDate = rascDate.replace("yy", date.getFullYear().toString().slice(-2));
        rascDate = rascDate.replace("YY", date.getFullYear().toString().slice(-2));
    }

    rascDate = rascDate.replace("hh", (date.getHours().toString()).padStart(2, '0'));
    rascDate = rascDate.replace("HH", (date.getHours().toString()).padStart(2, '0'));
    rascDate = rascDate.replace("mm", (date.getMinutes().toString()).padStart(2, '0'));
    rascDate = rascDate.replace("ss", (date.getSeconds().toString()).padStart(2, '0'));
    rascDate = rascDate.replace("SS", (date.getSeconds().toString()).padStart(2, '0'));

    if (!isPM)
        rascDate = rascDate.replace("tt", "AM");
    else
        rascDate = rascDate.replace("tt", "PM");

    return rascDate;
}

function GetDataDefaultFormats() {
    var projectDateFormat = projectDateTimeFormat.split(" ")[0];
    var kvArray = [
        ["d", projectDateFormat],
        ["D", "dddd, MMMM dd, yyyy"],
        ["f", "dddd, MMMM dd, yyyy hh:mm tt"],
        ["F", "dddd, MMMM dd, yyyy hh:mm:ss tt"],
        ["g", projectDateFormat.concat(" hh:mm tt").replace("-", DateSeparator)],
        ["G", projectDateFormat.concat(" hh:mm:ss tt").replace("-", DateSeparator)],
        ["m", "MMMM dd"],
        ["M", "MMMM dd"],
        ["o", projectDateFormat],
        ["O", projectDateFormat],
        ["r", "ddd, dd MMM yyyy hh:mm:ss GMT"],
        ["R", "ddd, dd MMM yyyy hh:mm:ss GMT"],
        ["s", "yyyy-MM-ddTHH:mm:ss".replace("-", DateSeparator)],
        ["t", "hh:mm tt"],
        ["T", "hh:mm:ss tt"],
        ["u", "yyyy-MM-dd hh:mm:ssZ".replace("-", DateSeparator)],
        ["U", "dddd, MMMM dd, yyyy hh:mm:ss tt"],
        ["Y", "MMMM, yyyy"]
    ];
    return new Map(kvArray);
}

//Elements position change treatment
function calculateAngleNewPosition(Element, toChange, value) {
    //Convert angle from degrees to radians
    if (toChange == "Angle" && Element.get_Angle() !== value)
        Element.set_Angle(value);
    var angle = Element.get_Angle();
    angle = angle * Math.PI / 180;
    //initial element position.
    var old_posLeft = Element.get_Left();
    var old_posTop = Element.get_Top();
    var init_w = Element.get_Width();
    var init_h = Element.get_Height();
    //Get position after rotation with original size
    var x = -init_w / 2;
    var y = init_h / 2;
    var new_x = y * Math.sin(angle) + x * Math.cos(angle);
    var new_y = y * Math.cos(angle) - x * Math.sin(angle);
    var diff1_Left = new_x - x;
    var diff1_Top = new_y - y;
    //Get position after rotation with new size
    if (toChange == "Height" && Element.get_Height() !== value)
        Element.set_Height(value);
    else if (toChange == "Width" && Element.get_Width() !== value)
        Element.set_Width(value);
    var new_width = Element.get_Width();
    var new_height = Element.get_Height();
    var x = -new_width / 2;
    var y = new_height / 2;
    var new_x = y * Math.sin(angle) + x * Math.cos(angle);
    var new_y = y * Math.cos(angle) - x * Math.sin(angle);
    var diff2_Left = new_x - x;
    var diff2_Top = new_y - y;
    //Get the difference between the two positions
    var offset_Left = diff2_Left - diff1_Left;
    var offset_Top = diff2_Top - diff1_Top;
    //Calculate the correction
    var new_posLeft = old_posLeft - offset_Left;
    var new_posTop = old_posTop + offset_Top;
    //Apply
	if(Element.get_Top() !== new_posTop)
		Element.set_Top(new_posTop);
	if(Element.get_Left() !== new_posLeft)
		Element.set_Left(new_posLeft);
    return Element;
}

function svRoundDoubleJs(value, decimals) {
    return Number(Math.round(value + 'e' + decimals) + 'e-' + decimals);
}

function ParseTagFloat(value, decimals) {
    if (Number.isNaN(value) || type(value) === "String" || type(value) === "Boolean")
        return value;
	if (decimals != undefined)
		return value.toFixed(decimals);
	return value;
}

function roundValue(num, decimals){
	if(decimals != undefined && decimals > 0)
		return Math.round(num * Math.pow(10, decimals)) / (Math.pow(10, decimals));
	else
		return Math.round(num);
}