• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

eswdd / aardvark / 4031096410

pending completion
4031096410

push

github

GitHub
Add rel="noopener noreferrer" to external link (#247)

1622 of 2218 branches covered (73.13%)

3379 of 4186 relevant lines covered (80.72%)

83.13 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

77.91
/static-content/DygraphRenderer.js
1
aardvark
1✔
2
    .factory('DygraphRenderer', ['GraphServices', '$http', '$uibModal', 'tsdbClient', 'tsdbUtils', function(graphServices, $http, $uibModal, tsdbClient, tsdbUtils) {
3
        // todo: specifying ratio and auto scale is going to look darn odd - need to decide what to do - add issue for this..
4
        var renderer = {
87✔
5
            create: function() {
6
                var ret = {
87✔
7
                    type: "dygraph",
8
                    supports_tsdb_export: true,
9
                    supports_grafana_export: false,
10
                    tsdb_export_link: "",
11
                    grafana_export_text: ""
12
                };
13
                ret.render = function(renderContext, config, global, graph, queries, datum) {
87✔
14
                    ret.tsdb_export_link = "";
87✔
15
                    var fromTimestamp = graphServices.tsdb_fromTimestampAsTsdbString(global);
87✔
16
                    // validation
17
                    if (fromTimestamp == null || fromTimestamp == "") {
87✔
18
                        renderContext.renderErrors[graph.id] = "No start date specified";
1✔
19
                        return;
1✔
20
                    }
21
                    if (queries == null || queries.length == 0) {
86✔
22
                        renderContext.renderErrors[graph.id] = "No queries specified";
1✔
23
                        return;
1✔
24
                    }
25

26
                    var dygraphOptions = graph.dygraph;
85✔
27
                    if (!dygraphOptions) {
85✔
28
                        dygraphOptions = {};
11✔
29
                    }
30

31
                    if (dygraphOptions.countFilter != null && dygraphOptions.countFilter.count + "" != "" && dygraphOptions.countFilter.count < 1) {
85✔
32
                        renderContext.renderErrors[graph.id] = "Minimum count for filtering is 1";
1✔
33
                        return;
1✔
34
                    }
35
                    if (dygraphOptions.valueFilter != null && dygraphOptions.valueFilter.lowerBound != "" && dygraphOptions.valueFilter.upperBound != "" && dygraphOptions.valueFilter.lowerBound > dygraphOptions.valueFilter.upperBound) {
84✔
36
                        renderContext.renderErrors[graph.id] = "Upper bound on value filter is less than lower bound";
1✔
37
                        return;
1✔
38
                    }
39
                    if (dygraphOptions.valueFilter != null && dygraphOptions.valueFilter.lowerBound != "" && dygraphOptions.valueFilter.upperBound != "" && dygraphOptions.valueFilter.lowerBound == dygraphOptions.valueFilter.upperBound) {
83✔
40
                        renderContext.renderWarnings[graph.id] = "Lower bound on value filter is same as upper bound";
1✔
41
                    }
42
                    var y1AxisRange = graphServices.parseDygraphAxisRange(renderContext, graph, dygraphOptions.y1AxisRange);
83✔
43
                    var y2AxisRange = graphServices.parseDygraphAxisRange(renderContext, graph, dygraphOptions.y2AxisRange);
83✔
44

45
                    renderContext.renderMessages[graph.id] = "Loading...";
83✔
46

47
                    // baseline approach
48
                    // 1. make both queries
49
                    // 2. link results together so that we have a way of mapping from main to baseline
50
                    // 3. perform filtering based on main, but remove matching baseline if exclude
51
                    // 4. initial label setting (both sets)
52
                    // 5. auto-scaling and label adjustment (both together)
53
                    // 6. baseline time adjustment
54
                    // 7. min/max time calculations (together)
55
                    // 8. seperate processing
56
                    //  a. ratio graphs
57
                    //  b. mean adjustment
58
                    //  c. negative squashing
59
                    // 9. gap filling / merge timeseries (together)
60
                    // 10. merge labels
61
                    // 11. render graph
62

63
                    var processJson = function(mainJson, baselineJson) {
83✔
64
                        if (!mainJson || mainJson.length == 0) {
83✔
65
                            renderContext.renderErrors[graph.id] = "Empty response from TSDB";
2✔
66
                            renderContext.renderMessages[graph.id] = "";
2✔
67
                            return;
2✔
68
                        }
69
                        var baselining = global.baselining;
81✔
70
                        if ((baselining && (!baselineJson || baselineJson.length == 0))) {
81✔
71
                            renderContext.renderWarnings[graph.id] = "Empty response from TSDB for baseline query";
1✔
72
                            baselining = false;
1✔
73
                        }
74

75
                        var width = Math.floor(graph.graphWidth);
81✔
76
                        var height = Math.floor(graph.graphHeight);
81✔
77

78
                        var graphData = [];
81✔
79

80
                        // 3. perform filtering based on main, but remove matching baseline if exclude 
81
                        var filteredOut = [];
81✔
82
                        var measured = null;
81✔
83
                        if (dygraphOptions.countFilter != null && dygraphOptions.countFilter.count != "" && dygraphOptions.countFilter.count < mainJson.length) {
81✔
84
                            measured = new Array(mainJson.length);
10✔
85
                            var sorted = new Array(mainJson.length);
10✔
86
                            for (var i=0; i<mainJson.length; i++) {
10✔
87
                                switch (dygraphOptions.countFilter.measure) {
36✔
88
                                    case "mean":
89
                                        measured[i] = 0;
8✔
90
                                        for (var p=0; p<mainJson[i].dps.length; p++) {
8✔
91
                                            measured[i] +=  mainJson[i].dps[p][1];
24✔
92
                                        }
93
                                        measured[i] /= mainJson[i].dps.length;
8✔
94
                                        break;
8✔
95
                                    case "min":
96
                                        measured[i] = Number.MAX_VALUE;
12✔
97
                                        for (var p=0; p<mainJson[i].dps.length; p++) {
12✔
98
                                            measured[i] = Math.min(measured[i], mainJson[i].dps[p][1]);
36✔
99
                                        }
100
                                        break;
12✔
101
                                    case "max":
102
                                        measured[i] = Number.MIN_VALUE;
16✔
103
                                        for (var p=0; p<mainJson[i].dps.length; p++) {
16✔
104
                                            measured[i] = Math.max(measured[i], mainJson[i].dps[p][1]);
56✔
105
                                        }
106
                                        break;
16✔
107
                                }
108
                                sorted[i] = measured[i];
36✔
109
                            }
110
                            // increasing order
111
                            sorted.sort(function(a,b){return a-b;}); // default sort is alphanumeric!
36✔
112
                            if (dygraphOptions.countFilter.end == "top") {
10✔
113
                                var thresholdIndex1 = (mainJson.length - dygraphOptions.countFilter.count);
4✔
114
                                var threshold1 = sorted[thresholdIndex1];
4✔
115
                                for (var i=mainJson.length-1; i>=0; i--) {
4✔
116
                                    if (measured[i] < threshold1) {
16✔
117
                                        filteredOut.push(mainJson[i]);
7✔
118
                                        mainJson.splice(i,1);
7✔
119
                                        measured.splice(i,1);
7✔
120
                                    }
121
                                }
122
                            }
123
                            else if (dygraphOptions.countFilter.end == "bottom") {
6!
124
                                var thresholdIndex2 = dygraphOptions.countFilter.count - 1;
6✔
125
                                var threshold2 = sorted[thresholdIndex2];
6✔
126
                                for (var i=mainJson.length-1; i>=0; i--) {
6✔
127
                                    if (measured[i] > threshold2) {
20✔
128
                                        filteredOut.push(mainJson[i]);
9✔
129
                                        mainJson.splice(i,1);
9✔
130
                                        measured.splice(i,1);
9✔
131
                                    }
132
                                }
133
                            }
134
                        }
135
                        if (dygraphOptions.valueFilter != null && (dygraphOptions.valueFilter.lowerBound != "" || dygraphOptions.valueFilter.upperBound != "")) {
81✔
136
                            if ((dygraphOptions.countFilter && dygraphOptions.valueFilter.measure != dygraphOptions.countFilter.measure) || measured == null) {
14!
137
                                measured = new Array(mainJson.length);
14✔
138
                                for (var i=0; i<mainJson.length; i++) {
14✔
139
                                    switch (dygraphOptions.valueFilter.measure) {
56✔
140
                                        case "mean":
141
                                            measured[i] = 0;
12✔
142
                                            for (var p=0; p<mainJson[i].dps.length; p++) {
12✔
143
                                                measured[i] +=  mainJson[i].dps[p][1];
36✔
144
                                            }
145
                                            measured[i] /= mainJson[i].dps.length;
12✔
146
                                            break;
12✔
147
                                        case "min":
148
                                            measured[i] = Number.MAX_VALUE;
12✔
149
                                            for (var p=0; p<mainJson[i].dps.length; p++) {
12✔
150
                                                measured[i] = Math.min(measured[i], mainJson[i].dps[p][1]);
36✔
151
                                            }
152
                                            break;
12✔
153
                                        case "max":
154
                                            measured[i] = Number.MIN_VALUE;
12✔
155
                                            for (var p=0; p<mainJson[i].dps.length; p++) {
12✔
156
                                                measured[i] = Math.max(measured[i], mainJson[i].dps[p][1]);
36✔
157
                                            }
158
                                            break;
12✔
159
                                    }
160
                                }
161
                            }
162
                            var include = new Array(mainJson.length);
14✔
163
                            for (var i=mainJson.length-1; i>=0; i--) {
14✔
164
                                include[i] = true;
56✔
165
                                switch (dygraphOptions.valueFilter.measure) {
56✔
166
                                    case "mean":
167
                                    case "min":
168
                                    case "max":
169
                                        if (dygraphOptions.valueFilter.lowerBound != "") {
36✔
170
                                            if (measured[i] < dygraphOptions.valueFilter.lowerBound) {
24✔
171
                                                include[i] = false;
10✔
172
                                            }
173
                                        }
174
                                        if (dygraphOptions.valueFilter.upperBound != "") {
36✔
175
                                            if (measured[i] > dygraphOptions.valueFilter.upperBound) {
24✔
176
                                                include[i] = false;
8✔
177
                                            }
178
                                        }
179
                                        break;
36✔
180
                                    case "any":
181
                                        include[i] = false;
20✔
182
                                        for (var p=0; p<mainJson[i].dps.length; p++) {
20✔
183
                                            var includePoint = true;
49✔
184
                                            if (dygraphOptions.valueFilter.lowerBound != "") {
49✔
185
                                                if (mainJson[i].dps[p][1] < dygraphOptions.valueFilter.lowerBound) {
41✔
186
                                                    includePoint = false;
24✔
187
                                                }
188
                                            }
189
                                            if (dygraphOptions.valueFilter.upperBound != "") {
49✔
190
                                                if (mainJson[i].dps[p][1] > dygraphOptions.valueFilter.upperBound) {
38✔
191
                                                    includePoint = false;
16✔
192
                                                }
193
                                            }
194
                                            if (includePoint) {
49✔
195
                                                include[i] = true;
9✔
196
                                                break;
9✔
197
                                            }
198

199
                                        }
200
                                }
201
                                if (!include[i]) {
56✔
202
                                    filteredOut.push(mainJson[i]);
29✔
203
                                    mainJson.splice(i,1);
29✔
204
                                    measured.splice(i,1);
29✔
205
                                }
206
                            }
207
                        }
208

209
                        if (mainJson.length == 0) {
81✔
210
                            renderContext.renderErrors[graph.id] = "Value filtering excluded all time series";
1✔
211
                            renderContext.renderMessages[graph.id] = "";
1✔
212
                            return;
1✔
213
                        }
214

215
                        // now filter out of baselineJson based on filteredOut
216
                        if (baselining && measured != null) {
80✔
217
                            var filteredOutByQuery = {};
2✔
218
                            for (var s=0; s<filteredOut.length; s++) {
2✔
219
                                var str = JSON.stringify(filteredOut[s].query);
2✔
220
                                if (!(str in filteredOutByQuery)) {
2!
221
                                    filteredOutByQuery[str] = [];
2✔
222
                                }
223
                                filteredOutByQuery[str].push(filteredOut[s]);
2✔
224
                            }
225

226
                            var allTagsMatch = function(tags1, tags2) {
2✔
227
                                var tagsArray1 = [];
2✔
228
                                for (var k in tags1) {
2✔
229
                                    if (tags1.hasOwnProperty(k)) {
×
230
                                        tagsArray1.push(k);
×
231
                                    }
232
                                }
233
                                var tagsArray2 = [];
2✔
234
                                for (var k in tags2) {
2✔
235
                                    if (tags2.hasOwnProperty(k)) {
×
236
                                        tagsArray2.push(k);
×
237
                                    }
238
                                }
239
                                if (tagsArray1.length != tagsArray2.length) {
2!
240
                                    return false;
×
241
                                }
242
                                for (var i=0; i<tagsArray1.length; i++) {
2✔
243
                                    var k = tagsArray1[i];
×
244
                                    if (tags2.hasOwnProperty(k)) {
×
245
                                        if (tags1[k] != tags2[k]) {
×
246
                                            return false;
×
247
                                        }
248
                                    }
249
                                    else {
250
                                        return false;
×
251
                                    }
252
                                }
253
                                return true;
2✔
254
                            }
255

256
                            for (var s=baselineJson.length-1; s>=0; s--) {
2✔
257
                                var str = JSON.stringify(baselineJson[s].query);
3✔
258
                                // ok, we have definitely removed some results from this query
259
                                if (str in filteredOutByQuery) {
3✔
260
                                    for (var i=0; i<filteredOutByQuery[str].length; i++) {
2✔
261
                                        if (allTagsMatch(baselineJson[s].tags, filteredOutByQuery[str][i].tags)) {
2!
262
                                            baselineJson.splice(s,1);
2✔
263
                                            filteredOutByQuery[str].splice(i,1);
2✔
264
                                            if (filteredOutByQuery[str].length == 0) {
2!
265
                                                delete filteredOutByQuery[str];
2✔
266
                                            }
267
                                            break;
2✔
268
                                        }
269
                                    }
270
                                }
271
                            }
272
                        }
273

274

275
                        // indices, used to track progress through dps arrays
276
                        var mainIndices1 = new Array(mainJson.length);
80✔
277
                        var mainIndices2 = new Array(mainJson.length);
80✔
278
                        var mainIndices3 = new Array(mainJson.length);
80✔
279
                        for (var s=0; s<mainJson.length; s++) {
80✔
280
                            mainIndices1[s] = 0;
146✔
281
                            mainIndices2[s] = 0;
146✔
282
                            mainIndices3[s] = 0;
146✔
283
                        }
284
                        var baselineIndices1;
80✔
285
                        var baselineIndices2;
80✔
286
                        var baselineIndices3;
80✔
287
                        if (baselining) {
80✔
288
                            baselineIndices1 = new Array(baselineJson.length);
22✔
289
                            baselineIndices2 = new Array(baselineJson.length);
22✔
290
                            baselineIndices3 = new Array(baselineJson.length);
22✔
291
                            for (var s=0; s<baselineJson.length; s++) {
22✔
292
                                baselineIndices1[s] = 0;
33✔
293
                                baselineIndices2[s] = 0;
33✔
294
                                baselineIndices3[s] = 0;
33✔
295
                            }
296
                        }
297

298
                        // now we've filtered we can work out the set of annotations which need rendering
299
                        var annotations = [];
80✔
300
                        var discoverAnnotations = function(json, indices, isBaseline) {
80✔
301
                            var globalAnnotations = [];
30✔
302
                            for (var s=0; s<json.length; s++) {
30✔
303
                                if (json[s].annotations != null) {
48!
304
                                    json[s].annotations.sort(function(a,b){
48✔
305
                                        var ret = a.startTime - b.startTime;
8✔
306
                                        if (ret == 0) {
8!
307
                                            if (a.endTime == null || b.endTime == null) {
×
308
                                                return 0;
×
309
                                            }
310
                                            return a.endTime - b.endTime;
×
311
                                        }
312
                                        return ret;
8✔
313
                                    });
314
                                    for (var p= 0, a=0; p<json[s].dps.length && a<json[s].annotations.length; ) {
48✔
315
                                        var t = json[s].dps[p][0];
50✔
316
                                        var annT = json[s].annotations[a].startTime; // todo: how do we represent endTime with dygraph?
50✔
317
                                        if (t < annT) {
50✔
318
                                            // annotation after last point
319
                                            if (p == json[s].dps.length - 1) {
34✔
320
                                                // add a point at the end
321
                                                // let dygraphs interpolate
322
                                                json[s].dps.push([annT,null]);
4✔
323
                                                // insert annotation
324
                                                annotations.push([json[s], json[s].annotations[a], isBaseline]);
4✔
325
                                                // next annotation
326
                                                a++;
4✔
327
                                            }
328
                                            // annotation after a mid point
329
                                            else {
330
                                                p++;
30✔
331
                                            }
332
                                        }
333
                                        else if (t == annT) {
16✔
334
                                            // we have a point at the correct time, this is good
335
                                            //                            console.log("inserting annotation at existing point")
336
                                            annotations.push([json[s], json[s].annotations[a], isBaseline]);
8✔
337
                                            a++;
8✔
338
                                        }
339
                                        else { // t > annT
340
                                            // annotation needs to go in here
341
                                            // let dygraphs interpolate
342
                                            json[s].dps.splice(p,0,[annT,null]);
8✔
343
                                            // insert annotation
344
                                            annotations.push([json[s], json[s].annotations[a], isBaseline]);
8✔
345
                                            // next annotation
346
                                            a++;
8✔
347
                                        }
348
                                    }
349
                                }
350
                                if (dygraphOptions.globalAnnotations && globalAnnotations.length == 0 && json[s].globalAnnotations.length > 0) {
48✔
351
                                    globalAnnotations = json[s].globalAnnotations;
10✔
352
                                    globalAnnotations.sort(function(a,b){
10✔
353
                                        var ret = a.startTime - b.startTime;
10✔
354
                                        if (ret == 0) {
10!
355
                                            if (a.endTime == null || b.endTime == null) {
×
356
                                                return 0;
×
357
                                            }
358
                                            return a.endTime - b.endTime;
×
359
                                        }
360
                                        return ret;
10✔
361
                                    });
362
                                }
363
                            }
364
                            if (dygraphOptions.globalAnnotations && globalAnnotations.length > 0) {
30✔
365
                                for (var a=0; a<globalAnnotations.length; a++) {
10✔
366
                                    var annT = globalAnnotations[a].startTime;
20✔
367
                                    var hitAtTime = false;
20✔
368
                                    for (var s=0; s<json.length; s++) {
20✔
369
                                        while (indices[s] < json[s].dps.length && json[s].dps[indices[s]][0] < annT) {
40✔
370
                                            indices[s]++;
47✔
371
                                        }
372
                                        if (indices[s] < json[s].dps.length && json[s].dps[indices[s]][0] == annT) {
40✔
373
                                            hitAtTime = true;
10✔
374
                                        }
375
                                    }
376
                                    // now each index is either past the end of the points, or dps[index].time >= annotation.startTime
377
                                    var annotationAdded = false;
20✔
378
                                    for (var s=0; s<json.length; s++) {
20✔
379
                                        var p = indices[s];
40✔
380
                                        if (p < json[s].dps.length) {
40✔
381
                                            var t = json[s].dps[indices[s]][0];
32✔
382
                                            if (t == annT) {
32✔
383
                                                if (!annotationAdded) {
10✔
384
                                                    annotations.push([json[s], globalAnnotations[a], isBaseline]);
8✔
385
                                                    annotationAdded = true;
8✔
386
                                                }
387
                                            }
388
                                            else { // t > annT
389
                                                // ensure it gets added somewhere, so here is good enough
390
                                                if (!hitAtTime && !annotationAdded) {
22✔
391
                                                    annotations.push([json[s], globalAnnotations[a], isBaseline]);
8✔
392
                                                    // put in a null point here
393
                                                    json[s].dps.splice(p,0,[annT,null]);
8✔
394
                                                    annotationAdded = true;
8✔
395
                                                }
396
                                            }
397
                                        }
398
                                        // past end of dps
399
                                        else if (!annotationAdded) {
8✔
400
                                            annotations.push([json[s], globalAnnotations[a], isBaseline]);
4✔
401
                                            json[s].dps.push([annT, null]);
4✔
402
                                            annotationAdded = true;
4✔
403
                                        }
404
                                    }
405
                                }
406
                            }
407
                        }
408
                        if (dygraphOptions.annotations) {
80✔
409
                            discoverAnnotations(mainJson, mainIndices1, false);
20✔
410
                            if (baselining) {
20✔
411
                                discoverAnnotations(baselineJson, baselineIndices1, true);
10✔
412
                            }
413
                        }
414

415
                        var isY1Axis = function (axis) {
80✔
416
                            return axis==null || axis=="x1y1";
1,396✔
417
                        }
418

419
                        var isY2Axis = function (axis) {
80✔
420
                            return axis=="x1y2";
764✔
421
                        }
422

423
                        // 4. initial label setting (both sets) and axis allocation
424
                        var mainLabels = ["x"];
80✔
425
                        var baselineLabels = ["x"];
80✔
426
                        var seriesOptions = {};
80✔
427
                        for (var t=0; t<mainJson.length; t++) {
80✔
428
                            var name = graphServices.timeSeriesName(mainJson[t]);
146✔
429
                            mainLabels.push(name);
146✔
430
                            var axis = isY1Axis(mainJson[t].aardvark_query.graphOptions.axis) ? "y1" : "y2";
146✔
431
                            seriesOptions[name] = { axis: axis };
146✔
432
                            seriesOptions[name].strokeWidth = 0;
146✔
433
                            seriesOptions[name].drawPoints = false;
146✔
434
                            seriesOptions[name].pointSize = 2;
146✔
435
                            // must have one, if none then assume lines
436
                            if (!mainJson[t].aardvark_query.graphOptions.dygraph || mainJson[t].aardvark_query.graphOptions.dygraph.drawLines || !mainJson[t].aardvark_query.graphOptions.dygraph.drawPoints) {
146!
437
                                seriesOptions[name].strokeWidth = 1;
146✔
438
                            }
439
                            if (mainJson[t].aardvark_query.graphOptions.dygraph && mainJson[t].aardvark_query.graphOptions.dygraph.drawPoints) {
146!
440
                                seriesOptions[name].drawPoints = true;
×
441
                            }
442
                        }
443
                        if (baselining) {
80✔
444
                            for (var t=0; t<baselineJson.length; t++) {
22✔
445
                                var name = graphServices.timeSeriesName(baselineJson[t]) + "[BL]";
33✔
446
                                baselineLabels.push(name);
33✔
447
                                var axis = isY1Axis(baselineJson[t].aardvark_query.graphOptions.axis) ? "y1" : "y2";
33!
448
                                seriesOptions[name] = { axis: axis };
33✔
449
                                seriesOptions[name].strokeWidth = 0;
33✔
450
                                seriesOptions[name].drawPoints = false;
33✔
451
                                seriesOptions[name].pointSize = 2;
33✔
452
                                // must have one, if none then assume lines
453
                                if (!mainJson[t].aardvark_query.graphOptions.dygraph || baselineJson[t].aardvark_query.graphOptions.drawLines || !baselineJson[t].aardvark_query.graphOptions.drawPoints) {
33!
454
                                    seriesOptions[name].strokeWidth = 1;
33✔
455
                                }
456
                                if (baselineJson[t].aardvark_query.graphOptions.drawPoints) {
33!
457
                                    seriesOptions[name].drawPoints = true;
×
458
                                }
459
                            }
460
                        }
461

462
                        var isNegativeSquashingEnabled = function(json) {
80✔
463
                            return (isY1Axis(json.aardvark_query.graphOptions.axis) && dygraphOptions.y1SquashNegative)
855✔
464
                                || (isY2Axis(json.aardvark_query.graphOptions.axis) && dygraphOptions.y2SquashNegative);
465
                        }
466

467
                        var scaleMultiplierByMetricNameY1 = {}; // default 1
80✔
468
                        var scaleMultiplierByMetricNameY2 = {}; // default 1
80✔
469
                        var autoScale = function(axisMatchFn, perAxisScaleMultipliers) {
80✔
470
                            // 5. auto-scaling and label adjustment (both together)
471

472
                            var initScaleMultipliers = function(json) {
17✔
473
                                for (var s=0; s<json.length; s++) {
21✔
474
                                    if (axisMatchFn(json[s].aardvark_query.graphOptions.axis)) {
48✔
475
                                        perAxisScaleMultipliers[json[s].metric] = 1;
43✔
476
                                    }
477
                                }
478
                            }
479
                            initScaleMultipliers(mainJson);
17✔
480
                            if (baselining) {
17✔
481
                                initScaleMultipliers(baselineJson);
4✔
482
                            }
483

484
                            var maxValueByMetricName = {};
17✔
485

486
                            var calcMaxValues = function(json) {
17✔
487
                                for (var s=0; s<json.length; s++) {
21✔
488
                                    if (axisMatchFn(json[s].aardvark_query.graphOptions.axis)) {
48✔
489
                                        perAxisScaleMultipliers[json[s].metric] = 1;
43✔
490
                                        var max = 0;
43✔
491
                                        var min = Number.MAX_VALUE;
43✔
492
                                        for (var p=0; p<json[s].dps.length; p++) {
43✔
493
                                            max = Math.max(max, json[s].dps[p][1]);
213✔
494
                                            min = Math.min(min, json[s].dps[p][1]);
213✔
495
                                        }
496
                                        if (!isNegativeSquashingEnabled(json[s]) && min < 0) {
43✔
497
                                            max = Math.max(max, Math.abs(min));
8✔
498
                                        }
499
                                        if (maxValueByMetricName[json[s].metric] == null)
43✔
500
                                        {
501
                                            maxValueByMetricName[json[s].metric] = max;
34✔
502
                                        }
503
                                        else {
504
                                            maxValueByMetricName[json[s].metric] = Math.max(maxValueByMetricName[json[s].metric], max);
9✔
505
                                        }
506
                                    }
507
                                }
508
                            }
509

510
                            calcMaxValues(mainJson);
17✔
511
                            if (baselining) {
17✔
512
                                calcMaxValues(baselineJson);
4✔
513
                            }
514

515
                            var maxl = 0;
17✔
516
                            for (var metric in maxValueByMetricName) {
17✔
517
                                var logMax = parseInt(Math.log(maxValueByMetricName[metric])/Math.log(10));
34✔
518
                                if (logMax>maxl) {
34✔
519
                                    maxl = logMax;
21✔
520
                                }
521
                            }
522

523
                            for (var metric in maxValueByMetricName) {
17✔
524
                                var pow = parseInt(Math.log(maxValueByMetricName[metric])/Math.log(10));
34✔
525
                                var l = maxl - pow;
34✔
526
                                if (l > 0) {
34✔
527
                                    perAxisScaleMultipliers[metric] = Math.pow(10, l);
10✔
528
                                }
529
                            }
530

531
                            var updateScaleFactors = function(json, labels) {
17✔
532
                                for (var s=0;s<json.length;s++) {
21✔
533
                                    if (axisMatchFn(json[s].aardvark_query.graphOptions.axis)) {
48✔
534
                                        var scale = perAxisScaleMultipliers[json[s].metric];
43✔
535
                                        if (scale > 1) {
43✔
536
                                            var oldLabel = labels[s+1];
13✔
537
                                            var newLabel = scale+"x "+labels[s+1];
13✔
538
                                            labels[s+1] = newLabel;
13✔
539
                                            seriesOptions[newLabel] = seriesOptions[oldLabel];
13✔
540
                                            delete seriesOptions[oldLabel];
13✔
541
                                        }
542
                                    }
543
                                }
544
                            }
545
                            updateScaleFactors(mainJson, mainLabels);
17✔
546
                            if (baselining) {
17✔
547
                                updateScaleFactors(baselineJson, baselineLabels);
4✔
548
                            }
549
                        }
550

551
                        if (dygraphOptions.y1AutoScale) {
80✔
552
                            autoScale(isY1Axis, scaleMultiplierByMetricNameY1);
16✔
553
                        }
554

555
                        if (dygraphOptions.y2AutoScale) {
80✔
556
                            autoScale(isY2Axis, scaleMultiplierByMetricNameY2);
1✔
557
                        }
558

559
                        // 6. baseline time adjustment
560
                        if (baselining) {
80✔
561
                            var baselineOffset = graphServices.baselineOffset(global, datum).asMilliseconds();
22✔
562
                            for (var s=0; s<baselineJson.length; s++) {
22✔
563
                                for (var p=0; p<baselineJson[s].dps.length; p++) {
33✔
564
                                    baselineJson[s].dps[p][0] += baselineOffset;
157✔
565
                                }
566
                            }
567
                        }
568

569
                        // 7. min/max time calculations (together)
570
                        var minTime = new Date().getTime() + 86400000; // now + 1 day so we don't hit tz issues
80✔
571
                        var maxTime = 0
80✔
572
                        for (var s=0; s<mainJson.length; s++) {
80✔
573
                            if (mainJson[s].dps.length > 0) {
146!
574
                                minTime = Math.min(minTime, mainJson[s].dps[0][0]);
146✔
575
                                maxTime = Math.max(maxTime, mainJson[s].dps[mainJson[s].dps.length-1][0]);
146✔
576
                            }
577
                        }
578
                        if (baselineJson != null) {
80✔
579
                            for (var s=0; s<baselineJson.length; s++) {
23✔
580
                                if (baselineJson[s].dps.length > 0) {
33!
581
                                    minTime = Math.min(minTime, baselineJson[s].dps[0][0]);
33✔
582
                                    maxTime = Math.max(maxTime, baselineJson[s].dps[baselineJson[s].dps.length-1][0]);
33✔
583
                                }
584
                            }
585
                        }
586
                        if (maxTime == 0) {
80!
587
                            minTime == 0;
×
588
                        }
589

590
                        var ignoredOptions = [];
80✔
591
                        if (dygraphOptions.ratioGraph) {
80✔
592
                            if (dygraphOptions.meanAdjusted) {
7✔
593
                                ignoredOptions.push("mean adjustment");
2✔
594
                            }
595
                            if (dygraphOptions.y1AutoScale || dygraphOptions.y2AutoScale) {
7✔
596
                                ignoredOptions.push("auto scaling");
2✔
597
                            }
598
                        }
599
                        else if (dygraphOptions.meanAdjusted) {
73✔
600
                            if (dygraphOptions.y1AutoScale || dygraphOptions.y2AutoScale) {
5✔
601
                                ignoredOptions.push("auto scaling");
1✔
602
                            }
603
                        }
604
                        var ignoredBecause = null;
80✔
605

606
                        // 8. seperate processing
607
                        //  a. ratio graphs
608
                        //  b. mean adjustment
609
                        //  c. negative squashing
610
                        var seperateProcessing = function(json, indices) {
80✔
611
                            //                console.log("seperateProcessing:");
612
                            //                console.log("json = "+JSON.stringify(json));
613
                            for (var t=minTime; t<=maxTime; ) {
102✔
614
                                //                    console.log("t = "+t);
615
                                var nextTime = maxTime + 1; // break condition
496✔
616
                                var sum = 0; // for mean adjusted graphs
496✔
617
                                var hadValue = [];
496✔
618
                                for (var s=0; s<json.length; s++) {
496✔
619
                                    hadValue.push(false);
871✔
620
                                    if (indices[s] >= json[s].dps.length) {
871✔
621
                                        // skip this one
622
                                    }
623
                                    else if (json[s].dps[indices[s]][0] == t) {
864✔
624
                                        hadValue[s] = true;
812✔
625
                                        var val = json[s].dps[indices[s]][1];
812✔
626
                                        if (isNegativeSquashingEnabled(json[s]) && val < 0) {
812✔
627
                                            json[s].dps[indices[s]][1] = 0;
57✔
628
                                            val = 0;
57✔
629
                                        }
630
                                        indices[s]++;
812✔
631
                                        if (indices[s] < json[s].dps.length) {
812✔
632
                                            nextTime = Math.min(nextTime, json[s].dps[indices[s]][0]);
633✔
633
                                        }
634
                                        if (dygraphOptions.ratioGraph) {
812✔
635
                                            sum += Math.abs(val);
90✔
636
                                        }
637
                                        else if (dygraphOptions.meanAdjusted) {
722✔
638
                                            sum += val;
70✔
639
                                        }
640
                                    }
641
                                    else {
642
                                        nextTime = Math.min(nextTime, json[s].dps[indices[s]][0]);
52✔
643
                                    }
644
                                }
645
                                if (dygraphOptions.ratioGraph) {
496✔
646
                                    for (var s=0; s<json.length; s++) {
45✔
647
                                        if (hadValue[s] && json[s].dps[indices[s]-1][1]!=null && !isNaN(json[s].dps[indices[s]-1][1])) {
90!
648
                                            json[s].dps[indices[s]-1][1] = (json[s].dps[indices[s]-1][1] * 100) / sum;
90✔
649
                                        }
650
                                    }
651
                                    ignoredBecause = "ratio graphs";
45✔
652
                                }
653
                                else if (dygraphOptions.meanAdjusted) {
451✔
654
                                    var mean = sum / json.length;
35✔
655
                                    for (var s=0; s<json.length; s++) {
35✔
656
                                        //                            console.log("s = "+s);
657
                                        //                            console.log("indices[s] = "+indices[s]);
658
                                        if (hadValue[s] && json[s].dps[indices[s]-1][1]!=null && !isNaN(json[s].dps[indices[s]-1][1])) {
70!
659

660
                                            //                                console.log("val = "+json[s].dps[indices[s]-1][1]);
661
                                            //                                console.log("mean = "+mean);
662
                                            json[s].dps[indices[s]-1][1] -= mean;
70✔
663
                                        }
664
                                    }
665
                                    ignoredBecause = "mean adjustment";
35✔
666
                                }
667
                                else {
668
                                    if (dygraphOptions.y1AutoScale) {
416✔
669
                                        for (var s=0; s<json.length; s++) {
85✔
670
                                            if (isY1Axis(json[s].aardvark_query.graphOptions.axis)) {
190✔
671
                                                if (hadValue[s] && json[s].dps[indices[s]-1][1]!=null && !isNaN(json[s].dps[indices[s]-1][1])) {
175✔
672
                                                    json[s].dps[indices[s]-1][1] *= scaleMultiplierByMetricNameY1[json[s].metric];
173✔
673
                                                }
674
                                            }
675
                                        }
676
                                        ignoredBecause = "auto scaling";
85✔
677
                                    }
678
                                    if (dygraphOptions.y2AutoScale) {
416✔
679
                                        for (var s=0; s<json.length; s++) {
5✔
680
                                            if (isY2Axis(json[s].aardvark_query.graphOptions.axis)) {
20✔
681
                                                if (hadValue[s] && json[s].dps[indices[s]-1][1]!=null && !isNaN(json[s].dps[indices[s]-1][1])) {
10!
682
                                                    json[s].dps[indices[s]-1][1] *= scaleMultiplierByMetricNameY2[json[s].metric];
10✔
683
                                                }
684
                                            }
685
                                        }
686
                                        ignoredBecause = "auto scaling";
5✔
687
                                    }
688
                                }
689
                                t = nextTime;
496✔
690
                            }
691
                        }
692

693
                        seperateProcessing(mainJson, mainIndices2);
80✔
694
                        if (baselining) {
80✔
695
                            seperateProcessing(baselineJson, baselineIndices2);
22✔
696
                        }
697

698
                        // ie we had some clashes and some data..
699
                        if (ignoredBecause != null && ignoredOptions.length > 0) {
80✔
700
                            var buff = "";
4✔
701
                            var sep = "Ignored ";
4✔
702
                            for (var i=0; i<ignoredOptions.length; i++) {
4✔
703
                                buff += sep + ignoredOptions[i];
5✔
704
                                sep = " and ";
5✔
705
                            }
706
                            buff += " as not compatible with " + ignoredBecause;
4✔
707

708
                            renderContext.renderWarnings[graph.id] = buff;
4✔
709
                        }
710

711
                        // 9. gap filling / merge timeseries (together)
712
                        for (var t=minTime; t<=maxTime; ) {
80✔
713
                            var row = [new Date(t)];
392✔
714
                            var nextTime = maxTime + 1; // break condition
392✔
715
                            var gapFillAndMergeJson = function(json, indices) {
392✔
716
                                for (var s=0; s<json.length; s++) {
502✔
717
                                    // gap filling
718
                                    if (indices[s] >= json[s].dps.length) {
873✔
719
                                        row.push(null);
8✔
720
                                    }
721
                                    else if (json[s].dps[indices[s]][0] == t) {
865✔
722
                                        var val = json[s].dps[indices[s]][1];
812✔
723
                                        row.push(val);
812✔
724
                                        indices[s]++;
812✔
725
                                        if (indices[s] < json[s].dps.length) {
812✔
726
                                            nextTime = Math.min(nextTime, json[s].dps[indices[s]][0]);
633✔
727
                                        }
728
                                    }
729
                                    else {
730
                                        row.push(null);
53✔
731
                                        nextTime = Math.min(nextTime, json[s].dps[indices[s]][0]);
53✔
732
                                    }
733
                                }
734
                            }
735
                            gapFillAndMergeJson(mainJson, mainIndices3);
392✔
736
                            if (baselining) {
392✔
737
                                gapFillAndMergeJson(baselineJson, baselineIndices3);
110✔
738
                            }
739
                            graphData.push(row);
392✔
740
                            t = nextTime;
392✔
741
                        }
742

743
                        // 10. merge labels
744
                        var labels = mainLabels;
80✔
745
                        if (baselining) {
80✔
746
                            for (var s=1; s<baselineLabels.length; s++) {
22✔
747
                                labels.push(baselineLabels[s]);
33✔
748
                            }
749
                        }
750

751

752
                        var originalXRangeInDygraph;
80✔
753
                        var originalY1RangeInDygraph;
80✔
754
                        var originalY2RangeInDygraph;
80✔
755
                        var originalXRangeInGraph = {
80✔
756
                            absoluteTimeSpecification: global.absoluteTimeSpecification,
757
                            relativePeriod: global.relativePeriod,
758
                            fromDate: global.fromDate,
759
                            fromTime: global.fromTime,
760
                            toDate: global.toDate,
761
                            toTime: global.toTime                            
762
                        };
763
                        var originalY1RangeInGraph = graph.dygraph ? graph.dygraph.y1AxisRange : "";
80✔
764
                        var originalY2RangeInGraph = graph.dygraph ? graph.dygraph.y2AxisRange : "";
80✔
765
                        var drawCallback = function(dygraph, is_initial) {
80✔
766
                            if (is_initial) {
×
767
                                originalXRangeInDygraph = dygraph.xAxisRange();
×
768
                                originalY1RangeInDygraph = dygraph.yAxisRange(0);
×
769
                                originalY2RangeInDygraph = dygraph.yAxisRange(1);
×
770
                            }
771
                        }
772
                        var zoomCallback = function(minX, maxX, yRanges) {
80✔
773
                            var newXRange;
×
774
                            var newY1Range;
×
775
                            if (minX == originalXRangeInDygraph[0] && maxX == originalXRangeInDygraph[1]) {
×
776
                                newXRange = originalXRangeInGraph;
×
777
                            }
778
                            else {
779
                                var fromMoment = moment.utc(minX);
×
780
                                var toMoment = moment.utc(maxX);
×
781
                                newXRange = {
×
782
                                    absoluteTimeSpecification: true,
783
                                    relativePeriod: originalXRangeInGraph.relativePeriod,
784
                                    fromDate: fromMoment.format("YYYY/MM/DD"),
785
                                    fromTime: fromMoment.format("HH:mm:ss"),
786
                                    toDate: toMoment.format("YYYY/MM/DD"),
787
                                    toTime: toMoment.format("HH:mm:ss")
788
                                }
789
                            }
790
                            var y1Range = yRanges[0];
×
791
                            if (y1Range[0] == originalY1RangeInDygraph[0] && y1Range[1] == originalY1RangeInDygraph[1]) {
×
792
                                newY1Range = originalY1RangeInGraph;
×
793
                            }
794
                            else {
795
                                newY1Range = graphServices.dygraphAxisRangeToString(y1Range);
×
796
                            }
797
                            var graphUpdate = {y1AxisRange:newY1Range};
×
798
                            if (yRanges.length > 1 && yRanges[1] != null) {
×
799
                                var newY2Range;
×
800
                                var y2Range = yRanges[1];
×
801
                                if (y2Range[1] == originalY2RangeInDygraph[0] && y2Range[1] == originalY2RangeInDygraph[1]) {
×
802
                                    newY2Range = originalY2RangeInGraph;
×
803
                                }
804
                                else {
805
                                    newY2Range = graphServices.dygraphAxisRangeToString(y2Range);
×
806
                                }
807
                                graphUpdate.y2AxisRange = newY2Range;
×
808
                            }
809
//                            renderContext.updateGraphModel(null, {scatter:{xRange:newXRange,yRange:newYRange}}, true);
810
                            renderContext.updateGlobalModel(null, newXRange, true);
×
811
                            renderContext.updateGraphModel(null, {dygraph:graphUpdate}, true);
×
812
                        }
813
                        
814
                        // default to no-op
815
                        var dygraphPointClickHandler = null;
80✔
816
                        var dygraphAnnotationClickHandler = null;
80✔
817

818

819
                        var dygraphConfig = {
80✔
820
                            labels: labels,
821
                            width: width,
822
                            height: height,
823
                            legend: "always",
824
                            pointClickCallback: function(event, p) {
825
                                if (dygraphPointClickHandler != null) {
×
826
                                    dygraphPointClickHandler(event,p);
×
827
                                }
828
                            },
829
                            annotationClickHandler: function(ann, point, dg, event) {
830
                                if (dygraphAnnotationClickHandler != null) {
×
831
                                    dygraphAnnotationClickHandler(ann, point, dg, event);
×
832
                                }
833
                            },
834
                            stackedGraph: dygraphOptions.stackedLines,
835
                            connectSeparatedPoints: dygraphOptions.interpolateGaps,
836
                            drawGapEdgePoints: true,
837
                            axisLabelFontSize: 9,
838
                            labelsDivStyles: { fontSize: 9, textAlign: 'left', left: '100px', width: (width-100)+'px', "background-color": 'rgba(255,255,255,0)', "padding-top": '20px' },
839
                            labelsDivWidth: (width-100),
840
                            labelsSeparateLines: true,
841
                            series: seriesOptions,
842
                            zoomCallback: zoomCallback,
843
                            drawCallback: drawCallback,
844
                            highlightCircleSize: 4,
845
                            axes: {
846
                                y: {
847
                                    valueFormatter: function(y) {
848
                                        if (isNaN(y) || y < 1000) {
×
849
                                            return "" + y;
×
850
                                        }
851
                                        return y.toString().replace(/\B(?=(?:\d{3})+(?!\d))/g, ",")
×
852
                                    },
853
                                    axisLabelFormatter: function(y) {
854
                                        if (isNaN(y) || y < 1000) {
×
855
                                            return "" + Dygraph.round_(y, 3);
×
856
                                        }
857
                                        return y.toString().replace(/\B(?=(?:\d{3})+(?!\d))/g, ",")
×
858
                                    },
859
                                    valueRange: y1AxisRange,
860
                                    logscale: dygraphOptions.y1Log
861
                                },
862
                                y2: {
863
                                    valueFormatter: function(y) {
864
                                        if (isNaN(y) || y < 1000) {
×
865
                                            return "" + y;
×
866
                                        }
867
                                        return y.toString().replace(/\B(?=(?:\d{3})+(?!\d))/g, ",")
×
868
                                    },
869
                                    axisLabelFormatter: function(y) {
870
                                        if (isNaN(y) || y < 1000) {
×
871
                                            return "" + Dygraph.round_(y, 3);
×
872
                                        }
873
                                        return y.toString().replace(/\B(?=(?:\d{3})+(?!\d))/g, ",")
×
874
                                    },
875
                                    valueRange: y2AxisRange,
876
                                    logscale: dygraphOptions.y2Log
877

878
                                }
879
                            }
880
                        };
881
                        if (dygraphOptions.highlightLines) {
80!
882
                            dygraphConfig.highlightSeriesOpts = {
×
883
                                strokeWidth: 2,
884
                                strokeBorderWidth: 1,
885
                                highlightCircleSize: 6,
886
                                pointSize: 4
887
                            };
888
                            /*
889
                             dygraphConfig.highlightCallback = function(event, x, points, row, seriesName) {
890
                             if (labelsDiv) {
891
                             //find the y val
892
                             var yval = '';
893
                             for (var i=0;i<points.length;i++) {
894
                             if (points[i].name==seriesName) {
895
                             yval = points[i].yval;
896
                             break;
897
                             }
898
                             }
899
                             labelsDiv.innerHTML = "<span><b>" + seriesName + "</b>" + " "
900
                             + Dygraph.hmsString_(x) + ", " + yval + "</span>";
901
                             }
902
                             }*/
903

904
                        }
905

906
                        var dygraph = graphServices.dygraph_render("dygraphDiv_"+graph.id, graph.id, graphData, dygraphConfig);
80✔
907

908
                        var createDygraphAnnotation = function(g, seriesAndAnnotation) {
80✔
909
                            var series = seriesAndAnnotation[0];
40✔
910
                            var annotation = seriesAndAnnotation[1];
40✔
911
                            var icon = "unknown.jpg";
40✔
912
                            if (annotation.custom && annotation.custom.type) {
40✔
913
                                if (annotation.custom.type.toUpperCase() == "CONFIG") {
20✔
914
                                    icon = "config.jpg"
18✔
915
                                }
916
                                else if (annotation.custom.type.toUpperCase() == "DEPLOYMENT") {
2✔
917
                                    icon = "deployment.jpg"
1✔
918
                                }
919
                                else if (annotation.custom.type.toUpperCase() == "PROBLEM") {
1!
920
                                    icon = "problem.jpg"
1✔
921
                                }
922
                            }
923

924
                            var label = graphServices.timeSeriesName(series);
40✔
925
                            var scale = isY1Axis(seriesAndAnnotation[0].aardvark_query.graphOptions.axis) ? scaleMultiplierByMetricNameY1[seriesAndAnnotation[0].metric] : scaleMultiplierByMetricNameY1[seriesAndAnnotation[0].metric];
40!
926
                            if (scale > 1) {
40✔
927
                                label = scale+"x "+label;
5✔
928
                            }
929
                            var baseline = seriesAndAnnotation[2];
40✔
930
                            var offsetMs = 0;
40✔
931
                            if (baseline) {
40✔
932
                                label += "[BL]";
20✔
933
                                offsetMs = graphServices.baselineOffset(global, datum).asMilliseconds();
20✔
934
                            }
935

936
                            var ret = {
40✔
937
                                series: label,
938
                                xval: annotation.startTime + offsetMs,
939
                                height: 16,
940
                                width: 16,
941
                                icon: icon,
942
                                attachAtBottom: true,
943
                                tickHeight: g.height - 16,
944
                                text: annotation.description
945
                            };
946

947

948
                            return ret;
40✔
949
                        }
950

951
                        if (dygraphOptions.annotations) {
80✔
952
                            var syncDygraphWithAnnotations = function() {
20✔
953
                                var dygraphAnnotations = [];
20✔
954
                                for (var a=0; a<annotations.length; a++) {
20✔
955
                                    dygraphAnnotations.push(createDygraphAnnotation(dygraph, annotations[a]));
40✔
956
                                }
957
                                graphServices.dygraph_setAnnotations(dygraph, dygraphAnnotations);
20✔
958
                            }
959
                            syncDygraphWithAnnotations();
20✔
960

961
                            var showAnnotationDialog = function(annotationIndex, point) {
20✔
962
                                var adding = annotationIndex == -1;
×
963
                                var seriesAndQueries = {};
×
964
                                for (var i=0; i<mainJson.length; i++) {
×
965
                                    seriesAndQueries[graphServices.timeSeriesName(mainJson[i])] = mainJson[i].aardvark_query;
×
966
                                }
967
                                var modalInstance = $uibModal.open({
×
968
                                    animation: false,
969
                                    ariaLabelledBy: 'modal-title',
970
                                    ariaDescribedBy: 'modal-body',
971
                                    templateUrl: 'annotationsDialog.tmpl.html',
972
                                    controller: 'AnnotationsDialogCtrl',
973
                                    controllerAs: '$ctrl',
974
                                    size: 'lg',
975
                                    resolve: {
976
                                        adding: function() {
977
                                            return adding;
×
978
                                        },
979
                                        originalAnnotation: function() {
980
                                            return annotationIndex >= 0
×
981
                                                ? annotations[annotationIndex][1]
982
                                                : {
983
                                                startTime: point.xval
984
                                            };
985
                                        },
986
                                        readOnly: function() {
987
                                            return annotationIndex >= 0
×
988
                                                ? annotations[annotationIndex][2]
989
                                                : seriesAndQueries[point.name] == null;
990
                                        },
991
                                        time: function() {
992
                                            return point ? point.xval : 0;
×
993
                                        },
994
                                        rootConfig: function() {
995
                                            return config;
×
996
                                        },
997
                                        seriesAndQueries: function() {
998
                                            return seriesAndQueries;
×
999
                                        },
1000
                                        clickedSeries: function() {
1001
                                            return point ? point.name : null;
×
1002
                                        },
1003
                                        $tsdbClient: function() {
1004
                                            return tsdbClient;
×
1005
                                        },
1006
                                        $tsdbUtils: function() {
1007
                                            return tsdbUtils;
×
1008
                                        }
1009
                                    }
1010
                                });
1011
                                modalInstance.result.then(function (result) {
×
1012
                                    var action = result.action;
×
1013
                                    var selectedAnnotations = result.annotations;
×
1014
                                    if (action == "add") {
×
1015
                                        tsdbClient.bulkSaveAnnotations(selectedAnnotations, function() {
×
1016
                                            for (var a = 0; a<selectedAnnotations.length; a++) {
×
1017
                                                var tsuid = selectedAnnotations[a].tsuid;
×
1018
                                                for (var t=0; t<mainJson.length; t++) {
×
1019
                                                    if (mainJson[t].tsuids.indexOf(tsuid) >= 0) {
×
1020
                                                        // [json[s], json[s].annotations[a], isBaseline]
1021
                                                        annotations.push([mainJson[t],selectedAnnotations[a],false]);
×
1022
                                                        // don't break - same tsuid could be present in many visible timeseries
1023
                                                    }
1024
                                                }
1025
                                            }
1026
                                            syncDygraphWithAnnotations();
×
1027
                                        }, function() {
1028
                                            console.log("failed to add annotation(s)");
×
1029
                                            // todo: errors?
1030
                                        });
1031
                                    }
1032
                                    else if (action == "edit") {
×
1033
                                        // use singular endpoint
1034
                                        tsdbClient.saveAnnotation(selectedAnnotations[0], function() {
×
1035
                                            annotations[annotationIndex][1] = selectedAnnotations[0];
×
1036
                                            syncDygraphWithAnnotations();
×
1037
                                        }, function() {
1038
                                            console.log("failed to save annotation");
×
1039
                                            // todo: errors?
1040
                                        });
1041

1042
                                    }
1043
                                    else if (action == "delete") {
×
1044
                                        // use singular endpoint
1045
                                        tsdbClient.deleteAnnotation(selectedAnnotations[0], function() {
×
1046
                                            annotations.splice(annotationIndex, 1);
×
1047
                                            syncDygraphWithAnnotations();
×
1048
                                        }, function() {
1049
                                            console.log("failed to delete annotation");
×
1050
                                            // todo: errors?
1051
                                        });
1052
                                    }
1053
                                    else {
1054
                                        throw 'Unexpected annotation action: '+action;
×
1055
                                    }
1056
                                }, function () {
1057
                                    // do nothing
1058
                                });
1059
                            }
1060

1061
                            if (config.annotations.allowAddEdit) {
20!
1062
                                dygraphPointClickHandler = function(event, p) {
20✔
1063
                                    if ((!event.ctrlKey && !event.metaKey/*osx*/) || event.button != 0) {
×
1064
                                        return;
×
1065
                                    }
1066

1067
                                    showAnnotationDialog(-1, p);
×
1068
                                };
1069
                                dygraphAnnotationClickHandler = function(ann, point, dg, event) {
20✔
1070
                                    var anns = dygraph.annotations();
×
1071

1072
                                    var annIndex = -1;
×
1073
                                    for (var i=0; i<anns.length; i++) {
×
1074
                                        if (anns[i].xval == ann.xval) {
×
1075
                                            annIndex = i;
×
1076
                                        }
1077
                                    }
1078

1079
                                    if (annIndex == -1) {
×
1080
                                        return;
×
1081
                                    }
1082

1083
                                    showAnnotationDialog(annIndex, null);
×
1084
                                };
1085
                            }
1086
                        }
1087

1088
                        var yAxisParams = {
80✔
1089
                            range:dygraphOptions.y1AxisRange,
1090
                            squashNegative:dygraphOptions.y1SquashNegative,
1091
                            logscale:dygraphOptions.y1Log
1092
                        };
1093
                        var y2AxisParams = {
80✔
1094
                            range:dygraphOptions.y2AxisRange,
1095
                            squashNegative:dygraphOptions.y2SquashNegative,
1096
                            logscale:dygraphOptions.y2Log
1097
                        };
1098
                        ret.tsdb_export_link = graphServices.tsdbGraphUrl("/#", renderContext, config, global, graph, queries, /*forceAxis*/null, /*downsampleOverrideFn*/null, yAxisParams, y2AxisParams, /*keyParams*/{}, /*lineSmoothing*/false, /*style*/null, dygraphOptions.globalAnnotations);
80✔
1099
                        renderContext.renderMessages[graph.id] = "";
80✔
1100
                        renderContext.graphRendered(graph.id);
80✔
1101
                        return;
80✔
1102

1103
                    }
1104
                    
1105
                    var options = {
83✔
1106
                        supports_annotations: true,
1107
                        supports_baselining: true,
1108
                        require_arrays: true,
1109
                        annotations: dygraphOptions.annotations,
1110
                        globalAnnotations: dygraphOptions.globalAnnotations,
1111
                        processJson: processJson,
1112
                        errorResponse: function(json) {},
1113
                        downsampleOverrideFn: null
1114
                    };
1115
                    
1116
                    graphServices.perform_queries(renderContext, config, global, graph, queries, options, datum);
83✔
1117
                    
1118
                }
1119
                return ret;
87✔
1120
            }
1121
        };
1122
        return renderer;
87✔
1123
    }]);
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc