genAdjunto.js

/**
 * Módulo general para manerjar el control de adjuntar en los formularios normales y en las grids Fw7.
 * jQuery es una dependencia para este script
 * @module genAdjunto
 */

var _$ = jQuery.noConflict();

/**
 * Objeto global que almacena la definición de cada control adjuntar
 */
var _filesForm = [];
var loadIframeUploda = false;
var modalUpload = null;
var _gridFiles = {};
var _isResponse = false;
/*
 * gridFiles
 *  -> gridId
 *      -> idx Columna
 *          -> idx Row
 *              -> Arreglo Adjuntos
 */

/**
*Inicializa todos los controles de adjuntar en el formulario.
*Todos los controles se buscan con la clase .btnAdjuntar
*/
function initFiles() {
    _initUpload();

    _$('.btnAdjuntar').each(function () {
        var idFile = this.dataset.idf;
        var f = getFileFormConf(idFile);

        if (f == null) {
            var obj = JSON.parse(JSON.stringify(this.dataset));

            if (obj.fnRemove)
                obj.fnRemove = eval(obj.fnRemove);

            if (obj.grid)
                _gridFiles[obj.grid] = {};

            if (_gridFiles[obj.grid] && obj.gridCol) {
                _gridFiles[obj.grid][obj.gridCol] = {};
                _addFileEvtGrid(obj.grid);
            }

            _filesForm.push(obj);
            cleanFileBtnAttr(this);
        } else {
            f = JSON.parse(JSON.stringify(this.dataset));
            console.error('Ya existe un file con el id {0} configurado en el formulario'.format(idFile));
        }
    });

    _$('.btnAdjuntar').on('click', function () {
        var idFile = this.dataset.idf;
        var f = getFileFormConf(idFile);

        if (f != null)
            adjuntarFile(idFile);
    });
}

/**
*Limpia los data atributos para adjuntar de un botón disparador del control adjuntar.
*@param {HTMLButtonElement} btn botón que dispara el modal para adjuntar
*@example cleanFileBtnAttr(_$('#btnf1')[0]);
*/
function cleanFileBtnAttr(btn) {
    var attrs = ['data-tipo-f', 'data-num-files', 'data-kb'];
    for (var i = 0; i < attrs.length; i++) {
        btn.removeAttribute(attrs[i]);
    }
}

/**
*Obtiene la configuración de un file desde el objeto global mediante el id.
*@param {string} idFile id que se asigna en el control HTML ol
*@returns {object} objeto con la configuración del file
*@example getFileFormConf('f1');
*/
function getFileFormConf(idFile) {
    var f = _filesForm.find(x => x.idf == idFile);
    return f;
}

/**
*Carga la configuración y abre el modal para adjuntar, esta función se invoca automáticamente
*@param {string} idFile id que se asigna en el control HTML ol
*@param {string} type extensión con los archivos permitidos
*@param {string} kb tamaño máximo admitido en Kilobytes
*@param {string} numFiles Número máximo de archivos que se pueden adjuntar
*/
function adjuntarFile(idFile, type, kb, numFiles) {
    var uploadArgs = { 'type': '', 'kb': '4096', 'numFiles': '2', 'idFile': idFile };
    var f = _filesForm.find(x => x.idf == idFile);

    if (f != null) {
        uploadArgs.type = f.tipoF;
        uploadArgs.kb = f.kb;
        uploadArgs.numFiles = f.numFiles;
    }

    if (type != null && type != '')
        uploadArgs.type = type;

    if (kb != null && kb != '')
        uploadArgs.kb = kb;

    if (numFiles != null && numFiles != '')
        uploadArgs.numFiles = numFiles;

    uploadArgs.numFiles -= _$('#' + idFile).find('li').length;
    setUploadIframe(uploadArgs);
    modalUpload.idFile = idFile;
    viewUpload(true);
}

function _initUpload() {
    initModalUpload();
    addBloqOverlay();
}

/**
*Inicializa el contenido HTML para el modal
*/
function initModalUpload() {
    modalUpload = document.createElement("div");
    _$(modalUpload).addClass('uploadModal nb-hidden');

    modalUpload.close = function (rFiles) {
        try {
            addFileInForm(rFiles, this.idFile);
        } catch (E) {
            console.error(E);
        }

        viewUpload(false);
    }

    _$(document.body).append(modalUpload);
}

