function ViewModel(perspectiva, data, usarGoogleGeoCode, chaveMapa) {
    const mSelf = this;
    mSelf.currentMap = new InlogMaps.Map;

    mSelf.iniciarMapa = (map, elementId, callback, gestureHandling) => {
        let mapType = InlogMaps.MapType.Leaflet;
        let params = {};

        if (usarGoogleGeoCode) {
            params = {
                libraries: ['places', 'drawing', 'geometry'],
                apiKey: chaveMapa,
                gestureHandling: gestureHandling,
                options: {
                    streetViewControl: true
                }
            };

            mapType = InlogMaps.MapType.Google;
        } else {
            params = {
                scriptsDependencies: [
                    `${urlGlobal}Scripts${urlAreaGlobal}/Path.Drag.js`,
                    `${urlGlobal}Scripts${urlAreaGlobal}/Leaflet.Editable.js`,
                    `${urlGlobal}Scripts${urlAreaGlobal}/leaflet.polylineDecorator.js`,
                    `${urlGlobal}Scripts${urlAreaGlobal}/leaflet.markercluster-src.js`
                ],
                cssDependencies: [`${urlGlobal}Content${urlAreaGlobal}/MarkerCluster.Default.css`],
                gestureHandling: gestureHandling
            };
        }

        mSelf.MapType = mapType;
        map.initialize(mapType, params, elementId)
            .then(() => callback());
    }

    if (perspectiva) {
        mSelf.Perspectiva = new window[perspectiva](mSelf, data);
    } else {
        mSelf.Perspectiva = new SinoticoVeiculosPerspectiva(mSelf);
    }

    mSelf.usarGeoGoogle = usarGoogleGeoCode;
    mSelf.Modulos = ko.observableArray([]);
    mSelf.Ferramentas = ko.observableArray([]);
    mSelf.ModulosLaterais = ko.observableArray([]);
    mSelf.Conteudos = ko.observableArray([]);
    mSelf.ConteudosRodape = ko.observableArray([]);
    mSelf.Breadcumbs = ko.observableArray([]);
    mSelf.MensagensAlerta = ko.observableArray([]);
    mSelf.MensagemTempoReal = ko.observable(false);
    mSelf.Titulo = ko.observable("");
    mSelf.UsuarioLogado = ko.observable();
    mSelf.EmpresaUsuarioLogado = ko.observable();
    mSelf.FuncionalidadesCarregadas = ko.observableArray([]);

    mSelf.Init = () => setTimeout(mSelf.Perspectiva.Init(), 500);
    mSelf.LoadingOff = () => $(".loading-inlog").fadeOut(500);
    mSelf.LoadingIn = () => $(".loading-inlog").fadeOut(500);

    vmEventos = new ViewModelEventos();

    vmEventos.TempoReal = new PConVeiculos({
        mapeamento: urlGlobal + 'TunelVeiculosGenerico',
        onConnected: () => { console.log('Connectado SignalR'); }
    });

    mSelf.iniciarMapa(mSelf.currentMap, 'mapa', mSelf.Init);

    mSelf.FuncionalidadesCarregadasAtualizar = (nome) => {
        let todosOk = 0;
        ko.utils.arrayForEach(mSelf.FuncionalidadesCarregadas(), (func) => {
            if (func.Name == nome) {
                func.Carregado = true;
            }

            if (func.Carregado) {
                todosOk++;
            }
        });

        if (mSelf.FuncionalidadesCarregadas().length == todosOk) {
            $(".loading-inlog").fadeOut(500);
            mSelf.FuncionalidadesCarregadas.removeAll();

            //Verificar se a respectiva Perspectiva possui o método que está sendo chamado
            if (mSelf.Perspectiva && mSelf.Perspectiva.AjustesLayoutPerspectiva) {
                mSelf.Perspectiva.AjustesLayoutPerspectiva();
            }
        }
    };

    mSelf.CarregarFuncionalidades = (funcionalidades, onSuccess) => {
        ko.utils.arrayForEach(funcionalidades(), (func) => {
            mSelf.FuncionalidadesCarregadas.push({ Name: func.url, Carregado: false });
            if (func.url) {
                $.ajax({
                    url: func.url,
                    contentType: 'application/json; charset=utf-8',
                    dataType: "json",
                    data: func.data,
                    type: "GET",
                    success: onSuccess,
                    complete: () => mSelf.FuncionalidadesCarregadasAtualizar(func.url)
                });
            } else {
                $.ajax({
                    url: func.url,
                    contentType: 'application/json; charset=utf-8',
                    dataType: "json",
                    type: "GET",
                    success: onSuccess,
                    complete: () => mSelf.FuncionalidadesCarregadasAtualizar(func.url)
                });
            }
        });
    };

    mSelf.GetModulo = (identificador) => {
        return ko.utils.arrayFirst(mSelf.Modulos(), (modulo) => {
            if (modulo.ModuloId() == identificador) {
                return modulo;
            }
        });
    };

    mSelf.GetConteudo = (identificador) => {
        return ko.utils.arrayFirst(mSelf.Conteudos(), (conteudo) => {
            if (conteudo.ConteudoId() == identificador) {
                return conteudo;
            }
        });
    };

    mSelf.GetModuloMenuLateral = (identificador) => {
        return ko.utils.arrayFirst(mSelf.ModulosLaterais(), (modulo) => {
            if (modulo.ModuloId() == identificador) {
                return modulo;
            }
        });
    };

    mSelf.GetFerramenta = (identificador) => {
        return ko.utils.arrayFirst(mSelf.Ferramentas(), (ferramenta) => {
            if (ferramenta.FerramentaId() == identificador) {
                return ferramenta;
            }
        });
    };

    mSelf.TrocarPerspectiva = (novaPerspectiva) => {
        $(".loading-inlog").fadeIn(500, () => {
            if (novaPerspectiva != null) {
                mSelf.Perspectiva.Destroy();
                delete mSelf.Perspectiva;
                mSelf.Perspectiva = novaPerspectiva;
                mSelf.Perspectiva.Init();
            }
        });
    };

    mSelf.FecharMensagemAlerta = (index, data, evt) => mSelf.MensagensAlerta.splice(index, 1);

    mSelf.FecharTodosMensagemAlerta = () => mSelf.MensagensAlerta.removeAll();

    mSelf.ShowMensagemSucesso = (mensagem) => {
        mSelf.FecharTodosMensagemAlerta();
        mSelf.MensagensAlerta.push({ Mensagem: mensagem, Titulo: 1 });
    };

    mSelf.ShowMensagemInformacao = (mensagem) => {
        mSelf.FecharTodosMensagemAlerta();
        mSelf.MensagensAlerta.push({ Mensagem: mensagem, Titulo: 2 });
    };

    mSelf.ShowMensagemAlerta = (mensagem) => {
        mSelf.FecharTodosMensagemAlerta();
        mSelf.MensagensAlerta.push({ Mensagem: mensagem, Titulo: 3 });
    };

    mSelf.ShowMensagemErro = (mensagem) => {
        mSelf.FecharTodosMensagemAlerta();
        mSelf.MensagensAlerta.push({ Mensagem: mensagem, Titulo: 4 });
    };

    mSelf.OpenSobre = () => window.open(urlGlobal + "Sobre/", "_blank");

    mSelf.TextoDialog = ko.observable();

    mSelf.ConsultaUsuarioLogado = () => {
        $.ajax({
            url: urlGlobal + "Autenticador/Autenticador/ConsultaUsuarioLogado",
            contentType: 'application/json; charset=utf-8',
            dataType: "json",
            type: "GET",
            success: (ret) => {
                if (ret) {
                    const usuario = ko.mapping.fromJS(ret);

                    mSelf.UsuarioLogado(usuario.UsuarioNome());
                    mSelf.EmpresaUsuarioLogado(usuario.Empresa());
                }
            },
            complete: () => { }
        });
    }

    mSelf.getIconSize = (url) => new Promise((resolve, reject) => {
        const img = new Image();
        img.onload = function () {
            const size = [this.width, this.height];
            resolve(size);
        }
        img.onerror = () => reject;

        img.src = url;
    });
}

function abrirDialogModal(options, titulo, mensagem) {
    vm.TextoDialog(mensagem);

    options = options != null ? options : {
        resizable: false,
        height: 190,
        modal: true,
        buttons: {
            "Fechar": function () {
                $(this).dialog("close");
            }
        }
    };

    options.title = titulo;
    $("#dialog-confirm").dialog(options);
}