[9628] | 1 | @* HeuristicLab
|
---|
[9646] | 2 | * Copyright (C) 2002-2013 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
|
---|
[9628] | 3 | *
|
---|
| 4 | * This file is part of HeuristicLab.
|
---|
| 5 | *
|
---|
| 6 | * HeuristicLab is free software: you can redistribute it and/or modify
|
---|
| 7 | * it under the terms of the GNU General Public License as published by
|
---|
| 8 | * the Free Software Foundation, either version 3 of the License, or
|
---|
| 9 | * (at your option) any later version.
|
---|
| 10 | *
|
---|
| 11 | * HeuristicLab is distributed in the hope that it will be useful,
|
---|
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
---|
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
---|
| 14 | * GNU General Public License for more details.
|
---|
| 15 | *
|
---|
| 16 | * You should have received a copy of the GNU General Public License
|
---|
| 17 | * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
|
---|
| 18 | *@
|
---|
| 19 |
|
---|
[11020] | 20 | @helper AjaxDataRenderer()
|
---|
| 21 | {
|
---|
[9625] | 22 | <script>
|
---|
| 23 | var ajaxDataRenderer = function (url, plot, options) {
|
---|
| 24 | var ret = null;
|
---|
| 25 | $.ajax({
|
---|
| 26 | async: false,
|
---|
| 27 | url: url,
|
---|
| 28 | dataType: "json",
|
---|
| 29 | success: function (data) {
|
---|
| 30 | ret = data;
|
---|
| 31 | }
|
---|
| 32 | });
|
---|
| 33 | return ret;
|
---|
| 34 | };
|
---|
| 35 | </script>
|
---|
| 36 | }
|
---|
| 37 |
|
---|
[11246] | 38 | @helper LineChartTime(string destinationTag, string url, string title = "", double? minY = null, double? maxY = null, string axisYFormat = null)
|
---|
| 39 | {
|
---|
[11196] | 40 | <script>
|
---|
| 41 | var @(destinationTag)Plot = $.jqplot("@destinationTag", "@url", {
|
---|
| 42 | title: "@title",
|
---|
| 43 | highlighter: {
|
---|
| 44 | show: true,
|
---|
| 45 | sizeAdjust: 7.5
|
---|
| 46 | },
|
---|
| 47 | seriesDefaults: {
|
---|
| 48 | markerOptions: { show: false }
|
---|
| 49 | },
|
---|
| 50 | dataRenderer: ajaxDataRenderer,
|
---|
| 51 | axes: {
|
---|
| 52 | xaxis: {
|
---|
| 53 | renderer: $.jqplot.DateAxisRenderer,
|
---|
| 54 | pad: 0
|
---|
| 55 | },
|
---|
| 56 | yaxis: {
|
---|
| 57 | @if (axisYFormat != null)
|
---|
| 58 | {<text>
|
---|
| 59 | tickOptions: {
|
---|
| 60 | formatString: "@axisYFormat",
|
---|
| 61 | },
|
---|
| 62 | </text>}
|
---|
| 63 | autoscale: true,
|
---|
| 64 | pad: 0,
|
---|
| 65 | @if (minY != null)
|
---|
| 66 | {
|
---|
| 67 | @:min: @minY,
|
---|
| 68 | }
|
---|
| 69 | @if (maxY != null)
|
---|
| 70 | {
|
---|
| 71 | @:max: @maxY,
|
---|
| 72 | }
|
---|
| 73 | },
|
---|
| 74 | },
|
---|
| 75 | gridPadding: { left: 50, right: 10 },
|
---|
| 76 | cursor: {
|
---|
| 77 | show: true,
|
---|
| 78 | showTooltip: false,
|
---|
| 79 | zoom: true,
|
---|
| 80 | clickReset: false,
|
---|
| 81 | dblClickReset: false,
|
---|
| 82 | constrainZoomTo: 'x'
|
---|
| 83 | }
|
---|
| 84 | });
|
---|
[9625] | 85 |
|
---|
[11196] | 86 | $(window).resize(function() {
|
---|
| 87 | @(destinationTag)Plot.replot({ resetAxes: true });
|
---|
| 88 | });
|
---|
| 89 | </script>
|
---|
[9643] | 90 | }
|
---|
| 91 |
|
---|
[11246] | 92 | @helper RefreshChart(string destinationTag, string url, string startDate, string endDate, double? minY = null, double? maxY = null)
|
---|
| 93 | {
|
---|
[11020] | 94 | <text>
|
---|
[11036] | 95 | $.ajax({url: "@(new HtmlString(url))?start=" + @startDate + "&end=" + @endDate, datatype: "json", success: function(result) {
|
---|
[11020] | 96 | for (var i = 0; i < result.length; i++) {
|
---|
| 97 | @(destinationTag)Plot.series[i].data = result[i];
|
---|
| 98 | }
|
---|
[11196] | 99 | //Resets only the xaxis, still need to resize y with given min/max
|
---|
| 100 | @(destinationTag)Plot.replot({resetAxes:true});
|
---|
[11246] | 101 | @if (minY != null)
|
---|
| 102 | {
|
---|
[11196] | 103 | //If min Y was provided set the plot's min Y value
|
---|
| 104 | @:@(destinationTag)Plot.axes.yaxis.min = @minY;
|
---|
| 105 | }
|
---|
[11246] | 106 | @if (maxY != null)
|
---|
| 107 | {
|
---|
[11196] | 108 | //If max Y was provided set the plot's max Y value
|
---|
| 109 | @:@(destinationTag)Plot.axes.yaxis.max = @maxY;
|
---|
| 110 | }
|
---|
| 111 | @(destinationTag)Plot.axes.yaxis.reset();
|
---|
| 112 | //A final replot to redraw possible new Y axis values
|
---|
| 113 | @(destinationTag)Plot.replot({resetAxes:['xaxis']});
|
---|
| 114 | @* @(destinationTag)Plot.replot({ resetAxes:true });*@
|
---|
[11020] | 115 | }});
|
---|
| 116 | </text>
|
---|
| 117 | }
|
---|
| 118 |
|
---|
[11084] | 119 | @helper ResizeCharts()
|
---|
| 120 | {
|
---|
| 121 | <text>
|
---|
| 122 | function resizeCharts(caller) {
|
---|
| 123 | //If current plot is collapsed
|
---|
| 124 | if($(caller).css("display") == "none") {
|
---|
| 125 | //Display the contents of chartContainer
|
---|
| 126 | ResizeOpenClose($(caller).siblings(".collapse"));
|
---|
| 127 | //Reset barWidth for bar charts
|
---|
| 128 | $.each(window[caller.id].series, function(index, series) {
|
---|
| 129 | series.barWidth = undefined;
|
---|
| 130 | });
|
---|
| 131 | //Replot the chart with same name as element id
|
---|
| 132 | window[caller.id].replot({ resetAxes: false });
|
---|
| 133 | //Hide the contents of the current div
|
---|
| 134 | ResizeOpenClose($(caller).siblings(".collapse"));
|
---|
| 135 | }
|
---|
| 136 | //Current plot is expanded
|
---|
| 137 | else {
|
---|
| 138 | //Reset barWidth for bar charts
|
---|
| 139 | $.each(window[caller.id].series, function(index, series) {
|
---|
| 140 | series.barWidth = undefined;
|
---|
| 141 | });
|
---|
| 142 | //Replot the chart with same name as element id
|
---|
| 143 | window[caller.id].replot({ resetAxes: false });
|
---|
| 144 | }
|
---|
| 145 | }
|
---|
| 146 |
|
---|
| 147 | $(window).resize(function() {
|
---|
| 148 | $("div.jqplot-target",".chartContainer").each(function() {
|
---|
| 149 | var potentialTab = $(this).parent().parent().parent();
|
---|
| 150 | //If the section is tabular and currently hidden
|
---|
| 151 | if(potentialTab.hasClass("tabSection") && potentialTab.css("display") == "none") {
|
---|
| 152 | ResizeOpenCloseTabular(potentialTab);
|
---|
| 153 | resizeCharts(this);
|
---|
| 154 | ResizeOpenCloseTabular(potentialTab);
|
---|
| 155 | }
|
---|
| 156 | else {
|
---|
| 157 | resizeCharts(this);
|
---|
| 158 | }
|
---|
| 159 | });
|
---|
| 160 | });
|
---|
| 161 | </text>
|
---|
| 162 | }
|
---|
| 163 |
|
---|
[11036] | 164 | @helper NumberPages(string records, string limit, string container, string functionName, string currentPage = null)
|
---|
[11020] | 165 | {
|
---|
| 166 | <text>
|
---|
[11036] | 167 | if(@(records).length > @limit) {
|
---|
[11246] | 168 | var appendPages = '<section class="pageContainer"><label class="pageTitle">Page: </label>';
|
---|
[11036] | 169 | var pages = Math.floor(@(records).length/@(limit)) + 1;
|
---|
| 170 | for(var i=0; i < pages; i++) {
|
---|
[11246] | 171 | appendPages += '<a id="@(container)Page' + (i + 1) + '" class="page">' + (i + 1) + '</a>';
|
---|
[11036] | 172 | }
|
---|
[11246] | 173 | appendPages += '</section>';
|
---|
| 174 | $("#@container").append(appendPages);
|
---|
[11036] | 175 | if(@currentPage != null) {
|
---|
| 176 | $("#@(container)Page" + @currentPage).css('color','#F7921D');
|
---|
| 177 | }
|
---|
| 178 | else {
|
---|
| 179 | $("#@(container)Page1").css('color','#F7921D');
|
---|
| 180 | }
|
---|
| 181 | $(".page").click(function () {
|
---|
| 182 | pageNumber = $(this).html();
|
---|
| 183 | @(functionName)();
|
---|
| 184 | });
|
---|
| 185 | if(@currentPage != null) {
|
---|
| 186 | @(records).splice(0,(@(currentPage)-1)*@(limit));
|
---|
| 187 | if (@(records).length > @(limit)) {
|
---|
| 188 | @(records).splice(@(limit), @(records).length - @(limit));
|
---|
| 189 | }
|
---|
| 190 | }
|
---|
| 191 | else {
|
---|
| 192 | @(records).splice(@(limit), @(records).length - @(limit));
|
---|
| 193 | }
|
---|
| 194 | }
|
---|
| 195 | </text>
|
---|
| 196 | }
|
---|
| 197 |
|
---|
[11246] | 198 | @helper TaskContainers(string destinationTag, string url, string functionName, string userName, string limit, string startDate = null, string endDate = null, string jobId = null, string taskState = null, string pageNumber = null)
|
---|
[11036] | 199 | {
|
---|
| 200 | <text>
|
---|
[11084] | 201 | var GetRequest = "?userName=" + @(userName);
|
---|
| 202 | @if (startDate != null)
|
---|
| 203 | {
|
---|
[11030] | 204 | @:if(@(startDate)!=null) {
|
---|
| 205 | @:GetRequest += "&start=" + @startDate;
|
---|
| 206 | @:}
|
---|
| 207 | }
|
---|
[11084] | 208 | @if (endDate != null)
|
---|
| 209 | {
|
---|
[11030] | 210 | @:if(@(endDate)!=null) {
|
---|
| 211 | @:GetRequest += "&end=" + @endDate;
|
---|
| 212 | @:}
|
---|
| 213 | }
|
---|
[11084] | 214 | @if (jobId != null)
|
---|
| 215 | {
|
---|
[11030] | 216 | @:if(@(jobId)!=null) {
|
---|
| 217 | @:GetRequest += "&jobId=" + @jobId;
|
---|
| 218 | @:}
|
---|
| 219 | }
|
---|
[11084] | 220 | @if (taskState != null)
|
---|
| 221 | {
|
---|
[11030] | 222 | @:if(@(taskState)!=null) {
|
---|
| 223 | @:GetRequest += "&taskState=" + @taskState;
|
---|
| 224 | @:}
|
---|
| 225 | }
|
---|
[11036] | 226 | $.ajax({
|
---|
| 227 | async: false, url: "@(new HtmlString(url))" + GetRequest, datatype: "json", success: function(result) {
|
---|
[11053] | 228 | if(@(userName) == null) {
|
---|
[11084] | 229 | $("#@(destinationTag)").append(
|
---|
[11053] | 230 | '<section class="chartContainer">' +
|
---|
| 231 | '<h1 class="title">Please select a user!</h1>' +
|
---|
| 232 | '</section>'
|
---|
[11020] | 233 | )
|
---|
| 234 | }
|
---|
[11053] | 235 | else if(result.length==0) {
|
---|
[11084] | 236 | $("#@(destinationTag)").append(
|
---|
[11053] | 237 | '<section class="chartContainer">' +
|
---|
| 238 | '<h1 class="title">' + @userName + ' has no tasks for the specified filters!</h1>' +
|
---|
| 239 | '</section>'
|
---|
| 240 | )
|
---|
| 241 | }
|
---|
| 242 | else {
|
---|
| 243 | //Checks if multipage display, if it is then trims results to
|
---|
| 244 | //the results for the page to be displayed
|
---|
[11084] | 245 | @ChartHelper.NumberPages("result", limit, destinationTag, functionName, pageNumber)
|
---|
[11036] | 246 |
|
---|
[11053] | 247 | //Set display of all errors to none, errors matching the tasks will be reset below
|
---|
[11084] | 248 | $("#@(destinationTag)").find(".errorContainer, .errorTitle, .errorTask, .errorMessage").css('display','none');
|
---|
[11030] | 249 |
|
---|
[11222] | 250 | //Set variable for tracking jobId and open first job grouping
|
---|
| 251 | var currentJob = result[0].JobId;
|
---|
| 252 |
|
---|
[11053] | 253 | //For each result create a seperate collapsable section with a chart and info label
|
---|
| 254 | for(var i = 0; i < result.length; i++){
|
---|
[11246] | 255 | if(currentJob != result[i].JobId || i == 0) {
|
---|
| 256 | $("#@(destinationTag)").append(
|
---|
| 257 | '<section class="jobTaskGroup" id="' + result[i].JobId + '">' +
|
---|
| 258 | '<h1>' + result[i].JobName + '</h1>' +
|
---|
| 259 | '<section class="chartContainer">' +
|
---|
| 260 | '<h1 class="title" id="@(destinationTag)' + result[i].JobId + 'PlotTitle">Job Overview - ' + result[i].JobName + '</h1>' +
|
---|
| 261 | '<button class="collapse" onclick="LoadJob(this)">+</button>' +
|
---|
| 262 | '<div id="@(destinationTag)JobOverviewPlot' + i + '"></div>' +
|
---|
| 263 | '<label id="@(destinationTag)JobOverviewPlotInfo' + i + '"></label>' +
|
---|
| 264 | '</section>' +
|
---|
| 265 | '</section>'
|
---|
| 266 | );
|
---|
| 267 | CollapsedByDefault(document.getElementById("@(destinationTag)JobOverviewPlot" + i));
|
---|
[11222] | 268 | currentJob = result[i].JobId;
|
---|
| 269 | }
|
---|
| 270 | $("#" + currentJob).append(
|
---|
[11053] | 271 | '<section class="chartContainer">' +
|
---|
[11222] | 272 | '<h1 class="title" id="@(destinationTag)' + result[i].TaskId + 'PlotTitle">Task ' + result[i].TaskId + '</h1>' +
|
---|
[11246] | 273 | '<button class="collapse" onclick="LoadTask(this)">+</button>' +
|
---|
[11053] | 274 | '<div id="@(destinationTag)Plot' + i + '"></div>' +
|
---|
| 275 | '<label id="@(destinationTag)PlotInfo' + i + '"></label>' +
|
---|
[11222] | 276 | '<a id="' + result[i].TaskId + '" class="moreInfo" onclick="MoreTaskInfo(this)">More Info</a>' +
|
---|
[11053] | 277 | '</section>'
|
---|
[11246] | 278 | );
|
---|
[11053] | 279 | //Re-enables the error display if any of the tasks on the page have Ids matching
|
---|
| 280 | //those of errors
|
---|
[11084] | 281 | $(".errorTask","#" + "@(destinationTag)").each(function() {
|
---|
[11222] | 282 | if($(this).html()==result[i].TaskId) {
|
---|
| 283 | $("#@(destinationTag)" + result[i].TaskId + "PlotTitle").css("color","red");
|
---|
| 284 | $("#@(destinationTag)" + result[i].TaskId + "PlotTitle").append(" - ERROR");
|
---|
[11084] | 285 | $(".errorContainer, .errorTitle, .underline","#@(destinationTag)").css('display','inline-block');
|
---|
[11053] | 286 | $(this).css('display','inline-block');
|
---|
| 287 | $(this).next().css('display','inline-block');
|
---|
| 288 | }
|
---|
| 289 | });
|
---|
[11246] | 290 | CollapsedByDefault(document.getElementById("@(destinationTag)Plot" + i));
|
---|
| 291 | }
|
---|
| 292 | }
|
---|
| 293 | }});
|
---|
| 294 | </text>
|
---|
| 295 | }
|
---|
| 296 |
|
---|
| 297 | @helper LoadJob(string url)
|
---|
| 298 | {
|
---|
| 299 | <text>
|
---|
| 300 | function LoadJob(caller) {
|
---|
| 301 | CheckFilters();
|
---|
| 302 | if($(caller).next().html()=="") {
|
---|
| 303 | var plotName = $(caller).next().attr('id');
|
---|
| 304 | //Set current job id
|
---|
| 305 | var jobId = $(caller).parent().parent().attr('id');
|
---|
| 306 | var GetRequest = "?jobId=" + jobId;
|
---|
| 307 |
|
---|
| 308 | $.ajax({ async: false, url: "@(new HtmlString(url))" + GetRequest, datatype: "json", success: function(result) {
|
---|
| 309 | CollapseSection(caller);
|
---|
| 310 | var seriesDescription = ['Waiting','Transferring','Calculating','Finished','Error'];
|
---|
| 311 | window[plotName] = $.jqplot(plotName, [[result.Wait],[result.Transfer],[result.Calculate],[result.Finish],[result.Error]], {
|
---|
[11053] | 312 | seriesDefaults:{
|
---|
| 313 | renderer:$.jqplot.BarRenderer,
|
---|
| 314 | shadowAngle: 135,
|
---|
[11246] | 315 | pointLabels: {show: true, formatString: '%.0f'}
|
---|
[11020] | 316 | },
|
---|
[11053] | 317 | series:[
|
---|
[11246] | 318 | {label:seriesDescription[0]},
|
---|
| 319 | {label:seriesDescription[1]},
|
---|
| 320 | {label:seriesDescription[2]},
|
---|
| 321 | {label:seriesDescription[3]},
|
---|
| 322 | {label:seriesDescription[4]}
|
---|
[11053] | 323 | ],
|
---|
| 324 | legend: {
|
---|
| 325 | show: true,
|
---|
| 326 | location: 'e',
|
---|
| 327 | placement: 'outside'
|
---|
| 328 | },
|
---|
| 329 | axes: {
|
---|
| 330 | xaxis: {
|
---|
| 331 | renderer: $.jqplot.CategoryAxisRenderer,
|
---|
| 332 | showLabel: false,
|
---|
| 333 | pad: 0
|
---|
| 334 | },
|
---|
| 335 | yaxis: {
|
---|
[11246] | 336 | padMax: 1.5
|
---|
[11053] | 337 | }
|
---|
| 338 | },
|
---|
| 339 | cursor: {
|
---|
| 340 | showTooltip: false
|
---|
[11020] | 341 | }
|
---|
[11053] | 342 | });
|
---|
| 343 | /* Bind a datalistener to each chart and display details of clicked
|
---|
| 344 | upon data in the label below the chart */
|
---|
[11246] | 345 | $("#" + plotName).bind('jqplotDataClick', function (ev, seriesIndex, pointIndex, data) {
|
---|
| 346 | $(this).next("label").html("Tasks " + seriesDescription[seriesIndex] + ": " + data[1]);
|
---|
[11053] | 347 | });
|
---|
[11246] | 348 | }});
|
---|
| 349 | }
|
---|
| 350 | else {
|
---|
| 351 | CollapseSection(caller);
|
---|
| 352 | }
|
---|
| 353 | }
|
---|
| 354 | </text>
|
---|
| 355 | }
|
---|
[11030] | 356 |
|
---|
[11246] | 357 | @helper LoadTask(string url)
|
---|
| 358 | {
|
---|
| 359 | <text>
|
---|
| 360 | function LoadTask(caller) {
|
---|
| 361 | CheckFilters();
|
---|
| 362 | if($(caller).next().html()=="") {
|
---|
| 363 | var plotName = $(caller).next().attr('id');
|
---|
| 364 | //Set current slave id
|
---|
| 365 | var currentTask = $(caller).siblings('a.moreInfo').attr('id');
|
---|
| 366 | var GetRequest = "?taskId=" + currentTask;
|
---|
| 367 |
|
---|
| 368 | $.ajax({ async: false, url: "@(new HtmlString(url))" + GetRequest, datatype: "json", success: function(result) {
|
---|
| 369 | var waitTime;
|
---|
| 370 | var transferTime;
|
---|
| 371 | var runTime;
|
---|
| 372 | var seriesDescriptions = ["Time waiting","Time transferring","Time calculating"];
|
---|
| 373 | CollapseSection(caller);
|
---|
| 374 | for(i = 0; i < result.length; i++) {
|
---|
| 375 | waitTime = [result[i].TotalWaiting];
|
---|
| 376 | transferTime = [result[i].TotalTransfer];
|
---|
| 377 | runTime = [result[i].TotalRuntime];
|
---|
| 378 | window[plotName] = $.jqplot(plotName, [waitTime,transferTime,runTime], {
|
---|
| 379 | seriesDefaults:{
|
---|
| 380 | renderer:$.jqplot.BarRenderer,
|
---|
| 381 | shadowAngle: 135,
|
---|
| 382 | pointLabels: {show: true, formatString: '%.3f'}
|
---|
| 383 | },
|
---|
| 384 | series:[
|
---|
| 385 | {label:'Waiting'},
|
---|
| 386 | {label:'Transferring'},
|
---|
| 387 | {label:'Calculating'}
|
---|
| 388 | ],
|
---|
| 389 | legend: {
|
---|
| 390 | show: true,
|
---|
| 391 | location: 'e',
|
---|
| 392 | placement: 'outside'
|
---|
| 393 | },
|
---|
| 394 | axes: {
|
---|
| 395 | xaxis: {
|
---|
| 396 | renderer: $.jqplot.CategoryAxisRenderer,
|
---|
| 397 | showLabel: false,
|
---|
| 398 | pad: 0
|
---|
| 399 | },
|
---|
| 400 | yaxis: {
|
---|
| 401 | pad: 0
|
---|
| 402 | }
|
---|
| 403 | },
|
---|
| 404 | cursor: {
|
---|
| 405 | showTooltip: false
|
---|
| 406 | }
|
---|
| 407 | });
|
---|
| 408 | /* Bind a datalistener to each chart and display details of clicked
|
---|
| 409 | upon data in the label below the chart */
|
---|
| 410 | $("#" + plotName).bind('jqplotDataClick', function (ev, seriesIndex, pointIndex, data) {
|
---|
| 411 | $(this).next("label").html(seriesDescriptions[seriesIndex] + ": " + data[1].toFixed(4) + " seconds");
|
---|
| 412 | });
|
---|
| 413 | }
|
---|
| 414 | }});
|
---|
[11020] | 415 | }
|
---|
[11246] | 416 | else {
|
---|
| 417 | CollapseSection(caller);
|
---|
| 418 | }
|
---|
| 419 | }
|
---|
[11020] | 420 | </text>
|
---|
| 421 | }
|
---|
| 422 |
|
---|
[11084] | 423 | @helper SetStreamingProperties(int refresh, int chartLength, int upperY)
|
---|
[11020] | 424 | {
|
---|
| 425 | <text>
|
---|
| 426 | //Refresh time (in millisec)
|
---|
| 427 | var refreshRate = @(refresh);
|
---|
| 428 | //Number of data points on chart
|
---|
| 429 | var chartSize = @(chartLength);
|
---|
| 430 | //Amount to add to max Y value
|
---|
| 431 | var upperYBuffer = @(upperY);
|
---|
| 432 |
|
---|
| 433 | //Used to return a string containing the names of the series
|
---|
| 434 | //to be used in plot creation
|
---|
[11053] | 435 | function GetSeries(numberData,dataName){
|
---|
[11020] | 436 | var result = "[";
|
---|
| 437 | for(i=0; i < numberData; i++) {
|
---|
| 438 | if(i < numberData -1) {
|
---|
| 439 | result += dataName + i + ",";
|
---|
| 440 | }
|
---|
| 441 | else {
|
---|
| 442 | result += dataName + i + "]";
|
---|
| 443 | }
|
---|
| 444 | }
|
---|
| 445 | return result;
|
---|
| 446 | }
|
---|
| 447 | </text>
|
---|
| 448 | }
|
---|
| 449 |
|
---|
[11084] | 450 | @helper CreateStreamChart(string dataName, string destinationTag, string url, string title, string format = null, double? maxY = null)
|
---|
| 451 | {
|
---|
[11020] | 452 | <text>
|
---|
| 453 | //Get current time, used to create initial values
|
---|
| 454 | var @(dataName)CurrentDate = (new Date()).getTime();
|
---|
| 455 |
|
---|
| 456 | var @(dataName)Data = [];
|
---|
| 457 | var @(dataName)CurrentValue = [];
|
---|
| 458 |
|
---|
| 459 | //Get the most recent value(s) from the given URL
|
---|
| 460 | $.ajax({
|
---|
| 461 | async: false, url: '@(url)', datatype: "json", success: function (result) {
|
---|
| 462 | for(i = 0; i < result.length; i++) {
|
---|
| 463 | @(dataName)CurrentValue[i] = result[i];
|
---|
| 464 | }
|
---|
| 465 | }
|
---|
| 466 | });
|
---|
| 467 |
|
---|
| 468 | //Create a chartSize worth of data using CurrentDate and CurrentValue
|
---|
| 469 | //for each CurrentValue
|
---|
| 470 | for(i = 0; i < @(dataName)CurrentValue.length; i++) {
|
---|
| 471 | window["@(dataName)Data" + i] = [];
|
---|
| 472 | for (j = 0; j < chartSize; j++) {
|
---|
| 473 | window[ "@(dataName)Data" + i].push([@(dataName)CurrentDate - (chartSize - 1 - j) * refreshRate, @(dataName)CurrentValue[i]]);
|
---|
| 474 | }
|
---|
| 475 | }
|
---|
| 476 |
|
---|
| 477 | //Options for the chart to be created
|
---|
| 478 | var @(destinationTag)PlotOptions = {
|
---|
| 479 | title: "@title",
|
---|
| 480 | axes: {
|
---|
| 481 | xaxis: {
|
---|
| 482 | numberTicks: 4,
|
---|
| 483 | renderer: $.jqplot.DateAxisRenderer,
|
---|
| 484 | tickOptions: { formatString: '%H:%M:%S' },
|
---|
| 485 | min: window["@(dataName)Data" + 0][0][0],
|
---|
| 486 | max: window["@(dataName)Data" + 0][window["@(dataName)Data" + 0].length - 1][0]
|
---|
| 487 | },
|
---|
| 488 | yaxis: {
|
---|
| 489 | @if (format != null)
|
---|
| 490 | {<text>
|
---|
| 491 | tickOptions: {
|
---|
| 492 | formatString: "@format",
|
---|
| 493 | },
|
---|
| 494 | </text>}
|
---|
| 495 | min: 0,
|
---|
[11084] | 496 | @if (maxY != null)
|
---|
| 497 | {
|
---|
[11020] | 498 | @:max: @maxY,
|
---|
[9646] | 499 | }
|
---|
[11084] | 500 | else
|
---|
| 501 | {
|
---|
[11020] | 502 | @:max: window["@(dataName)Data" + 0][window["@(dataName)Data" + 0].length - 1][1] + upperYBuffer,
|
---|
| 503 | }
|
---|
| 504 | numberTicks: 6
|
---|
| 505 | }
|
---|
| 506 | },
|
---|
| 507 | seriesDefaults: {
|
---|
| 508 | rendererOptions: { smooth: true },
|
---|
| 509 | markerOptions: {
|
---|
| 510 | show: false
|
---|
| 511 | }
|
---|
| 512 | }
|
---|
| 513 | };
|
---|
| 514 |
|
---|
| 515 | //Declares the jqPlot variable, evals the string of series returned
|
---|
| 516 | //from getSeries which allows for any number of series
|
---|
[11053] | 517 | window[ "@(destinationTag)Plot"] = $.jqplot('@destinationTag', eval(GetSeries(@(dataName)CurrentValue.length,"@(dataName)Data")), @(destinationTag)PlotOptions);
|
---|
[11020] | 518 | </text>
|
---|
| 519 | }
|
---|
| 520 |
|
---|
[11084] | 521 | @helper UpdateStreamChart(string dataName, string destinationTag, string url, string fixedY = null)
|
---|
| 522 | {
|
---|
[11020] | 523 | <text>
|
---|
| 524 | //If the data is beyond chartSize use shift to trim one off the end
|
---|
| 525 | for(i = 0; i < @(dataName)CurrentValue.length; i++) {
|
---|
| 526 | if (window["@(dataName)Data" + i].length > chartSize - 1) {
|
---|
| 527 | window["@(dataName)Data" + i].shift();
|
---|
| 528 | }
|
---|
| 529 | }
|
---|
| 530 |
|
---|
| 531 | //Get the up-to-date data, each result assigned to it's own array
|
---|
| 532 | $.ajax({
|
---|
| 533 | async: false, url: '@(url)', datatype: "json", success: function (result) {
|
---|
| 534 | for(i = 0; i < result.length; i++) {
|
---|
| 535 | window[ "@(dataName)Data" + i].push([(new Date()).getTime(), result[i]]);
|
---|
| 536 | }
|
---|
| 537 | }
|
---|
| 538 | });
|
---|
| 539 |
|
---|
| 540 | //If the plot exists currently destroy it
|
---|
| 541 | if ( @(destinationTag)Plot) {
|
---|
| 542 | @(destinationTag)Plot.destroy();
|
---|
| 543 | }
|
---|
| 544 |
|
---|
| 545 | //Assign series
|
---|
| 546 | for(i = 0; i < @(dataName)CurrentValue.length; i++) {
|
---|
| 547 | @(destinationTag)Plot.series[i].data = window["@(dataName)Data" + i];
|
---|
| 548 | }
|
---|
| 549 |
|
---|
| 550 | //Recalculate x and possibly y axis max and mins
|
---|
| 551 | @(destinationTag)PlotOptions.axes.xaxis.min = window["@(dataName)Data" + 0][0][0];
|
---|
| 552 | @(destinationTag)PlotOptions.axes.xaxis.max = window["@(dataName)Data" + 0][window["@(dataName)Data" + 0].length - 1][0];
|
---|
[11084] | 553 | @if (fixedY == null)
|
---|
| 554 | {
|
---|
[11020] | 555 | @:@(destinationTag)PlotOptions.axes.yaxis.max = window["@(dataName)Data" + 0][window["@(dataName)Data" + 0].length - 1][1] + upperYBuffer;
|
---|
| 556 | }
|
---|
| 557 |
|
---|
| 558 | //Re-assigns the jqPlot variable, evals the string of series returned
|
---|
| 559 | //from getSeries which allows for any number of series
|
---|
[11053] | 560 | @(destinationTag)Plot = $.jqplot('@destinationTag', eval(GetSeries(@(dataName)CurrentValue.length,"@(dataName)Data")), @(destinationTag)PlotOptions);
|
---|
[11020] | 561 | </text>
|
---|
[11036] | 562 | }
|
---|
| 563 |
|
---|
[11246] | 564 | @helper SlaveContainers(string destinationTag, string url, string limit, bool singleSlave, string startDate = null, string endDate = null, string userName = null, string functionName = null, string pageNumber = null, string slaveId = null)
|
---|
[11084] | 565 | {
|
---|
[11036] | 566 | <text>
|
---|
[11084] | 567 | var GetRequest = "";
|
---|
| 568 | @if (startDate != null)
|
---|
| 569 | {
|
---|
[11036] | 570 | @:if(@(startDate)!=null) {
|
---|
[11084] | 571 | @:if(GetRequest == "") {
|
---|
| 572 | @:GetRequest += "?start=" + @startDate;
|
---|
| 573 | @:}
|
---|
| 574 | @:else {
|
---|
| 575 | @:GetRequest += "&start=" + @startDate;
|
---|
| 576 | @:}
|
---|
[11036] | 577 | @:}
|
---|
| 578 | }
|
---|
[11084] | 579 | @if (endDate != null)
|
---|
| 580 | {
|
---|
[11036] | 581 | @:if(@(endDate)!=null) {
|
---|
[11084] | 582 | @:if(GetRequest == "") {
|
---|
| 583 | @:GetRequest += "?end=" + @endDate;
|
---|
| 584 | @:}
|
---|
| 585 | @:else {
|
---|
| 586 | @:GetRequest += "&end=" + @endDate;
|
---|
| 587 | @:}
|
---|
[11036] | 588 | @:}
|
---|
| 589 | }
|
---|
[11084] | 590 | @if (userName != null)
|
---|
| 591 | {
|
---|
[11036] | 592 | @:if(@(userName)!=null) {
|
---|
[11084] | 593 | @:if(GetRequest == "") {
|
---|
| 594 | @:GetRequest += "?userName=" + @userName;
|
---|
| 595 | @:}
|
---|
| 596 | @:else {
|
---|
| 597 | @:GetRequest += "&userName=" + @userName;
|
---|
| 598 | @:}
|
---|
[11036] | 599 | @:}
|
---|
| 600 | }
|
---|
[11084] | 601 | @if (slaveId != null)
|
---|
| 602 | {
|
---|
[11053] | 603 | @:if(@(slaveId)!=null) {
|
---|
[11084] | 604 | @:if(GetRequest == "") {
|
---|
| 605 | @:GetRequest += "?slaveId=" + @slaveId;
|
---|
| 606 | @:}
|
---|
| 607 | @:else {
|
---|
| 608 | @:GetRequest += "&slaveId=" + @slaveId;
|
---|
| 609 | @:}
|
---|
[11053] | 610 | @:}
|
---|
| 611 | }
|
---|
[11246] | 612 | $.ajax({ async: false, url: "@(new HtmlString(url))" + GetRequest, datatype: "json", success: function(result) {
|
---|
| 613 | //Set chart names for use in creation below, must be set identically in LoadSlave and ResizeSlaves
|
---|
[11053] | 614 | var slaveChartNames = ["TotalUsedCores","TotalUsedMemory","CPUUtilization"];
|
---|
[11036] | 615 |
|
---|
[11246] | 616 | @if (!singleSlave) {
|
---|
| 617 | @:$('#@(destinationTag)').html("");
|
---|
| 618 |
|
---|
| 619 | //Checks if multipage display, if it is then trims results to
|
---|
| 620 | //the results for the page to be displayed
|
---|
| 621 | @ChartHelper.NumberPages("result", limit, destinationTag, functionName, pageNumber)
|
---|
| 622 |
|
---|
| 623 |
|
---|
| 624 | @:var destTag = "@(destinationTag)";
|
---|
[11059] | 625 | }
|
---|
[11246] | 626 | else {
|
---|
| 627 | @:var destTag = @(destinationTag);
|
---|
| 628 | }
|
---|
[11059] | 629 |
|
---|
[11053] | 630 | if(result.length == 0) {
|
---|
[11246] | 631 | @if (!singleSlave)
|
---|
| 632 | {
|
---|
[11084] | 633 | @:$('#' + destTag).html("");
|
---|
| 634 | }
|
---|
[11059] | 635 | $('#' + destTag).append(
|
---|
[11053] | 636 | '<section class="chartContainer">' +
|
---|
| 637 | '<h1 class="title">No slave information for the specified filters!</h1>' +
|
---|
| 638 | '</section>'
|
---|
| 639 | )
|
---|
| 640 | }
|
---|
| 641 |
|
---|
[11246] | 642 | for(i = 0; i < result.length; i++) {
|
---|
| 643 | $('#' + destTag).append(
|
---|
| 644 | '<section class="chartContainer">' +
|
---|
| 645 | '<h1 class="title" id="' + destTag + result[i][0].SlaveID + 'PlotTitle">Slave ' + result[i][0].ClientName + '</h1>' +
|
---|
| 646 | '<button class="collapse" onclick="LoadSlave(this)">+</button>' +
|
---|
| 647 | '<div id="' + destTag + slaveChartNames[0] + 'Plot' + result[i][0].SlaveID + '"></div>' +
|
---|
| 648 | '<div id="' + destTag + slaveChartNames[1] + 'Plot' + result[i][0].SlaveID + '"></div>' +
|
---|
| 649 | '<div id="' + destTag + slaveChartNames[2] + 'Plot' + result[i][0].SlaveID + '"></div>' +
|
---|
| 650 | '<a id="' + result[i][0].SlaveID + '" class="moreInfo" onclick="MoreSlaveInfo(this)">More Info</a>' +
|
---|
| 651 | '</section>');
|
---|
| 652 | for(k = 0; k < slaveChartNames.length; k++) {
|
---|
| 653 | CollapsedByDefault(document.getElementById(destTag + slaveChartNames[k] + "Plot" + result[i][0].SlaveID));
|
---|
| 654 | }
|
---|
| 655 | }
|
---|
| 656 | }});
|
---|
| 657 | </text>
|
---|
| 658 | }
|
---|
| 659 |
|
---|
| 660 | @helper LoadSlave(string url, string limit, string startDate = null, string endDate = null, string userName = null, string pageNumber = null) {
|
---|
| 661 | <text>
|
---|
| 662 | function LoadSlave(caller) {
|
---|
| 663 | CheckFilters();
|
---|
| 664 | if($(caller).next().html()=="") {
|
---|
| 665 | var destTag = $(caller).parent().parent().attr('id');
|
---|
| 666 | //Set current slave id
|
---|
| 667 | var currentSlave = $(caller).siblings('a.moreInfo').attr('id');
|
---|
| 668 | var GetRequest = "?slaveId=" + currentSlave;
|
---|
| 669 | @if (startDate != null)
|
---|
[11084] | 670 | {
|
---|
[11246] | 671 | @:if(@(startDate)!=null) {
|
---|
| 672 | @:GetRequest += "&start=" + @startDate;
|
---|
| 673 | @:}
|
---|
[11059] | 674 | }
|
---|
[11246] | 675 | @if (endDate != null)
|
---|
| 676 | {
|
---|
| 677 | @:if(@(endDate)!=null) {
|
---|
| 678 | @:GetRequest += "&end=" + @endDate;
|
---|
| 679 | @:}
|
---|
| 680 | }
|
---|
| 681 | @if (userName != null)
|
---|
| 682 | {
|
---|
| 683 | @:if(@(userName)!=null) {
|
---|
| 684 | @:GetRequest += "&userName=" + @userName;
|
---|
| 685 | @:}
|
---|
| 686 | }
|
---|
| 687 | $.ajax({ async: false, url: "@(new HtmlString(url))" + GetRequest, datatype: "json", success: function(result) {
|
---|
| 688 | //Set chart names for use in creation below, must be set identically in SlaveContainers and ResizeSlaves
|
---|
| 689 | var slaveChartNames = ["TotalUsedCores","TotalUsedMemory","CPUUtilization"];
|
---|
| 690 | CollapseSection(caller);
|
---|
| 691 | var time = new Date();
|
---|
[11053] | 692 | var coreSeries = [];
|
---|
| 693 | coreSeries[0] = [];
|
---|
| 694 | coreSeries[1] = [];
|
---|
| 695 | var memorySeries = [];
|
---|
| 696 | memorySeries[0] = [];
|
---|
| 697 | memorySeries[1] = [];
|
---|
| 698 | var cpuSeries = [];
|
---|
| 699 | cpuSeries[0] = [];
|
---|
[11246] | 700 | for(i = 0; i < result.length; i++) {
|
---|
| 701 | time.setTime(result[i].Time.replace(/\D/g,''));
|
---|
| 702 | coreSeries[0].push([time.toUTCString(),result[i].TotalCores]);
|
---|
| 703 | coreSeries[1].push([time.toUTCString(),result[i].UsedCores]);
|
---|
| 704 | memorySeries[0].push([time.toUTCString(),(result[i].TotalMemory / 1000)]);
|
---|
| 705 | memorySeries[1].push([time.toUTCString(),(result[i].UsedMemory / 1000)]);
|
---|
| 706 | cpuSeries[0].push([time.toUTCString(),result[i].CPUUtilization]);
|
---|
[11036] | 707 | }
|
---|
[11246] | 708 | if(result.length > 1) {
|
---|
| 709 | @ChartHelper.LineChartGivenSeries("destTag + slaveChartNames[0]", "currentSlave", "coreSeries", "Total/Used Cores")
|
---|
| 710 | @ChartHelper.LineChartGivenSeries("destTag + slaveChartNames[1]", "currentSlave", "memorySeries", "Total/Used Memory", 0)
|
---|
| 711 | @ChartHelper.LineChartGivenSeries("destTag + slaveChartNames[2]", "currentSlave", "cpuSeries", "CPU Utilization", 0, 100, "%.1f%%")
|
---|
[11053] | 712 | }
|
---|
| 713 | else {
|
---|
[11246] | 714 | @ChartHelper.BarChartGivenSeries("destTag + slaveChartNames[0]", "currentSlave", "coreSeries", "Total/Used Cores")
|
---|
| 715 | @ChartHelper.BarChartGivenSeries("destTag + slaveChartNames[1]", "currentSlave", "memorySeries", "Total/Used Memory", 0)
|
---|
| 716 | @ChartHelper.BarChartGivenSeries("destTag + slaveChartNames[2]", "currentSlave", "cpuSeries", "CPU Utilization", 0, 100, "%.1f%%")
|
---|
[11053] | 717 | }
|
---|
[11246] | 718 | }});
|
---|
[11036] | 719 | }
|
---|
[11246] | 720 | else {
|
---|
| 721 | CollapseSection(caller);
|
---|
| 722 | }
|
---|
| 723 | }
|
---|
[11036] | 724 | </text>
|
---|
| 725 | }
|
---|
| 726 |
|
---|
[11084] | 727 | @helper LineChartGivenSeries(string destinationTag, string chartId, string series, string title, double? minY = null, double? maxY = null, string axisYFormat = null)
|
---|
| 728 | {
|
---|
[11036] | 729 | <text>
|
---|
[11059] | 730 | window[@(destinationTag) + "Plot" + @(chartId)] = $.jqplot(@(destinationTag) + "Plot" + @(chartId), @series, {
|
---|
[11036] | 731 | title: "@title",
|
---|
| 732 | axes: {
|
---|
| 733 | xaxis: {
|
---|
| 734 | renderer: $.jqplot.DateAxisRenderer,
|
---|
| 735 | tickOptions:{formatString:'%b %#d, %y'},
|
---|
| 736 | pad: 0
|
---|
| 737 | },
|
---|
| 738 | yaxis: {
|
---|
[11084] | 739 | @if (axisYFormat != null)
|
---|
| 740 | {
|
---|
[11036] | 741 | <text>
|
---|
| 742 | tickOptions: {
|
---|
| 743 | formatString: "@axisYFormat"
|
---|
| 744 | },
|
---|
| 745 | </text>
|
---|
| 746 | }
|
---|
[11084] | 747 | pad: 0,
|
---|
| 748 | @if (minY != null)
|
---|
| 749 | {
|
---|
[11036] | 750 | @:min: @minY,
|
---|
| 751 | }
|
---|
[11084] | 752 | @if (maxY != null)
|
---|
| 753 | {
|
---|
[11036] | 754 | @:max: @maxY,
|
---|
| 755 | }
|
---|
| 756 | }
|
---|
| 757 | },
|
---|
| 758 | gridPadding: { left: 50, right: 10 },
|
---|
| 759 | cursor: {
|
---|
| 760 | show: true,
|
---|
| 761 | showTooltip: false,
|
---|
| 762 | zoom: true,
|
---|
| 763 | clickReset: false,
|
---|
| 764 | dblClickReset: false,
|
---|
| 765 | constrainZoomTo: 'x'
|
---|
| 766 | }
|
---|
| 767 | });
|
---|
| 768 | </text>
|
---|
| 769 | }
|
---|
| 770 |
|
---|
[11084] | 771 | @helper BarChartGivenSeries(string destinationTag, string chartId, string series, string title, double? minY = null, double? maxY = null, string axisYFormat = null)
|
---|
| 772 | {
|
---|
[11036] | 773 | <text>
|
---|
[11059] | 774 | window[@(destinationTag) + "Plot" + @(chartId)] = $.jqplot(@(destinationTag) + "Plot" + @(chartId), @series, {
|
---|
[11036] | 775 | title: "@title",
|
---|
| 776 | seriesDefaults:{
|
---|
| 777 | renderer:$.jqplot.BarRenderer,
|
---|
| 778 | shadowAngle: 135,
|
---|
| 779 | pointLabels: {show: true, formatString: '%.2f'}
|
---|
| 780 | },
|
---|
| 781 | axes: {
|
---|
| 782 | xaxis: {
|
---|
| 783 | renderer: $.jqplot.CategoryAxisRenderer,
|
---|
| 784 | tickOptions:{formatString:'%b %#d, %y'},
|
---|
| 785 | pad: 0
|
---|
| 786 | },
|
---|
| 787 | yaxis: {
|
---|
[11084] | 788 | @if (axisYFormat != null)
|
---|
| 789 | {
|
---|
[11036] | 790 | <text>
|
---|
| 791 | tickOptions: {
|
---|
| 792 | formatString: "@axisYFormat"
|
---|
| 793 | },
|
---|
| 794 | </text>
|
---|
| 795 | }
|
---|
[11084] | 796 | pad: 0,
|
---|
| 797 | @if (minY != null)
|
---|
| 798 | {
|
---|
[11036] | 799 | @:min: @minY,
|
---|
| 800 | }
|
---|
[11084] | 801 | @if (maxY != null)
|
---|
| 802 | {
|
---|
[11036] | 803 | @:max: @maxY,
|
---|
| 804 | }
|
---|
| 805 | }
|
---|
| 806 | },
|
---|
| 807 | gridPadding: { left: 50, right: 10 },
|
---|
| 808 | cursor: {
|
---|
| 809 | show: true,
|
---|
| 810 | showTooltip: false,
|
---|
| 811 | zoom: true,
|
---|
| 812 | clickReset: false,
|
---|
| 813 | dblClickReset: false,
|
---|
| 814 | constrainZoomTo: 'x'
|
---|
| 815 | }
|
---|
| 816 | });
|
---|
| 817 | </text>
|
---|
[11246] | 818 | }
|
---|
| 819 |
|
---|
| 820 | @helper UserTasks(string url, string insertAfter, string taskState) {
|
---|
| 821 | <text>
|
---|
| 822 | var GetRequest = "?taskState=@(taskState)";
|
---|
| 823 |
|
---|
| 824 | $.ajax({ async: false, url: "@(new HtmlString(url))" + GetRequest, datatype: "json", success: function(result) {
|
---|
| 825 | if(result.length==0) {
|
---|
| 826 | $("@(insertAfter)").after(
|
---|
| 827 | '<section class="chartContainer">' +
|
---|
| 828 | '<h1 class="title">No users currently have any tasks ' + @(taskState) + '!</h1>' +
|
---|
| 829 | '</section>'
|
---|
| 830 | )
|
---|
| 831 | }
|
---|
| 832 | else {
|
---|
| 833 | $("@(insertAfter)").after(
|
---|
| 834 | '<section class="chartContainer">' +
|
---|
| 835 | '<h1 class="title" id="UserTask@(taskState)PlotTitle">@(taskState) Tasks</h1>' +
|
---|
| 836 | '<button class="collapse" onclick="CollapseSection(this)">-</button>' +
|
---|
| 837 | '<div id="UserTask@(taskState)Plot" class="noXTicks"></div>' +
|
---|
| 838 | '</section>'
|
---|
| 839 | );
|
---|
| 840 | }
|
---|
| 841 | var userTasks = [];
|
---|
| 842 | var userNames = [];
|
---|
| 843 | for (i=0; i < result.length; i++) {
|
---|
| 844 | userTasks[i] = [result[i].Value];
|
---|
| 845 | userNames[i] = { label: result[i].Key };
|
---|
| 846 | }
|
---|
| 847 | window["UserTask@(taskState)Plot"] = $.jqplot("UserTask@(taskState)Plot", userTasks, {
|
---|
| 848 | title: "Users with @(taskState) Tasks",
|
---|
| 849 | seriesDefaults:{
|
---|
| 850 | renderer:$.jqplot.BarRenderer,
|
---|
| 851 | shadowAngle: 135,
|
---|
| 852 | pointLabels: {show: true, formatString: '%.3f'}
|
---|
| 853 | },
|
---|
| 854 | series: userNames,
|
---|
| 855 | legend: {
|
---|
| 856 | show: true,
|
---|
| 857 | location: 'e',
|
---|
| 858 | placement: 'outside'
|
---|
| 859 | },
|
---|
| 860 | axes: {
|
---|
| 861 | xaxis: {
|
---|
| 862 | renderer: $.jqplot.CategoryAxisRenderer,
|
---|
| 863 | showTicks: false,
|
---|
| 864 | pad: 0
|
---|
| 865 | },
|
---|
| 866 | yaxis: {
|
---|
| 867 | padMax: 1.5
|
---|
| 868 | }
|
---|
| 869 | },
|
---|
| 870 | cursor: {
|
---|
| 871 | showTooltip: false
|
---|
| 872 | }
|
---|
| 873 | });
|
---|
| 874 | }});
|
---|
| 875 | </text>
|
---|
[9625] | 876 | } |
---|