/**
*Carga en el control adjuntar los archivos subidos, se invoca luego de cerrar el modal
*Si el archivo ya existe, lo valida, dispara eventos cuando existe y cuando agrega los archivos
*@param {string} dataFile Objeto de la grid ya inicializada
*@param {string} idFile id que se asigna en el control HTML ol
*/
function addFileInForm(dataFile, idFile) {
    if (dataFile) {
        var files = dataFile.split('¬');

        for (var i = 0; i < files.length; i++) {
            var arrDataFiles = files[i].split("&");
            var fileName = arrDataFiles[0];
            var fileMD5 = arrDataFiles[1];

            if (!existFile(fileMD5)) {
                addNewLiFile(idFile, fileMD5, fileName, true);
            } else {
                var confFile = getFileFormConf(idFile);
                if (confFile != null && confFile.removeMsg != null) {
                    if (confFile.removeMsg == 'fw7' && app != null)
                        app.dialog.alert('El archivo {0} ya ha sido adjuntado'.format(fileName), '!Opps');
                } else {
                    var evtExistFile = new CustomEvent('existFile', { 'detail': arrDataFiles });
                    _$('#' + idFile)[0].dispatchEvent(evtExistFile);
                }

                console.error('El archivo {0} ruta {1} ya fue adjuntado'.format(fileName, fileMD5));
            }
        }

        setValidate(idFile);
        showHideBtnFile(idFile);

        var f = getFileFormConf(idFile);
        var evtAddFile = new CustomEvent('addFile', { 'detail': f });
        _$('#' + idFile)[0].dispatchEvent(evtAddFile);

        addEvtPreview(idFile);
        addFileGrid(idFile);
    }

    addEvtRemove();
}

/**
*Valida que un mismo archivo no se adjunte 2 veces
*/
function existFile(ruta) {
    var eFile = _$('ol').find('li[id="{0}"]'.format(ruta));
    return eFile.length > 0;
}

/**
*Asigna el evento de previsualización a los files
*/
function addEvtPreview(idFile) {
    _$('#' + idFile).find('li').unbind();
    _$('#' + idFile).find('li').on('click', function () {
        _previewAdjunto(this);
    });
}

/**
*Asigna el evento para remover los archivos
*/
function addEvtRemove() {
    _$('.remove-file').unbind();
    _$('.remove-file').on('click', function () {
        var idFile = _$(this).parents('ol').prop('id');
        var f = getFileFormConf(idFile);

        if (f.fnRemove)
            f.fnRemove.apply(_$(this).parent()[0], [f]);
        else
            removeFile(_$(this).parent()[0], f);
    });
}

/**
*Devuelve el contenido HTML necesario para crear un nodo li para el control adjuntar
*@param {string} url ruta del archivo adjunto
*@param {string} name nombre del archivo
*@param {boolean} remove true para que aparezca el botón para eliminar, false para que no aparezca
*@returns {string} contenido HTML con el li listo
*/
function getNewLiFile(url, name, remove) {
    var li = '<li id="{0}" title="{1}"><label>{1}</label><span class="remove-file {2}">x</span></li>'.format(url, name, (!remove) ? 'nb-hidden':'');
    return li;
}

/**
*Agrega un nuevo nodo li sobre el control adjuntar <ol>
*@param {string} idFile id que se asigna en el control HTML ol
*@param {string} url ruta del archivo adjunto
*@param {string} name nombre del archivo
*@param {boolean} remove true para que aparezca el botón para eliminar, false para que no aparezca
*/
function addNewLiFile(idFile, url, name, remove) {
    var li = getNewLiFile(url, name, remove);
    _$('#' + idFile).append(li);
}

/**
*Elimina un nodo li del control adjuntar
*@param {HTMLLIElement} li control li que se desea eliminar
*@param {object} f objeto de configuración del file
*/
function removeFile(li, f) {
    var idFile = _$(li).parents('ol').prop('id');
    var fileData = { 'file': li.id, 'name': li.title, 'idFile': idFile };

    _$(li).remove();
    showHideBtnFile(idFile);

    if (f != null && f.grid != null) {
        var conf = getGridDataConf(f);
        var idxFile = _gridFiles[conf.idGrid][conf.gridCol][conf.idxRow].findIndex(x => x.ruta == fileData.file);

        if (idxFile != -1)
            _gridFiles[conf.idGrid][conf.gridCol][conf.idxRow].splice(idxFile);
    }

    var evtRemoveFile = new CustomEvent('removeFile', { 'detail': fileData });
    _$('#' + idFile)[0].dispatchEvent(evtRemoveFile);

    setValidate(idFile);
}

/**
*Muestra y oculta el botón que dispara la acción de adjuntar para adjuntar de cada control
*@param {string} idFile id que se asigna en el control HTML ol
*/
function showHideBtnFile(idFile) {
    var f = getFileFormConf(idFile);

    if (_$('#' + idFile).find('li').length >= (f.numFiles * 1))
        _$('#btn' + idFile).addClass('nb-hidden');
    else
        _$('#btn' + idFile).removeClass('nb-hidden');
}

