489 lines
17 KiB
JavaScript
489 lines
17 KiB
JavaScript
|
( function ( $, root, undefined ) {
|
||
|
root.rediscache = root.rediscache || {};
|
||
|
var rediscache = root.rediscache;
|
||
|
|
||
|
$.extend( rediscache, {
|
||
|
metrics: {
|
||
|
computed: null,
|
||
|
},
|
||
|
chart: null,
|
||
|
chart_defaults: {
|
||
|
noData: {
|
||
|
text: root.rediscache_metrics
|
||
|
? rediscache.l10n.no_data
|
||
|
: rediscache.l10n.no_cache,
|
||
|
align: 'center',
|
||
|
verticalAlign: 'middle',
|
||
|
offsetY: -25,
|
||
|
style: {
|
||
|
color: '#72777c',
|
||
|
fontSize: '14px',
|
||
|
fontFamily: 'inherit',
|
||
|
}
|
||
|
},
|
||
|
stroke: {
|
||
|
width: [2, 2],
|
||
|
curve: 'smooth',
|
||
|
dashArray: [0, 8],
|
||
|
},
|
||
|
colors: [
|
||
|
'#0096dd',
|
||
|
'#72777c',
|
||
|
],
|
||
|
annotations: {
|
||
|
texts: [{
|
||
|
x: '15%',
|
||
|
y: '30%',
|
||
|
fontSize: '20px',
|
||
|
fontWeight: 600,
|
||
|
fontFamily: 'inherit',
|
||
|
foreColor: '#72777c',
|
||
|
}],
|
||
|
},
|
||
|
chart: {
|
||
|
type: 'line',
|
||
|
height: $( '#metrics-pane #widget-redis-stats' ).length ? '300px' : '100%',
|
||
|
toolbar: { show: false },
|
||
|
zoom: { enabled: false },
|
||
|
animations: { enabled: false },
|
||
|
},
|
||
|
dataLabels: {
|
||
|
enabled: false,
|
||
|
},
|
||
|
legend: {
|
||
|
show: false,
|
||
|
},
|
||
|
fill: {
|
||
|
opacity: [0.25, 1],
|
||
|
},
|
||
|
xaxis: {
|
||
|
type: 'datetime',
|
||
|
labels: {
|
||
|
format: 'HH:mm',
|
||
|
datetimeUTC: false,
|
||
|
style: { colors: '#72777c', fontSize: '13px', fontFamily: 'inherit' },
|
||
|
},
|
||
|
tooltip: { enabled: false },
|
||
|
},
|
||
|
yaxis: {
|
||
|
type: 'numeric',
|
||
|
tickAmount: 4,
|
||
|
min: 0,
|
||
|
labels: {
|
||
|
style: { colors: '#72777c', fontSize: '13px', fontFamily: 'inherit' },
|
||
|
formatter: function ( value ) {
|
||
|
return Math.round( value );
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
tooltip: {
|
||
|
fixed: {
|
||
|
enabled: true,
|
||
|
position: 'bottomLeft',
|
||
|
offsetY: 15,
|
||
|
offsetX: 0,
|
||
|
},
|
||
|
}
|
||
|
},
|
||
|
templates: {
|
||
|
tooltip_title: _.template(
|
||
|
'<div class="apexcharts-tooltip-title"><%- title %></div>'
|
||
|
),
|
||
|
series_group: _.template(
|
||
|
'<div class="apexcharts-tooltip-series-group">' +
|
||
|
' <span class="apexcharts-tooltip-marker" style="background-color: <%- color %>;"></span>' +
|
||
|
' <div class="apexcharts-tooltip-text">' +
|
||
|
' <div class="apexcharts-tooltip-y-group">' +
|
||
|
' <span class="apexcharts-tooltip-text-label"><%- name %>:</span>' +
|
||
|
' <span class="apexcharts-tooltip-text-value"><%- value %></span>' +
|
||
|
' </div>' +
|
||
|
' </div>' +
|
||
|
'</div>'
|
||
|
),
|
||
|
series_pro: _.template(
|
||
|
'<div class="apexcharts-tooltip-series-group">' +
|
||
|
' <span class="apexcharts-tooltip-marker" style="background-color: <%- color %>;"></span>' +
|
||
|
' <div class="apexcharts-tooltip-text">' +
|
||
|
' <div class="apexcharts-tooltip-y-group">' +
|
||
|
' <span class="apexcharts-tooltip-text-label"><%- name %></span>' +
|
||
|
' </div>' +
|
||
|
' </div>' +
|
||
|
'</div>'
|
||
|
),
|
||
|
}
|
||
|
} );
|
||
|
|
||
|
// Build the charts by deep extending the chart defaults
|
||
|
$.extend( rediscache, {
|
||
|
charts: {
|
||
|
time: $.extend( true, {}, rediscache.chart_defaults, {
|
||
|
yaxis: {
|
||
|
labels: {
|
||
|
formatter: function ( value ) {
|
||
|
return Math.round( value ) + ' ms';
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
tooltip: {
|
||
|
custom: function ({ series, seriesIndex, dataPointIndex, w }) {
|
||
|
return [
|
||
|
rediscache.templates.tooltip_title({
|
||
|
title: new Date( w.globals.seriesX[ seriesIndex ][ dataPointIndex ] )
|
||
|
.toTimeString().slice( 0, 5 ),
|
||
|
}),
|
||
|
rediscache.templates.series_group({
|
||
|
color: rediscache.chart_defaults.colors[0],
|
||
|
name: w.globals.seriesNames[0],
|
||
|
value: series[0][ dataPointIndex ].toFixed(2) + ' ms',
|
||
|
}),
|
||
|
rediscache.templates.series_pro({
|
||
|
color: rediscache.chart_defaults.colors[1],
|
||
|
name: rediscache.l10n.pro,
|
||
|
}),
|
||
|
].join('');
|
||
|
},
|
||
|
},
|
||
|
} ),
|
||
|
bytes: $.extend( true, {}, rediscache.chart_defaults, {
|
||
|
yaxis: {
|
||
|
labels: {
|
||
|
formatter: function ( value ) {
|
||
|
var i = value === 0 ? 0 : Math.floor( Math.log( value ) / Math.log( 1024 ) );
|
||
|
|
||
|
return parseFloat( (value / Math.pow( 1024, i ) ).toFixed( i ? 2 : 0 ) ) + ' ' + ['B', 'KB', 'MB', 'GB', 'TB'][i];
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
tooltip: {
|
||
|
custom: function ({ series, seriesIndex, dataPointIndex, w }) {
|
||
|
var value = series[0][ dataPointIndex ];
|
||
|
var i = value === 0 ? 0 : Math.floor( Math.log( value ) / Math.log( 1024 ) );
|
||
|
var bytes = parseFloat( (value / Math.pow( 1024, i ) ).toFixed( i ? 2 : 0 ) ) + ' ' + ['B', 'KB', 'MB', 'GB', 'TB'][i];
|
||
|
|
||
|
return [
|
||
|
rediscache.templates.tooltip_title({
|
||
|
title: new Date( w.globals.seriesX[ seriesIndex ][ dataPointIndex ] ).toTimeString().slice( 0, 5 ),
|
||
|
}),
|
||
|
rediscache.templates.series_group({
|
||
|
color: rediscache.chart_defaults.colors[0],
|
||
|
name: w.globals.seriesNames[0],
|
||
|
value: bytes,
|
||
|
}),
|
||
|
rediscache.templates.series_pro({
|
||
|
color: rediscache.chart_defaults.colors[1],
|
||
|
name: rediscache.l10n.pro,
|
||
|
}),
|
||
|
].join('');
|
||
|
},
|
||
|
},
|
||
|
} ),
|
||
|
ratio: $.extend( true, {}, rediscache.chart_defaults, {
|
||
|
yaxis: {
|
||
|
max: 100,
|
||
|
labels: {
|
||
|
formatter: function ( value ) {
|
||
|
return Math.round( value ) + '%';
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
tooltip: {
|
||
|
custom: function ({ series, seriesIndex, dataPointIndex, w }) {
|
||
|
return [
|
||
|
rediscache.templates.tooltip_title({
|
||
|
title: new Date( w.globals.seriesX[ seriesIndex ][ dataPointIndex ] )
|
||
|
.toTimeString().slice( 0, 5 ),
|
||
|
}),
|
||
|
rediscache.templates.series_group({
|
||
|
color: rediscache.chart_defaults.colors[0],
|
||
|
name: w.globals.seriesNames[0],
|
||
|
value: Math.round( series[0][ dataPointIndex ] * 100 ) / 100 + '%',
|
||
|
}),
|
||
|
].join('');
|
||
|
},
|
||
|
},
|
||
|
} ),
|
||
|
calls: $.extend( true, {}, rediscache.chart_defaults, {
|
||
|
tooltip: {
|
||
|
custom: function ({ series, seriesIndex, dataPointIndex, w }) {
|
||
|
return [
|
||
|
rediscache.templates.tooltip_title({
|
||
|
title: new Date( w.globals.seriesX[ seriesIndex ][ dataPointIndex ] )
|
||
|
.toTimeString().slice( 0, 5 ),
|
||
|
}),
|
||
|
rediscache.templates.series_group({
|
||
|
color: rediscache.chart_defaults.colors[0],
|
||
|
name: w.globals.seriesNames[0],
|
||
|
value: Math.round( series[0][ dataPointIndex ] ),
|
||
|
}),
|
||
|
rediscache.templates.series_pro({
|
||
|
color: rediscache.chart_defaults.colors[1],
|
||
|
name: rediscache.l10n.pro,
|
||
|
}),
|
||
|
].join('');
|
||
|
},
|
||
|
},
|
||
|
} ),
|
||
|
},
|
||
|
} );
|
||
|
|
||
|
var compute_metrics = function ( raw_metrics ) {
|
||
|
var metrics = {};
|
||
|
|
||
|
// parse raw metrics in blocks of minutes
|
||
|
for ( var entry in raw_metrics ) {
|
||
|
var values = {};
|
||
|
var timestamp = raw_metrics[ entry ].timestamp;
|
||
|
var minute = ( timestamp - timestamp % 60 ) * 1000;
|
||
|
|
||
|
for ( var key in raw_metrics[ entry ] ) {
|
||
|
if ( raw_metrics[ entry ].hasOwnProperty( key ) ) {
|
||
|
values[ key ] = Number( raw_metrics[ entry ][ key ] );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( ! metrics[ minute ] ) {
|
||
|
metrics[ minute ] = [];
|
||
|
}
|
||
|
|
||
|
metrics[ minute ].push( values );
|
||
|
}
|
||
|
|
||
|
// calculate median value for each block
|
||
|
for ( var entry in metrics ) {
|
||
|
if ( metrics[ entry ].length === 1 ) {
|
||
|
metrics[ entry ] = metrics[ entry ].shift();
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
var medians = {};
|
||
|
|
||
|
for ( var key in metrics[ entry ][0] ) {
|
||
|
medians[ key ] = compute_median(
|
||
|
metrics[ entry ].map(
|
||
|
function ( metric ) {
|
||
|
return metric[ key ];
|
||
|
}
|
||
|
)
|
||
|
);
|
||
|
}
|
||
|
|
||
|
metrics[ entry ] = medians;
|
||
|
}
|
||
|
|
||
|
var computed = [];
|
||
|
|
||
|
for ( var timestamp in metrics ) {
|
||
|
var entry = metrics[ timestamp ];
|
||
|
|
||
|
entry.date = Number( timestamp );
|
||
|
entry.time = entry.time * 1000;
|
||
|
|
||
|
computed.push( entry );
|
||
|
}
|
||
|
|
||
|
computed.sort(
|
||
|
function( a, b ) {
|
||
|
return a.date - b.date;
|
||
|
}
|
||
|
);
|
||
|
|
||
|
return computed.length < 2 ? [] : computed;
|
||
|
};
|
||
|
|
||
|
var compute_median = function ( numbers ) {
|
||
|
var median = 0;
|
||
|
var numsLen = numbers.length;
|
||
|
|
||
|
numbers.sort();
|
||
|
|
||
|
if ( numsLen % 2 === 0 ) {
|
||
|
median = ( numbers[ numsLen / 2 - 1 ] + numbers[ numsLen / 2 ] ) / 2;
|
||
|
} else {
|
||
|
median = numbers[ ( numsLen - 1 ) / 2 ];
|
||
|
}
|
||
|
|
||
|
return median;
|
||
|
};
|
||
|
|
||
|
var render_chart = function ( id ) {
|
||
|
if ( rediscache.chart ) {
|
||
|
rediscache.chart.updateOptions( rediscache.charts[ id ] );
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
var chart = new ApexCharts(
|
||
|
document.querySelector( '#redis-stats-chart' ),
|
||
|
rediscache.charts[ id ]
|
||
|
);
|
||
|
|
||
|
chart.render();
|
||
|
root.rediscache.chart = chart;
|
||
|
};
|
||
|
|
||
|
var setup_charts = function () {
|
||
|
var metrics = {};
|
||
|
|
||
|
for ( var type in rediscache.charts ) {
|
||
|
if ( ! rediscache.charts.hasOwnProperty( type ) ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
metrics[type] = rediscache.metrics.computed.map(
|
||
|
function ( entry ) {
|
||
|
return [ entry.date, entry[type] ];
|
||
|
}
|
||
|
);
|
||
|
|
||
|
rediscache.charts[type].series = [{
|
||
|
name: rediscache.l10n[type],
|
||
|
type: 'area',
|
||
|
data: metrics[type],
|
||
|
}];
|
||
|
}
|
||
|
|
||
|
if ( ! rediscache.disable_pro ) {
|
||
|
var pro_charts = {
|
||
|
time: function ( entry ) {
|
||
|
return [ entry[0], entry[1] * 0.5 ]
|
||
|
},
|
||
|
bytes: function ( entry ) {
|
||
|
return [ entry[0], entry[1] * 0.3 ]
|
||
|
},
|
||
|
calls: function ( entry ) {
|
||
|
return [ entry[0], Math.round( entry[1] / 50 ) + 5 ]
|
||
|
},
|
||
|
};
|
||
|
|
||
|
for ( var type in pro_charts ) {
|
||
|
if ( ! rediscache.charts[type] ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
rediscache.charts[type].series.push({
|
||
|
name: rediscache.l10n.pro,
|
||
|
type: 'line',
|
||
|
data: metrics[type].map( pro_charts[type] ),
|
||
|
});
|
||
|
}
|
||
|
|
||
|
}
|
||
|
};
|
||
|
|
||
|
// executed on page load
|
||
|
$(function () {
|
||
|
var $tabs = $( '#rediscache .nav-tab-wrapper' );
|
||
|
var $panes = $( '#rediscache .content-column .tab-content' );
|
||
|
|
||
|
$tabs.find( 'a' ).on(
|
||
|
'click.redis-cache',
|
||
|
function ( event ) {
|
||
|
var toggle = $( this ).data( 'toggle' );
|
||
|
|
||
|
$( this ).blur();
|
||
|
|
||
|
show_tab( toggle );
|
||
|
|
||
|
if ( history.pushState ) {
|
||
|
history.pushState( null, null, '#' + toggle );
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
);
|
||
|
|
||
|
var firstRender = window.location.hash.indexOf('metrics') === -1;
|
||
|
|
||
|
var show_tab = function ( name ) {
|
||
|
$tabs.find( '.nav-tab-active' ).removeClass( 'nav-tab-active' );
|
||
|
$panes.find( '.tab-pane.active' ).removeClass( 'active' );
|
||
|
|
||
|
$( '#' + name + '-tab' ).addClass( 'nav-tab-active' );
|
||
|
$( '#' + name + '-pane' ).addClass( 'active' );
|
||
|
|
||
|
if (name === 'metrics' && firstRender) {
|
||
|
firstRender = false;
|
||
|
render_chart( 'time' );
|
||
|
}
|
||
|
};
|
||
|
|
||
|
var show_current_tab = function () {
|
||
|
var tabHash = window.location.hash.replace( '#', '' );
|
||
|
|
||
|
if ( tabHash !== '' && $( '#' + tabHash + '-tab' ) ) {
|
||
|
show_tab( tabHash );
|
||
|
}
|
||
|
};
|
||
|
|
||
|
show_current_tab();
|
||
|
|
||
|
$( window ).on( 'hashchange', show_current_tab );
|
||
|
|
||
|
if ( $( '#widget-redis-stats' ).length ) {
|
||
|
rediscache.metrics.computed = compute_metrics( root.rediscache_metrics );
|
||
|
|
||
|
setup_charts();
|
||
|
render_chart( 'time' );
|
||
|
}
|
||
|
|
||
|
$( '#widget-redis-stats ul a[data-chart]' ).on(
|
||
|
'click.redis-cache',
|
||
|
function ( event ) {
|
||
|
event.preventDefault();
|
||
|
|
||
|
$( '#widget-redis-stats .active' ).removeClass( 'active' );
|
||
|
$( this ).blur().addClass( 'active' );
|
||
|
|
||
|
render_chart(
|
||
|
$( event.target ).data( 'chart' )
|
||
|
);
|
||
|
}
|
||
|
);
|
||
|
|
||
|
$( '.notice.is-dismissible[data-dismissible]' ).on(
|
||
|
'click.roc-dismiss-notice',
|
||
|
'.notice-dismiss',
|
||
|
function ( event ) {
|
||
|
event.preventDefault();
|
||
|
|
||
|
var $parent = $( this ).parent();
|
||
|
|
||
|
$.post( ajaxurl, {
|
||
|
notice: $parent.data( 'dismissible' ),
|
||
|
action: 'roc_dismiss_notice',
|
||
|
_ajax_nonce: $parent.data( 'nonce' ),
|
||
|
} );
|
||
|
}
|
||
|
);
|
||
|
|
||
|
if ( $( '#redis-cache-copy-button' ).length ) {
|
||
|
if ( typeof ClipboardJS === 'undefined' ) {
|
||
|
$( '#redis-cache-copy-button' ).remove();
|
||
|
} else {
|
||
|
var successTimeout;
|
||
|
var clipboard = new ClipboardJS( '#redis-cache-copy-button .copy-button' );
|
||
|
|
||
|
clipboard.on( 'success', function( e ) {
|
||
|
var triggerElement = $( e.trigger ),
|
||
|
successElement = $( '.success', triggerElement.closest( 'div' ) );
|
||
|
|
||
|
e.clearSelection();
|
||
|
triggerElement.trigger( 'focus' );
|
||
|
|
||
|
clearTimeout( successTimeout );
|
||
|
successElement.removeClass( 'hidden' );
|
||
|
|
||
|
successTimeout = setTimeout( function() {
|
||
|
successElement.addClass( 'hidden' );
|
||
|
|
||
|
if ( clipboard.clipboardAction.fakeElem && clipboard.clipboardAction.removeFake ) {
|
||
|
clipboard.clipboardAction.removeFake();
|
||
|
}
|
||
|
}, 3000 );
|
||
|
|
||
|
} );
|
||
|
}
|
||
|
}
|
||
|
});
|
||
|
|
||
|
} ( window[ rediscache.jQuery ], window ) );
|