/* +-------------------------------------------------------------------------+ | Copyright (C) 2004-2026 The Cacti Group | | | | 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 2 | | 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. | +-------------------------------------------------------------------------+ | Cacti: The Complete RRDtool-based Graphing Solution | +-------------------------------------------------------------------------+ | This code is designed, written, and maintained by the Cacti Group. See | | about.php and/or the AUTHORS file for specific developer information. | +-------------------------------------------------------------------------+ | http://www.cacti.net/ | +-------------------------------------------------------------------------+ */ (function($){ $.fn.zoom = function(options) { /* +++++++++++++++++++++++ Global Variables +++++++++++++++++++++++++ */ let storage = Storages.localStorage; // JS calculates in relation to the localization of the client - we have to take care of that, but only for 0.8.8 let clientTime = new Date(); let clientTimeOffset = clientTime.getTimezoneOffset()*60*(-1); //requires -1 as PHP returns the opposite let timeOffset = 0; let mouseDown = false; // default values of the different options being offered let defaults = { inputfieldStartTime : '', // ID of the input field that contains the start date inputfieldEndTime : '', // ID of the input field that contains the end date submitButton : 'button_refresh_x', // ID of the 'submit' button cookieName : 'cacti_zoom', // default name required for session cookie serverTimeOffset : 0, // JS calculates in relation to the localization of the browser (0.8.8 only) zoomMinTime : 725846400, rangeTitle : zoom_outOfRangeTitle, rangeMessage : zoom_outOfRangeMessage }; // define global variables / objects here let zoom = { initiator: $(this), // 'initiator' is the element that initiates Zoom image: { top:0, left:0, width:0, height:0, clientWidth: 0, clientHeight: 0, cfw: 1, cfh: 1}, // 'image' means the image tag and its properties graph: { timespan:0, secondsPerPixel:0 }, // 'graph' stands for the rrdgraph itself, excluding legend, graph title, etc. box: { top:0, left:0, right:0, width:0, height:0 }, // 'box' describes the area in front of the graph within jQueryZoom will allow interaction marker: { 1 : { placed:false }, 2 : { placed:false} }, // 'markers' are selectors usable within the advanced mode custom: { zoomMode : 'quick', // 'custom' holds the local configuration done by the user zoomOutPositioning : 'center', zoomOutFactor : '2', zoomTimestamps : 'auto', zoom3rdMouseButton : 'zoom_out' }, options: $.extend(defaults, options), // 'options' contains the start input parameters attr: { start : 'none', // 'attributes' holds all values that will describe the selected area end : 'none', action : 'left2right', location : window.location.href.split('?'), urlPath : ((typeof urlPath == 'undefined') ? '' : urlPath), origin : ((typeof location.origin == 'undefined') ? location.protocol + '//' + location.host : location.origin) }, raw: { // raw holds all raw data points and legend items, requires RRDtool 1.8.0+ data : [], legend : {}, step : 0, start : 0, end : 0, current_timeframe : 0, formatted_date : '', }, refs: { m: { 1 : {}, 2 : {} }, // 'references' allows addressing all zoom container elements without an extra document query livedata: { container: false, header: false, content: false, items: [] } }, obj: { date : new Intl.DateTimeFormat(navigator.languages, { year: "numeric", month: "numeric", day: "numeric", }), time : new Intl.DateTimeFormat(navigator.languages, { hour: "numeric", minute: "numeric", second: "numeric", hour12: false, }) }, livedata: true }; const si_prefixes = { '24': 'Y', // yotta '21': 'Z', // zeta '18': 'E', // eta '15': 'P', // peta '12': 'T', // tera '9': 'G', // giga '6': 'M', // mega '3': 'k', // kilo '0': ' ', '-3': 'm', // milli '-6': 'ยต', // micro '-9': 'n', // nano '-12': 'p', // pico '-15': 'f', // femto '-18': 'a', // atto '-21': 'z', // zepto '-24': 'y' // yocto }; // support jQuery's concatenation return this.each(function() { zoom_init( $(this) ); }); /* ++++++++++++++++++++ Universal Functions +++++++++++++++++++++++++ */ /** * transforms an object into a comma separated string of key-value pairs **/ function serialize(object){ let str = ''; for (let key in object) { str += (key + '=' + object[key] + ','); } return str.slice(0, -1); } /** * transforms a comma separated string of key-values pairs into an object * including a change of the value type from string to boolean or numeric if reasonable. **/ function deserialize(string){ let obj = []; if (string != null) { let pairs = string.split(','); for (var i=0; i 9) ? date.getDate() : '0' + date.getDate(); let hours = (date.getHours() > 9) ? date.getHours() : '0' + date.getHours(); let minutes = (date.getMinutes() > 9) ? date.getMinutes() : '0' + date.getMinutes(); let seconds = (date.getSeconds() > 9) ? date.getSeconds() : '0' + date.getSeconds(); return year + '-' + month + '-' + day + ' ' + hours + ':' + minutes + ':' + seconds; } /* +++++++++++++++++++++++ Core Functions +++++++++++++++++++++++++++ */ /* init zoom */ function zoom_init(image) { let activeElement = ''; image.parent().disableSelection(); image.off().on('mouseenter', function(){ if($('#zoom-container').length !== 0) { activeElement = $('#zoom-container').attr('data-active-element'); } if (!activeElement || activeElement !== zoomGetImageId(image)){ zoomFunction_init(image); } zoomLiveData_hide(); } ); } function zoomGetElement(zoom) { let id = '#' + zoom.image.reference; if (zoom.image.rra_id > 0) { id += '[rra_id=\'' + zoom.image.rra_id + '\']'; } return id; } function zoomGetImageId(image) { let id = image.attr('id'); if (image.attr('rra_id') > 0) { id += '_rra' + image.attr('rra_id'); } return id; } function zoomGetId(zoom) { let id = zoom.image.reference; if (zoom.image.rra_id > 0) { id += '_rra' + zoom.image.rra_id; } return id; } function zoomFunction_init(image) { /* load global settings cached in a cookie if available */ if (storage.isSet(zoom.options.cookieName)) { zoom.custom = deserialize(storage.get(zoom.options.cookieName)); } /* take care of different time zones server and client can make use of */ if (zoom.options.serverTimeOffset > clientTimeOffset ) { timeOffset = (zoom.options.serverTimeOffset - clientTimeOffset)*1000; } else { timeOffset = (clientTimeOffset - zoom.options.serverTimeOffset)*1000*(-1); } /* fetch all attributes that rrdgraph provides */ zoom.image.data = atob( zoom.initiator.attr('src').split(',')[1] ); zoom.image.type = (zoom.initiator.attr('src').split(';')[0] === 'data:image/svg+xml' )? 'svg' : 'png'; zoom.image.reference = zoom.initiator.attr('id'); zoom.image.id = zoom.image.reference.replace('graph_', ''); zoom.image.rra_id = zoom.initiator.attr('rra_id'); zoom.image.name = 'cacti_' + zoomGetImageId(zoom.initiator)+ '.' + zoom.image.type; zoom.image.legend = (!($('#thumbnails').length !== 0 && $('#thumbnails').is(':checked'))); zoom.image.top = parseInt(zoom.initiator.offset().top); zoom.image.left = parseInt(zoom.initiator.offset().left); zoom.image.width = parseInt(zoom.initiator.attr('image_width')); zoom.image.height = parseInt(zoom.initiator.attr('image_height')); zoom.image.clientWidth = image.parent()[0].clientWidth; zoom.image.clientHeight = image.parent()[0].clientHeight; zoom.image.cfw = parseFloat(zoom.image.clientWidth / zoom.image.width); zoom.image.width = zoom.image.clientWidth; zoom.image.cfh = parseFloat(zoom.image.clientHeight / zoom.image.height); zoom.image.height = zoom.image.clientHeight; zoom.image.right = zoom.image.left + zoom.image.width; zoom.graph.top = parseInt(zoom.initiator.attr('graph_top')) * zoom.image.cfh; zoom.graph.left = parseInt(zoom.initiator.attr('graph_left')) * zoom.image.cfw; zoom.graph.width = parseInt(zoom.initiator.attr('graph_width')) * zoom.image.cfw; zoom.graph.height = parseInt(zoom.initiator.attr('graph_height')) * zoom.image.cfh; zoom.graph.start = parseInt(zoom.initiator.attr('graph_start')); zoom.graph.end = parseInt(zoom.initiator.attr('graph_end')); zoom.graph.timespan = zoom.graph.end - zoom.graph.start; zoom.graph.secondsPerPixel = zoom.graph.timespan/zoom.graph.width; zoom.box.width = zoom.graph.width; zoom.box.height = zoom.graph.height; zoom.box.top = zoom.graph.top-1; zoom.box.bottom = zoom.graph.top + zoom.box.height; zoom.box.left = zoom.graph.left; zoom.box.right = zoom.box.left + zoom.box.width; if (typeof(zoom.initiator.attr('data-raw')) !== 'undefined') { let raw_data = JSON.parse(lzjs.decompressFromBase64(zoom.initiator.attr('data-raw'))); if(raw_data.data !== undefined && raw_data.data.length > 0) { zoom.raw.data = raw_data.data; } if(raw_data.meta !== undefined) { if (raw_data.meta.legend !== undefined) { for (let key in raw_data.meta.legend) { if (raw_data.meta.legend[key] !== '') { zoom.raw.legend[key] = raw_data.meta.legend[key]; } } } if (raw_data.meta.step !== undefined) zoom.raw.step = raw_data.meta.step; if (raw_data.meta.start !== undefined) zoom.raw.start = raw_data.meta.start; if (raw_data.meta.end !== undefined) zoom.raw.end = raw_data.meta.end; } /* reset time parameters */ zoom.raw.current_timeframe = 0; zoom.raw.formatted_date = ''; } // add all additional HTML elements to the DOM if necessary and register // the individual events needed. Once added we will only reset // and reposition these elements. // add zoom container plus elements to DOM if not existing and fill up reference cache zoom.refs.container = $('#zoom-container'); if (zoom.refs.container.length === 0) { zoom.refs.container = $('
').appendTo('body'); zoom.refs.box = $('
').appendTo('#zoom-container'); zoom.refs.crosshair_x = $('
').appendTo('#zoom-box'); zoom.refs.crosshair_y = $('
').appendTo('#zoom-box'); zoom.refs.area = $('
').appendTo('#zoom-container'); zoom.refs.m[1].excludedArea = $('
').appendTo('#zoom-container'); zoom.refs.m[1].marker = $('
').appendTo('#zoom-container'); zoom.refs.m[1].tooltip = $('
').appendTo('#zoom-marker-1'); zoom.refs.m[2].excludedArea = $('
').appendTo('#zoom-container'); zoom.refs.m[2].marker = $('
').appendTo('#zoom-container'); zoom.refs.m[2].tooltip = $('
').appendTo('#zoom-marker-2'); zoom.refs.container.removeClass().addClass('zoom_active_' + zoomGetId(zoom)); } else { zoom.refs.box = $('#zoom-box'); zoom.refs.crosshair_x = $('#zoom-crosshair-x'); zoom.refs.crosshair_y = $('#zoom-crosshair-y'); zoom.refs.area = $('#zoom-area'); zoom.refs.m[1].excludedArea = $('#zoom-excluded-area-1'); zoom.refs.m[1].marker = $('#zoom-marker-1'); zoom.refs.m[1].tooltip = $('#zoom-marker-tooltip-1'); zoom.refs.m[2].excludedArea = $('#zoom-excluded-area-2'); zoom.refs.m[2].marker = $('#zoom-marker-2'); zoom.refs.m[2].tooltip = $('#zoom-marker-tooltip-2'); zoom.refs.container.attr('data-active-element', zoomGetImageId(image)); } // add the context (right click) menu zoom.refs.menu = $('#zoom-menu'); if (zoom.refs.menu.length === 0) { zoom.refs.menu = $('
' + '
' + '
' + '' + zoom_i18n_zoom_in + '' + '
' + '
' + '
' + '
' + '' + zoom_i18n_zoom_out + '' + '
' + '' + zoom_i18n_zoom_2 + '' + '' + zoom_i18n_zoom_4 + '' + '' + zoom_i18n_zoom_8 + '' + '' + zoom_i18n_zoom_16 + '' + '' + zoom_i18n_zoom_32 + '' + '
' + '
' + '
' + '
' + '
' + '
' + '' + zoom_i18n_mode + '' + '
' + '' + zoom_i18n_quick + '' + '' + zoom_i18n_advanced + '' + '
' + '
' + '
' + '
' + '
' + '
' + '' + zoom_i18n_graph + '' + '
' + '' + zoom_i18n_newTab + '' + '' + zoom_i18n_save_graph + '' + '' + zoom_i18n_copy_graph + '' + '' + zoom_i18n_copy_graph_link + '' + '
' + '
' + '
' + '
' + '
' + '' + zoom_i18n_settings + '' + '
' + '
' + '
' + '' + zoom_i18n_timestamps + '' + '
' + '' + zoom_i18n_on + '' + '' + zoom_i18n_auto + '' + '' + zoom_i18n_off + '' + '
' + '
' + '
' + '
' + '
' + '' + zoom_i18n_zoom_out_factor + '' + '
' + '' + zoom_i18n_zoom_2 + '' + '' + zoom_i18n_zoom_4 + '' + '' + zoom_i18n_zoom_8 + '' + '' + zoom_i18n_zoom_16 + '' + '' + zoom_i18n_zoom_32 + '' + '
' + '
' + '
' + '
' + '' + zoom_i18n_zoom_out_positioning + '' + '
' + '' + zoom_i18n_begin + '' + '' + zoom_i18n_center + '' + '' + zoom_i18n_end + '' + '
' + '
' + '
' + '
' + '' + zoom_i18n_3rd_button + '' + '
' + '' + zoom_i18n_zoom_in + '' + '' + zoom_i18n_zoom_out + '' + '' + zoom_i18n_disabled + '' + '
' + '
' + '
' + '
' + '
' + '
' + '
' + '' + zoom_i18n_close + '' + '
').appendTo('body'); } // add a hidden anchor to use for downloads zoom.refs.image = $('#zoom-image'); if (zoom.refs.image.length === 0) { zoom.refs.image = $('').appendTo('body'); } // add a hidden textarea used to copy images / links zoom.refs.textarea = $('#zoom-textarea'); if (zoom.refs.textarea.length === 0) { zoom.refs.textarea = $('').appendTo('body'); } // add a hidden live data container zoom.refs.livedata.container = $('#zoom-livedata'); if (zoom.refs.livedata.container.length === 0) { zoom.refs.livedata.container = $('
').appendTo('body'); zoom.refs.livedata.header = $('
').appendTo('#zoom-livedata'); zoom.refs.livedata.content = $('
').appendTo('#zoom-livedata'); } else { zoom.refs.livedata.header = $('#zoom-livedata-header'); zoom.refs.livedata.content = $('#zoom-livedata-content'); zoom.refs.livedata.content.empty(); } for (let key in zoom.raw.legend) { let ref = 'zoom-livedata-item-' + key; zoom.refs.livedata.items[key] = $('
' + '
' + '
' + zoom.raw.legend[key].legend + '
' + '
' + '
' + '
').appendTo('#zoom-livedata-content'); zoom.refs.livedata.items[key].color = $('#'+ref+'-color'); zoom.refs.livedata.items[key].color.css("background-color", '#'+zoom.raw.legend[key].color); zoom.refs.livedata.items[key].title = $('#'+ref+'-title'); zoom.refs.livedata.items[key].value = $('#'+ref+'-value'); zoom.refs.livedata.items[key].unit = $('#'+ref+'-unit'); } // ensure that zoom markers are hidden zoom.marker[1].placed = false; zoom.marker[2].placed = false; zoomElements_reposition(); zoomElements_reset(); zoomContextMenu_init(); zoomAction_init(); } /** * reposition all elements of Zoom **/ function zoomElements_reposition() { zoom.refs.container.insertBefore( zoomGetElement(zoom) ); } /** * resets all elements of Zoom **/ function zoomElements_reset() { zoom.attr.start = 'none'; zoom.marker = { 1 : { placed:false }, 2 : { placed:false} }; zoom.refs.container.find('div[id^="zoom-"]:not("#zoom-menu")').removeAttr('style'); zoom.refs.container.find('div[id^="zoom-"]').css({ 'pointer-events': 'all' }); zoom.refs.container.off(); zoom.refs.container.on('contextmenu', '', '', function(e) { zoomContextMenu_toggle(e); return false; } ); zoom.refs.box.off('contextmenu'); zoom.refs.box.on('contextmenu', '','',function(e) { zoomContextMenu_toggle(e); return false; } ); zoom.refs.box.css({ cursor:'crosshair', width:zoom.box.width + 'px', height:zoom.box.height + 'px', top:zoom.box.top+'px', left:zoom.box.left+'px' } ); zoom.refs.area.off() zoom.refs.area.css({ top:zoom.graph.top+'px', height:zoom.graph.height+'px' } ); $('.zoom-area-excluded').off().on( 'contextmenu', function(e) { zoomContextMenu_toggle(e); return false;} ).on( 'click', function() { zoomContextMenu_hide(); return false;} ); $('.zoom-marker-tooltip-value').disableSelection(); // TODO - update CSS instead zoomLiveData_hide(); } /* * registers all the different mouse click event handler */ function zoomAction_init() { if (zoom.custom.zoomMode === 'quick') { zoom.box.width = zoom.graph.width; zoom.refs.box.css({ width: zoom.box.width + 'px' }); zoom.refs.area.resizable({ containment: '#zoom-box', handles: 'e, w' }); zoom.refs.box.off().on('mousedown', '', {zoom : zoom}, function(e) { switch(e.which) { /* clicking the left mouse button will initiate a zoom-in */ case 1: // ensure menu is closed zoomContextMenu_hide(); // hide Live Data zoomLiveData_hide(); // reset the zoom area zoom.attr.start = e.pageX; zoom.refs.box.css({ cursor:'e-resize' }); zoom.refs.area.css({ width:'0px', left: zoom.attr.start-zoom.image.left+'px', display:'block' }); break; } }).on( 'mousemove', function(e) { zoomAction_draw(e); zoomAction_position_crosshair(e) } ).on( 'mouseleave', function(e) { zoomAction_draw(e); zoomAction_position_crosshair(e) } ); // avoid that drawing stops if the mouse will be quickly moved into the opposite direction. zoom.refs.area.off('mousemove').on('mousemove', function(e) { zoomAction_draw(e); }); /* register the mouse up event */ $('#zoom-box, #zoom-area').off('mouseup').on('mouseup', '', {zoom:zoom},function(e) { switch(e.which) { /* leaving the left mouse button will execute a zoom in */ case 1: if (zoom.attr.start !== 'none') { zoomAction_zoom_in(); } break; case 2: /* hide context menu if open */ zoomContextMenu_hide(); if (zoom.custom.zoom3rdMouseButton === 'zoom_in') { zoomAction_zoom_in(); } else { zoomAction_zoom_out( zoom.custom.zoomOutFactor ); } break; } }); /* capture mouse up/down events for zoom */ $('body').off('mousedown.zoom').on('mousedown.zoom', function(e) { mouseDown = true; clearTimeout(myRefresh); }).off('mouseup.zoom').on('mouseup.zoom', function(e) { if (mouseDown) { if (zoom.attr.start !== 'none') { zoomAction_zoom_in(); } } mouseDown = false; }); /* moving the mouse pointer quickly will avoid it that the mousemove event has enough time to actualize the zoom area */ zoom.refs.container.on( 'mouseout', function(e) { zoomAction_draw(e); } ); } else{ /* welcome to the advanced mode ;) */ zoom.box.width = zoom.graph.width+1; zoom.refs.box.css({ width:zoom.box.width + 'px' }); zoom.refs.box.off().on('mousedown', '', {zoom:zoom},function(e) { let zoom = e.data.zoom; switch(e.which) { case 1: // ensure menu is closed zoomContextMenu_hide(); /* find out which marker has to be added */ if (zoom.marker[1].placed && zoom.marker[2].placed) { zoomAction_zoom_in(); return; } let marker = zoom.marker[1].placed ? 2 : 1; let secondmarker = (marker === 1) ? 2 : 1; /* select marker */ let $this = zoom.refs.m[marker].marker; /* place the marker and make it visible */ zoom.marker[marker].placed = true; zoom.marker[marker].left = e.pageX-zoom.image.left; zoom.marker[marker].right = zoom.image.right-e.pageX; $this.css({ height:zoom.box.height+'px', top:zoom.box.top+'px', left:zoom.marker[marker].left+'px', display:'block' }); /* place the marker's tooltip, update its value and make it visible if necessary (Setting: 'Always On') */ zoom.marker[marker].unixtime = parseInt(parseInt(zoom.graph.start) + (zoom.marker[marker].left - zoom.box.left)*zoom.graph.secondsPerPixel); $('#zoom-marker-tooltip-value-' + marker).html( zoomFormattedDateTime(zoom.marker[marker].unixtime).replace(' ', '
') ); /* use Vanilla JS as methods width() and height() by jQuery are unreliable for hidden elements */ zoom.marker[marker].height = document.getElementById('zoom-marker-tooltip-'+marker).offsetHeight; zoom.marker[marker].width = document.getElementById('zoom-marker-tooltip-'+marker).offsetWidth; zoom.marker[marker].dst_lt = zoom.marker[marker].left - zoom.marker[marker].width; zoom.marker[marker].dst_rt = zoom.marker[marker].right - zoom.marker[marker].width; /* make the excluded areas immediately visible if both markers are set */ if (zoom.marker[1].placed && zoom.marker[2].placed) { zoom.marker.distance = zoom.marker[marker].left - zoom.marker[secondmarker].left; zoom.refs.m[marker].excludedArea.css({ position:'absolute', height:zoom.box.height+'px', top:zoom.box.top+'px', left: (zoom.marker.distance > 0) ? zoom.marker[marker].left : zoom.box.left, width: (zoom.marker.distance > 0) ? zoom.box.right - zoom.marker[marker].left : zoom.marker[marker].left - zoom.box.left, display:'block' }); zoom.refs.m[secondmarker].excludedArea.css({ position:'absolute', height:zoom.box.height+'px', top:zoom.box.top+'px', left: (zoom.marker.distance > 0) ? zoom.box.left : zoom.marker[secondmarker].left, width: (zoom.marker.distance > 0) ? zoom.marker[secondmarker].left - zoom.box.left : zoom.box.right - zoom.marker[secondmarker].left, display:'block' }); /* change cursor */ zoom.refs.box.css({cursor: 'pointer'}); } if (zoom.custom.zoomTimestamps === true) { zoom.refs.m[marker].tooltip.show(0); } checkTooltipOrientation(marker); /* make the marker draggable */ $this.draggable({ containment: '#zoom-box', axis: 'x', scroll: false, start: function() { zoom.livedata = false; if (zoom.custom.zoomTimestamps === 'auto') { $('.zoom-marker-tooltip').fadeIn(500); } }, drag: function(event, ui) { if (ui.position['left'] <= zoom.box.left) { zoom.marker[marker].left = zoom.box.left; } else if (ui.position['left'] >= zoom.box.right) { zoom.marker[marker].left = zoom.box.right; } else { zoom.marker[marker].left = Math.ceil(parseFloat(ui.position['left'])); } zoom.marker[marker].right = zoom.image.width - Math.ceil(parseFloat(ui.position['left'])); /* update the timestamp shown in tooltip */ zoom.marker[marker].unixtime = Math.ceil( parseFloat(parseInt(zoom.graph.start) + (zoom.marker[marker].left - zoom.graph.left)*zoom.graph.secondsPerPixel)); $('#zoom-marker-tooltip-value-' + marker).html( zoomFormattedDateTime(zoom.marker[marker].unixtime).replace(' ', '
') ); /* use Vanilla JS as methods width() and height() by jQuery are unreliable for hidden elements */ zoom.marker[marker].width = document.getElementById('zoom-marker-tooltip-'+marker).offsetWidth; zoom.marker[marker].dst_lt = zoom.marker[marker].left - zoom.marker[marker].width; zoom.marker[marker].dst_rt = zoom.marker[marker].right - zoom.marker[marker].width; /* update the excludedArea if both markers have been placed */ if (zoom.marker[1].placed && zoom.marker[2].placed) { zoom.marker.distance = zoom.marker[marker].left - zoom.marker[secondmarker].left; if ( zoom.marker.distance > 0 ) { zoom.marker[marker].excludeArea = 'right'; zoom.marker[secondmarker].excludeArea = 'left'; } else { zoom.marker[marker].excludeArea = 'left'; zoom.marker[secondmarker].excludeArea = 'right'; } /* in that case, we have to update the tooltip of both markers */ zoom.refs.m[marker].excludedArea.css({ left: (zoom.marker.distance > 0) ? zoom.marker[marker].left : zoom.box.left, width: (zoom.marker.distance > 0) ? zoom.box.right - zoom.marker[marker].left : zoom.marker[marker].left - zoom.box.left}); zoom.refs.m[secondmarker].excludedArea.css({ left: (zoom.marker.distance > 0) ? zoom.box.left : zoom.marker[secondmarker].left, width: (zoom.marker.distance > 0) ? zoom.marker[secondmarker].left - zoom.box.left : zoom.box.right - zoom.marker[secondmarker].left}); } checkTooltipOrientation(marker); }, stop: function() { zoom.livedata = true; /* hide all tooltips if we are in auto mode */ if (zoom.custom.zoomTimestamps === 'auto') { $('.zoom-marker-tooltip').fadeOut(1000); } } }); break; case 2: if (zoom.custom.zoom3rdMouseButton !== false) { /* hide the context menu if open */ zoomContextMenu_hide(); if (zoom.custom.zoom3rdMouseButton === 'zoom_in') { zoomAction_zoom_in(); } else { zoomAction_zoom_out( zoom.custom.zoomOutFactor ); } } break; } return false; }).on( 'mousemove', function(e) { zoomAction_position_crosshair(e) } ).on( 'mouseleave', function(e) {zoomAction_position_crosshair(e) } ) } } function checkTooltipOrientation(marker) { let secondmarker = (marker === 1) ? 2 : 1; if(zoom.marker.distance === undefined) { let test = (zoom.marker[marker].dst_lt < 1); zoom.refs.m[marker].tooltip.toggleClass('relative-right', (zoom.marker[marker].dst_lt < 1) ); } else { if (zoom.marker.distance < 0) { // marker is left beside secondmarker; zoom.refs.m[marker].tooltip.toggleClass('relative-right', (zoom.marker[marker].dst_lt < 1)); zoom.refs.m[secondmarker].tooltip.toggleClass('relative-right', (zoom.marker[secondmarker].dst_rt > 1)); } else { // marker is right beside secondmarker; zoom.refs.m[marker].tooltip.toggleClass('relative-right', (zoom.marker[marker].dst_rt > 1)); zoom.refs.m[secondmarker].tooltip.toggleClass('relative-right', (zoom.marker[secondmarker].dst_lt < 1)); } } } /* * executes a dynamic zoom in */ function zoomAction_zoom_in(){ setCustomFilterActionActionAndDate(); /* hide context menu if open */ zoomContextMenu_hide(); let newGraphStartTime; let newGraphEndTime; if (zoom.custom.zoomMode === 'quick') { newGraphStartTime = (zoom.attr.action === 'left2right') ? parseInt(parseInt(zoom.graph.start) + (zoom.attr.start -zoom.image.left -zoom.box.left)*zoom.graph.secondsPerPixel) : parseInt(parseInt(zoom.graph.start) + (zoom.attr.end -zoom.image.left -zoom.box.left)*zoom.graph.secondsPerPixel); newGraphEndTime = (zoom.attr.action === 'left2right') ? Math.ceil( parseFloat(newGraphStartTime + (zoom.attr.end-zoom.attr.start)*zoom.graph.secondsPerPixel)) : parseInt(newGraphStartTime + (zoom.attr.start-zoom.attr.end)*zoom.graph.secondsPerPixel); /* If the user only clicked on a graph then equal end and start date to ensure that we do not propagate NaNs */ if (isNaN(newGraphStartTime) && isNaN(newGraphEndTime)) { return; } else if (isNaN(newGraphStartTime) && !isNaN(newGraphEndTime)) { newGraphStartTime = newGraphEndTime; } else if (!isNaN(newGraphStartTime) && isNaN(newGraphEndTime)){ newGraphEndTime = newGraphStartTime; } } else { /* advanced mode has other requirements */ /* first of, do nothing if not both markers have been positioned */ if (!zoom.marker[1].placed || !zoom.marker[2].placed) { alert('NOTE: In advanced mode both markers have to be positioned first to define the period of time you want to zoom in.'); return; } else { newGraphStartTime = zoom.marker[((zoom.marker[1].unixtime > zoom.marker[2].unixtime)? 2 : 1 )].unixtime; newGraphEndTime = zoom.marker[((zoom.marker[1].unixtime > zoom.marker[2].unixtime)? 1 : 2 )].unixtime; } } /* hide Zoom without destroying its container */ zoom.refs.container.html(''); if (zoom.options.inputfieldStartTime !== '' && zoom.options.inputfieldEndTime !== ''){ zoom.initiator.attr('graph_start', newGraphStartTime); zoom.initiator.attr('graph_end', newGraphEndTime); /* execute zoom within 'tree view' or the 'preview view' */ $('#' + zoom.options.inputfieldStartTime).val(unixTime2Date(newGraphStartTime)); $('#' + zoom.options.inputfieldEndTime).val(unixTime2Date(newGraphEndTime)); if (graph_start !== null && graph_end !== null) { zoom.attr.start = 'none'; if (pageAction !== 'graph') { graph_start = newGraphStartTime; graph_end = newGraphEndTime; if (newGraphStartTime >= defaults.zoomMinTime) { initializeGraphs(true); } } else{ $('#graph_start').val(newGraphStartTime); $('#graph_end').val(newGraphEndTime); if (newGraphStartTime >= defaults.zoomMinTime) { initializeGraphs(true); } } } else { $("input[name='" + zoom.options.submitButton + "']").trigger('click'); } zoomAction_update_session(newGraphStartTime, newGraphEndTime); return false; } else { /* graph view is already in zoom status */ open(zoom.attr.location[0] + '?action=' + zoom.graph.action + '&local_graph_id=' + zoom.graph.local_graph_id + '&rra_id=' + zoom.graph.rra_id + '&view_type=' + zoom.graph.view_type + '&graph_start=' + newGraphStartTime + '&graph_end=' + newGraphEndTime + '&graph_height=' + zoom.graph.height + '&graph_width=' + zoom.graph.width + '&title_font_size=' + zoom.graph.title_font_size + '&disable_cache=true', '_self'); } zoom.attr.start = 'none'; } /* * sets the predefined timespan to 'Custom' */ function setCustomFilterActionActionAndDate() { if (typeof $('#predefined_timespan').selectmenu() == 'function') { $('#predefined_timespan').val('0').selectmenu('refresh'); } else{ $('#predefined_timespan').val('0'); } } function getZoomOutFactorText(zoomOutFactor) { switch(zoomOutFactor) { case 2: return zoom_i18n_zoom_out + ' (' + zoom_i18n_zoom_2 + ')'; case 4: return zoom_i18n_zoom_out + ' (' + zoom_i18n_zoom_4 + ')'; case 8: return zoom_i18n_zoom_out + ' (' + zoom_i18n_zoom_8 + ')'; case 16: return zoom_i18n_zoom_out + ' (' + zoom_i18n_zoom_16 + ')'; case 32: return zoom_i18n_zoom_out + ' (' + zoom_i18n_zoom_32 + ')'; } } /* * executes a static zoom out (as right click event) */ function zoomAction_zoom_out(multiplier){ setCustomFilterActionActionAndDate(); /* hide context menus if open */ zoomContextMenu_hide(); multiplier--; /* avoid that we cannot zoom out anymore if start and end date will be equal */ if (zoom.graph.timespan === 0) { zoom.graph.timespan = 1; } let newGraphStartTime; let newGraphEndTime; if (zoom.custom.zoomMode === 'quick' || !zoom.marker[1].placed || !zoom.marker[2].placed ) { if (zoom.custom.zoomOutPositioning === 'begin') { newGraphStartTime = parseInt(zoom.graph.start); newGraphEndTime = parseInt(parseInt(zoom.graph.end) + (multiplier * zoom.graph.timespan)); } else if (zoom.custom.zoomOutPositioning === 'end') { newGraphStartTime = parseInt(parseInt(zoom.graph.start) - (multiplier * zoom.graph.timespan)); newGraphEndTime = parseInt(zoom.graph.end); } else { if ($('#future').val() === 'on') { // define the new start and end time, so that the selected area will be centered per default newGraphStartTime = parseInt(parseInt(zoom.graph.start) - (0.5 * multiplier * zoom.graph.timespan)); newGraphEndTime = parseInt(parseInt(zoom.graph.end) + (0.5 * multiplier * zoom.graph.timespan)); } else{ let now = parseInt(Date.now() / 1000); newGraphEndTime = parseInt(parseInt(zoom.graph.end) + (0.5 * multiplier * zoom.graph.timespan)); newGraphStartTime = parseInt(parseInt(zoom.graph.start) - (0.5 * multiplier * zoom.graph.timespan)); if (newGraphEndTime > now) { let offset = newGraphEndTime - now; newGraphEndTime = now; newGraphStartTime -= offset; } } } } else { newGraphStartTime = zoom.marker[((zoom.marker[1].unixtime > zoom.marker[2].unixtime)? 2 : 1 )].unixtime; newGraphEndTime = zoom.marker[((zoom.marker[1].unixtime > zoom.marker[2].unixtime)? 1 : 2 )].unixtime; let selectedTimeSpan = newGraphEndTime - newGraphStartTime; if (zoom.custom.zoomOutPositioning === 'begin') { newGraphEndTime = newGraphEndTime + multiplier * selectedTimeSpan; } else if (zoom.custom.zoomOutPositioning === 'end') { newGraphStartTime = newGraphStartTime - multiplier * selectedTimeSpan; } else { newGraphStartTime = parseInt(newGraphStartTime - 0.5 * multiplier * selectedTimeSpan); newGraphEndTime = parseInt(newGraphEndTime + 0.5 * multiplier * selectedTimeSpan); } } /* hide Zoom without destroying its container */ $('#zoom-container').html(''); if (zoom.options.inputfieldStartTime !== '' && zoom.options.inputfieldEndTime !== ''){ zoom.initiator.attr('graph_start', newGraphStartTime); zoom.initiator.attr('graph_end', newGraphEndTime); /* execute zoom within 'tree view' or the 'preview view' */ $('#' + zoom.options.inputfieldStartTime).val(unixTime2Date(newGraphStartTime)); $('#' + zoom.options.inputfieldEndTime).val(unixTime2Date(newGraphEndTime)); if (graph_start !== null && graph_end !== null) { zoom.attr.start = 'none'; if (pageAction !== 'graph') { graph_start = newGraphStartTime; graph_end = newGraphEndTime; if (newGraphStartTime >= defaults.zoomMinTime) { initializeGraphs(true); } } else{ $('#graph_start').val(newGraphStartTime); $('#graph_end').val(newGraphEndTime); if (newGraphStartTime >= defaults.zoomMinTime) { initializeGraph(); } } } else { $("input[name='" + zoom.options.submitButton + "']").trigger('click'); } zoomAction_update_session(newGraphStartTime, newGraphEndTime); } else { open(zoom.attr.location[0] + '?action=' + zoom.graph.action + '&local_graph_id=' + zoom.graph.local_graph_id + '&rra_id=' + zoom.graph.rra_id + '&view_type=' + zoom.graph.view_type + '&graph_start=' + newGraphStartTime + '&graph_end=' + newGraphEndTime + '&graph_height=' + zoom.graph.height + '&graph_width=' + zoom.graph.width + '&title_font_size=' + zoom.graph.title_font_size + '&disable_cache=true', '_self'); } } /* * when updating the zoom window, we have to update cacti's zoom session variables */ function zoomAction_update_session(newGraphStartTime, newGraphEndTime) { if (newGraphStartTime <= defaults.zoomMinTime) { PopupWarning(defaults.rangeMessage, defaults.rangeTitle); $('.graphimage').each(function() { $(this).zoom({ inputfieldStartTime: 'date1', inputfieldEndTime: 'date2', serverTimeOffset: timeOffset }); }); } else { $.get(document.location.pathname + '?action=update_timespan' + '&date1=' + unixTime2Date(newGraphStartTime) + '&date2=' + unixTime2Date(newGraphEndTime), function() { $('#predefined_timespan').val('0'); if (typeof $('#predefined_timespan').selectmenu() === 'object') { $('#predefined_timespan').selectmenu('refresh'); } }); } } /* * updates the CSS parameters of the zoom area to reflect user's interaction */ function zoomAction_draw(event) { if (zoom.attr.start === 'none') { return; } else { zoom.livedata = false; } /* the mouse has been moved from right to left */ if ((event.pageX-zoom.attr.start)<0) { zoom.attr.action = 'right2left'; zoom.attr.end = (event.pageX < zoom.image.left+zoom.box.left) ? zoom.image.left+zoom.box.left : event.pageX; zoom.refs.area.css({ left:zoom.attr.end-zoom.image.left+'px', width:Math.abs(zoom.attr.start-zoom.attr.end+1)+'px' }); /* the mouse has been moved from left to right*/ } else { zoom.attr.action = 'left2right'; zoom.attr.end = (event.pageX > zoom.image.left+zoom.box.right) ? zoom.image.left+zoom.box.right : event.pageX; zoom.refs.area.css({ left:zoom.attr.start-zoom.image.left+'px', width:Math.abs(zoom.attr.end-zoom.attr.start)+'px' }); } } function zoomAction_position_crosshair(event) { if (zoom.livedata === true && (event.type === 'mousemove' || event.type === 'mouseenter') ) { zoom.refs.crosshair_x.css('top', (event.pageY - parseInt(zoom.initiator.offset().top) - zoom.box.top)+"px").show(); zoom.refs.crosshair_y.css('left', (event.pageX - parseInt(zoom.initiator.offset().left) - zoom.box.left)+"px").show(); zoomLiveData_show(event); } else if (event.type === 'mouseleave') { zoomLiveData_hide() } } function zoomLiveData_hide() { zoom.refs.crosshair_x.hide(); zoom.refs.crosshair_y.hide(); zoom.refs.livedata.container.hide(); } /** * * @access public * @return void **/ function zoomContextMenu_init(){ /* sync menu with cookie parameters */ $('.zoomContextMenuAction__set_zoomMode__' + zoom.custom.zoomMode).addClass('zoom-menu-highlight'); $('.zoomContextMenuAction__set_zoomTimestamps__' + ((zoom.custom.zoomTimestamps === 'auto') ? 'auto' : ((zoom.custom.zoomTimestamps) ? 'on' : 'off' ))).addClass('zoom-menu-highlight'); $('.zoomContextMenuAction__set_zoomOutFactor__' + zoom.custom.zoomOutFactor).addClass('zoom-menu-highlight'); $('.zoomContextMenuAction__set_zoomOutPositioning__' + zoom.custom.zoomOutPositioning).addClass('zoom-menu-highlight'); $('.zoomContextMenuAction__set_zoom3rdMouseButton__' + ((zoom.custom.zoom3rdMouseButton === false) ? 'off' : zoom.custom.zoom3rdMouseButton) ).addClass('zoom-menu-highlight'); $('.zoomContextMenuAction__zoom_out').text(getZoomOutFactorText(zoom.custom.zoomOutFactor)); if (zoom.custom.zoomMode === 'quick') { $('.advanced_mode').hide(); //# TODO - check if still required. } /* init click on events */ $('[class^=zoomContextMenuAction__]').off().on('click', function() { let zoomContextMenuAction = false; let zoomContextMenuActionValue = false; let classList = $(this).attr('class').trim().split(/\s+/); $.each( classList, function(index, item){ if ( item.search('zoomContextMenuAction__') !== -1) { let zoomContextMenuActionList = item.replace('zoomContextMenuAction__', '').split('__'); zoomContextMenuAction = zoomContextMenuActionList[0]; if (zoomContextMenuActionList[1] === 'undefined' || zoomContextMenuActionList[1] === 'off') { zoomContextMenuActionValue = false; } else if (zoomContextMenuActionList[1] === 'on') { zoomContextMenuActionValue = true; } else { zoomContextMenuActionValue = zoomContextMenuActionList[1]; } return false; } }); if ( zoomContextMenuAction ) { if ( zoomContextMenuAction.substring(0,8) === 'set_zoom') { zoomContextMenuAction_set( zoomContextMenuAction.replace('set_zoom', '').toLowerCase(), zoomContextMenuActionValue); } else { zoomContextMenuAction_do( zoomContextMenuAction, zoomContextMenuActionValue); } } }); /* init hover events */ $('.first_li , .sec_li, .inner_li span').hover( function () { $(this).addClass('zoom-menu-hover'); if ( $(this).children().length >0 ) if (zoom.custom.zoomMode === 'quick') { $(this).children('.inner_li:not(.advanced_mode)').show(); } else { $(this).children('.inner_li').show(); } }, function () { $(this).removeClass('zoom-menu-hover'); $(this).children('.inner_li').hide(); } ); } /** * * @access public * @return void **/ function zoomContextMenuAction_set(object, value){ switch(object) { case 'mode': if ( zoom.custom.zoomMode !== value) { zoom.custom.zoomMode = value; $('[class*=zoomContextMenuAction__set_zoomMode__]').toggleClass('zoom-menu-highlight'); if (value === 'quick') { // reset menu $('.advanced_mode').hide(); zoom.custom.zoomMode = 'quick'; storage.set(zoom.options.cookieName, serialize(zoom.custom)); } else { // switch to advanced mode $('.sec_li.advanced_mode').show(); zoom.custom.zoomMode = 'advanced'; storage.set(zoom.options.cookieName, serialize(zoom.custom)); } zoomContextMenu_hide(); zoomElements_reset(); zoomAction_init(); } break; case 'timestamps': if ( zoom.custom.zoomTimestamps !== value) { zoom.custom.zoomTimestamps = value; storage.set(zoom.options.cookieName, serialize(zoom.custom)); $('[class*=zoomContextMenuAction__set_zoomTimestamps__]').removeClass('zoom-menu-highlight'); $('.zoomContextMenuAction__set_zoomTimestamps__' + ((zoom.custom.zoomTimestamps === 'auto') ? 'auto' : ((zoom.custom.zoomTimestamps) ? 'on' : 'off' ))).addClass('zoom-menu-highlight'); /* make them visible only for mode 'Always On' */ if (zoom.custom.zoomTimestamps === true) { $('.zoom-marker-tooltip').fadeIn(500); } else { $('.zoom-marker-tooltip').fadeOut(500); } } break; case 'outfactor': if ( zoom.custom.zoomOutFactor !== value) { zoom.custom.zoomOutFactor = value; storage.set(zoom.options.cookieName, serialize(zoom.custom)); $('[class*=zoomContextMenuAction__set_zoomOutFactor__]').removeClass('zoom-menu-highlight'); $('.zoomContextMenuAction__set_zoomOutFactor__' + value).addClass('zoom-menu-highlight'); $('.zoomContextMenuAction__zoom_out').text(getZoomOutFactorText(value)); } break; case 'outpositioning': if ( zoom.custom.zoomOutPositioning !== value) { zoom.custom.zoomOutPositioning = value; storage.set(zoom.options.cookieName, serialize(zoom.custom)); $('[class*=zoomContextMenuAction__set_zoomOutPositioning__]').removeClass('zoom-menu-highlight'); $('.zoomContextMenuAction__set_zoomOutPositioning__' + value).addClass('zoom-menu-highlight'); } break; case '3rdmousebutton': if ( zoom.custom.zoom3rdMouseButton !== value) { zoom.custom.zoom3rdMouseButton = value; storage.set(zoom.options.cookieName, serialize(zoom.custom)); $('[class*=zoomContextMenuAction__set_zoom3rdMouseButton__]').removeClass('zoom-menu-highlight'); $('.zoomContextMenuAction__set_zoom3rdMouseButton__' + ((value === false) ? 'off' : value)).addClass('zoom-menu-highlight'); } break; } } function zoomContextMenuAction_do(action, value){ switch(action) { case 'close': zoomContextMenu_hide(); break; case 'zoom_out': if (value === undefined) { value = zoom.custom.zoomOutFactor; } zoomAction_zoom_out(value); break; case 'zoom_in': zoomAction_zoom_in(); break; case 'copy': zoom.refs.textarea.html('').select(); try { let successful = document.execCommand('copy'); } catch (err) { alert('Unsupported Browser'); } return false; case 'save': let arraybuffer = new ArrayBuffer(zoom.image.data.length); let view = new Uint8Array(arraybuffer); for (let i = 0; i < zoom.image.data.length; i++) { view[i] = zoom.image.data.charCodeAt(i) & 0xff; } let blob; try { blob = new Blob([arraybuffer], {type: 'application/octet-stream'}); } catch (e) { let bb = new (window.WebKitBlobBuilder || window.MozBlobBuilder); bb.append(arraybuffer); blob = bb.getBlob('application/octet-stream'); } if (window.navigator && window.navigator.msSaveOrOpenBlob) { window.navigator.msSaveOrOpenBlob(blob, zoom.image.name); } else { let objectUrl = URL.createObjectURL(blob); zoom.refs.image.removeAttr('target').attr({'download':zoom.image.name, 'href':objectUrl }).get(0).click(); } break; case 'newTab': url = zoom.attr.urlPath + 'graph_image.php?local_graph_id=' + zoom.image.id; if (zoom.image.rra_id > 0) { url += '&rra_id='+zoom.image.rra_id; } url += '&graph_start=' + zoom.graph.start + '&graph_end=' + zoom.graph.end + '&graph_width=' + zoom.graph.width + '&graph_height=' + zoom.graph.height + ( (zoom.image.legend === true) ? '' : '&graph_nolegend=true' ) + '&disable_cache=true'; zoom.refs.image.removeAttr('download').attr({ 'href':url, 'target': '_bank' }).get(0).click(); break; case 'link': url = zoom.attr.origin + ((zoom.attr.urlPath === '') ? '/' : zoom.attr.urlPath) + 'graph_image.php?local_graph_id=' + zoom.image.id + '&graph_start=' + zoom.graph.start + '&graph_end=' + zoom.graph.end + '&graph_width=' + zoom.graph.width + '&graph_height=' + zoom.graph.height + ( (zoom.image.legend === true) ? '' : '&graph_nolegend=true' ) + '&disable_cache=true'; zoom.refs.textarea.html(url).select(); try { let successful = document.execCommand('copy'); } catch (err) { alert('Unsupported Browser'); } return false; default: break; } } function zoomLiveData_show(e) { if (zoom.raw.data.length > 0 && zoom.refs.menu.css('display') === 'none') { if (e.type === 'mousemove' || e.type === 'mouseenter') { let container_y_pos = e.pageY; let container_y_offset = 10; let container_x_pos = e.pageX; let container_x_offset = 10; let window_size_x_1 = $(document).scrollLeft(); let window_size_x_2 = $(window).width() + $(document).scrollLeft(); let window_size_y_1 = $(document).scrollTop(); let window_size_y_2 = $(window).height() + $(document).scrollTop(); let container_height = zoom.refs.livedata.container.outerHeight(); let container_width = zoom.refs.livedata.container.outerWidth(); let unixTime = parseInt(parseInt(zoom.graph.start) + (e.pageX - zoom.image.left - zoom.box.left + 1) * zoom.graph.secondsPerPixel); let unixTimeframe = -1; if (zoom.raw.step > 0) { unixTimeframe = unixTime - unixTime % zoom.raw.step; // avoid superfluous calculation steps immediately if (zoom.raw.current_timeframe !== unixTimeframe) { const index = (unixTimeframe - zoom.raw.start) / zoom.raw.step + 1; for (let key in zoom.raw.legend) { let dataIndex = key; dataIndex++; let value = null; if(zoom.raw.data.hasOwnProperty(index) && zoom.raw.data[index][dataIndex] !== undefined) { value = zoom.raw.data[index][dataIndex]; if (value !== null) { value = zoomFormatNumToSI(value); } } zoom.refs.livedata.items[key].value.html(value === null ? 'n/a ' : value); } zoom.raw.current_timeframe = unixTimeframe; } unixTime = unixTimeframe; } // avoid superfluous calculation steps immediately if (zoom.raw.formatted_date === '' || zoom.raw.current_timeframe !== unixTimeframe) { zoom.refs.livedata.header.html( zoomFormattedDateTime(unixTime) ); } if ((container_x_pos + container_x_offset + container_width) > window_size_x_2) { container_x_offset = -1 * (container_x_offset + container_width); } if ((container_y_pos + container_y_offset + container_height) > window_size_y_2) { container_y_offset = -1 * (container_y_offset + container_height); } zoom.refs.livedata.container.css({ left: container_x_pos + container_x_offset, top: container_y_pos + container_y_offset, zIndex: '101' }).show(); } else { if (e.type === 'mouseleave' && e.target === e.currentTarget) { zoom.refs.livedata.container.hide(); } } } } function zoomFormattedDateTime(unixTime) { const date = new Date(unixTime * 1000); return zoom.obj.date.format(date) + ' ' + zoom.obj.time.format(date); } function zoomFormatNumToSI(num) { const signPrefix = num < 0 ? '-' : ' '; let sig = Math.abs(num); let exponent = 0; if (num === 0) { return ' ' + new Big(0).toFixed(2) + ' ' + si_prefixes[exponent]; } while (sig >= 1000 && exponent < 24) { sig /= 1000; exponent += 3; } while (sig < 1 && exponent > -24) { sig *= 1000; exponent -= 3; } return signPrefix + new Big(sig).toFixed(2) + ' ' + si_prefixes[exponent]; } function zoomContextMenu_show(e) { let menu_y_pos = e.pageY; let menu_y_offset = 5; let menu_x_pos = e.pageX; let menu_x_offset = 5; let window_size_x_1 = $(document).scrollLeft(); let window_size_x_2 = $(window).width() + $(document).scrollLeft(); let window_size_y_1 = $(document).scrollTop(); let window_size_y_2 = $(window).height() + $(document).scrollTop(); let menu_height = zoom.refs.menu.outerHeight(); let menu_width = zoom.refs.menu.outerWidth(); let menu_width_level_1 = Math.abs($('.zoom-menu .first_li span').outerWidth()); let menu_width_level_2 = Math.abs($('.zoom-menu .sec_li span').outerWidth()); let menu_height_level_1 = Math.abs($('.zoom-menu .first_li span').outerHeight()); let menu_height_level_2 = Math.abs($('.zoom-menu .sec_li span').outerHeight()); /* hide livedata */ zoomLiveData_hide(); /* let the menu occur on the right per default if possible, otherwise move it to the left: */ if (( menu_x_pos + menu_x_offset + menu_width) > window_size_x_2 ) { menu_x_offset += (-1*menu_width); $('.zoom-menu .inner_li').css({ 'margin-left': -menu_width_level_1 }); } else { if (( menu_x_pos + menu_x_offset + menu_width + menu_width_level_1 + menu_width_level_2 ) > window_size_x_2) { $('.zoom-menu .inner_li').css({ 'margin-left': -menu_width_level_1 }); } else { $('.zoom-menu .inner_li').css({ 'margin-left': menu_width_level_1 }); } } if (( menu_y_pos + menu_y_offset + menu_height ) > window_size_y_2 ) { menu_y_offset += (-1*menu_height); } zoom.refs.menu.css({ left: menu_x_pos+menu_x_offset, top: menu_y_pos+menu_y_offset, zIndex: '102' }).show(); } function zoomContextMenu_hide(){ zoom.refs.menu.hide(); } function zoomContextMenu_toggle(e){ (zoom.refs.menu.css('display') === 'none') ? zoomContextMenu_show(e) : zoomContextMenu_hide(); } }; })(jQuery);