/**
*Muestra y oculta el modal de adjuntar
*@param {boolean} view true para ver, false para ocultar
*/
function viewUpload(view) {
    if (view) {
        _$(modalUpload).removeClass('nb-hidden');
        viewBloqOverlay(view);
    } else {
        _$(modalUpload).addClass('nb-hidden');
        viewBloqOverlay(view);
    }
}

function addBloqOverlay() {
    if (_$('.bloqOverlay').length == 0) {
        var bloqDiv = document.createElement("div");
        var loadDiv = document.createElement("div");
        loadDiv.id = '_lUpload';
        _$(bloqDiv).addClass('bloqOverlay nb-hidden');
        _$(loadDiv).addClass('loaderCont nb-hidden').html('<div class="loader"></div>');

        _$(document.body).append([bloqDiv, loadDiv]);
    }
}

function viewBloqOverlay(view) {
    if (view)
        _$('.bloqOverlay').removeClass('nb-hidden');
    else
        _$('.bloqOverlay').addClass('nb-hidden');
}

function setUploadIframe(uploadArgs) {
    var srcIframe = "{0}/solicitudes/plantillas/upload.aspx?t={1}&m={2}&c={3}&f={4}"
        .format(location.origin, uploadArgs.type, uploadArgs.kb, uploadArgs.numFiles, uploadArgs.idFile);

    _$('#_lUpload').removeClass('nb-hidden');

    if (_$('#_upload_').length == 0) {
        var upload = document.createElement("iframe");
        _$(upload).prop('id', '_upload_');
        _$(upload).prop('src', srcIframe);
        _$(upload).addClass('iframeUpload nb-hidden');
        _$(modalUpload).append(upload);

        _$(upload).on('load', function () {
            _$(this).removeClass('nb-hidden');
            _$('#_lUpload').addClass('nb-hidden');
        });
    } else {
        _$('#_upload_').addClass('nb-hidden');
        _$('#_upload_').prop('src', srcIframe);
    }
}

/**
*Previsualiza el adjunto en un modal
*@param {HTMLLIElement} li control li que se desea previsualizar
*/
function _previewAdjunto(li) {
    var id = li.id.toUpperCase();
    _$('#_nPrev').html(li.title);

    if (li.id.indexOf('archivos') == -1)
        _$('#_file_').prop('href', '../archivos/temp/' + li.id);
    else
        _$('#_file_').prop('href', li.id);

    _$('#_file_').prop('download', li.title);

    _$('.prevFile, #_prevAll').addClass('nb-hidden');
    _$('#_fPrev, #imgPrev').addClass('nb-hidden');
    _$('#_adjPrev').removeClass('nb-hidden');

    if (id.indexOf('.JPG') != -1 || id.indexOf('.PNG') != -1 || id.indexOf('.JPEG') != -1
        || id.indexOf('.GIF') != -1) {

        if (li.id.indexOf('archivos') == -1)
            _$('#imgPrev').prop('src', '../archivos/temp/' + li.id);
        else
            _$('#imgPrev').prop('src', li.id);

        _$('#imgPrev').removeClass('nb-hidden');
    } else if (id.indexOf('.PDF') != -1 || id.indexOf('.TXT') != -1) {
        if (li.id.indexOf('archivos') == -1)
            _$('#fPrev').prop('src', '../archivos/temp/' + li.id);
        else
            _$('#fPrev').prop('src', li.id);

        _$('#_fPrev').removeClass('nb-hidden');
    } else {
        _$('#msgNoPrev').removeClass('nb-hidden');
    }

    var ol = _$(li).parents('ol')[0];
    var confFile = getFileFormConf(ol.id);

    if (confFile != null && confFile.preview != null) {
        if (confFile.preview == 'fw7' && app != null)
            app.popup.open('#_prevAdjunto');
    } else {
        var evtPrevFile = new CustomEvent('previewFile', { 'detail': { 'idModal': '_prevAdjunto' } });
        ol.dispatchEvent(evtPrevFile);
    }
}

function setValidate(idFile) {
    if (_$('#_t' + idFile).length > 0) {
        var arrFiles = [];
        _$('#' + idFile).find('li').each(function () {
            arrFiles.push(this.title);
        });

        _$('#_t' + idFile).val((arrFiles.length == 0) ? '' : arrFiles.join(','));
    }
}

/**
*Convierte todos los files en el formulario de respuesta a la misma visualización que el formulario de edición
*@param {boolean} response true si se va a usar para formularios de respuesta, false si no
*/
function convertResponseAllFiles(response) {
    _$('.fileResp').each(function () {
        convertResponseFile(this, response);
    });
}

