%PDF- %PDF-
Direktori : /var/www/projetos/suporte.iigd.com.br/js/ |
Current File : /var/www/projetos/suporte.iigd.com.br/js/common.js |
/** * --------------------------------------------------------------------- * * GLPI - Gestionnaire Libre de Parc Informatique * * http://glpi-project.org * * @copyright 2015-2024 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: '© <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+' '; i=i-1; } _elt.html(a+'»'+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} ${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 today = new Date(), strdate = new Date(str); today.setHours(0, 0, 0, 0); strdate.setHours(0, 0, 0, 0); var s = ( +new Date() - Date.parse(str) ) / 1e3, m = s / 60, h = m / 60, d = ( today - strdate ) / 864e5, w = d / 7, mo = d / 30.44, y = d / 365.24, 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') : w < 1.01 ? __('%s days ago').replace('%s', tmp) : (tmp = Math.floor(w)) === 1 ? __('a week ago') : mo < 1.01 ? __('%s weeks ago').replace('%s', tmp) : (tmp = Math.floor(mo)) === 1 ? __('a month ago') : y < 1 ? __('%s months ago').replace('%s', tmp) : (tmp = Math.floor(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); }); }); }