/**
*Carga todos los enlaces de los archivos adjuntos al nuevo esquema de control adjuntar
*@param {HTMLDivElement} dvFiles <div> contenedor que debe tener las clases fw7Link y fileResp
*@param {boolean} response true si se va a usar para formularios de respuesta, false si no
*/
function convertResponseFile(dvFiles, response) {
    var idFile = dvFiles.dataset.idf;

    var ol = '<ol id="{0}" class="view-file ufiles"></ol>'.format(idFile);
    _$(dvFiles).append(ol);

    _$(dvFiles).find('a').each(function () {
        _$(this).addClass('nb-hidden');
        var url = this.href;
        var name = this.innerHTML;

        if (response != null)
            addNewLiFile(idFile, url, name, true);
        else
            addNewLiFile(idFile, url, name, false);
    });

    addEvtPreview(idFile);

    if (response != null) {
        addEvtRemove();
        _$('#btn' + idFile).addClass('nb-hidden');
    }
}

/**
*Cargar los archivos ya guardados si el formulario se devuelve
*/
function loadFileInEdition() {
    convertResponseAllFiles(true);
}

/**
*Esta función se debe llamar antes de guardar los datos, si el formulario se devuelve
*/
function cleanFileInEdition() {
    _$('.ufiles').each(function () {
        _$(this).find('li').each(function () {
            var idFileS = this.id.toLowerCase();
            if (idFileS.startsWith('http')) {
                var idxF = idFileS.indexOf('archivos');
                var urlF = idFileS.substring(idxF+8);

                this.id = '';
                this.name = urlF;
                this.title = urlF;
            }
        });
    });
}

/**
*Función para agregar un adjunto en la grid
*@param {string} idFile id que se asigna en el control HTML ol
*/
function addFileGrid(idFile) {
    var f = getFileFormConf(idFile);

    if (f != null && f.grid != null) {
        var idGrid = f.grid;
        var gridCol = f.gridCol * 1;
        var grid = getGridForId(idGrid);

        var conf = getGridDataConf(f);

        if (_gridFiles[conf.idGrid][conf.gridCol][conf.idxRow] == null)
            _gridFiles[idGrid][gridCol][conf.idxRow] = [];

        _$('#{0} li'.format(idFile)).each(function () {
            var objAdj = getObjAdjunto(this.id, this.title);
            var arrFilesG = _gridFiles[conf.idGrid][conf.gridCol][conf.idxRow];

            var fAdd = arrFilesG.find(x => x.ruta == objAdj.ruta);
            if (fAdd == null) {
                arrFilesG.push(objAdj);
            }
        });
    }
}

/**
*Obtiene los datos de una grid basado en el objeto de configuración del adjunto
*@param {object} f Objeto de configuración que se obtiene con la función getFileFormConf
*@returns {object} objeto con la configuración de la grid
*/
function getGridDataConf(f) {
    var idGrid = f.grid;
    var gridCol = f.gridCol * 1;
    var grid = getGridForId(idGrid);
    var rId = _$('#_' + idGrid)[0].dataset.rid;
    var idxRow = (rId == "") ? getRowsNum(grid) : getRowIndexFromId(grid, rId);

    var data = { 'idGrid': idGrid, 'gridCol': gridCol, 'idxRow': idxRow };
    return data;
}

function getObjAdjunto(ruta, nombre) {
    var objAdj = { 'ruta': ruta, 'nombre': nombre };
    return objAdj;
}

/**
*Tiene la lógica para cargar los adjuntos en cada fila de la grid al abrir el modal
*@param {string} idGrid id de la grid
*/
function _addFileEvtGrid(idGrid) {
    var grid = getGridForId(idGrid);

    addEventGrid('onOpenPopup', grid, function (rId) {
        var idxRow = getRowIndexFromId(grid, rId);
        _loadGridFiles(idGrid, idxRow);
    });
}

/**
*Cargar los adjuntos de la grid cuando se abre el modal de una fila con adjuntos
*@param {string} idGrid id de la grid
*@param {integer} idxRow indice de la fila de la grid
*/
function _loadGridFiles(idGrid, idxRow) {
    for (var idxCol in _gridFiles[idGrid]) {
        var fConf = _filesForm.find(x => x.grid == idGrid && x.gridCol === idxCol);
        var arrFiles = _gridFiles[idGrid][idxCol][idxRow];

        if (arrFiles && arrFiles.length > 0) {
            _$('#' + fConf.idf).html('');

            for (var i = 0; i < arrFiles.length; i++) {
                var ff = arrFiles[i];
                addNewLiFile(fConf.idf, ff.ruta, ff.nombre, !isResponse());
            }

            addEvtPreview(fConf.idf);
            showHideBtnFile(fConf.idf);
        } else {
            _$('#' + fConf.idf).html('');
            showHideBtnFile(fConf.idf);
        }
    }

    addEvtRemove();
}

String.prototype.format = function () {
    var txt = this;
    for (var i = 0; i < arguments.length; i++) {
        var exp = new RegExp('\\{' + (i) + '\\}', 'gm');
        txt = txt.replace(exp, arguments[i]);
    }
    return txt;
}