• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 *    Copyright 2015-2017 ARM Limited
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17var EventPlot = (function () {
18
19    /* EventPlot receives data that is hashed by the keys
20     * and each element in the data is sorted by start time.
21     * Since events on each lane are mutually exclusive, they
22     * they are also sorted by the end time. We use this information
23     * and binary search on the input data for filtering events
24     * This maintains filtering complexity to O[KLogN]
25     */
26
27    var GUIDER_WIDTH = 2;
28
29    infoProps = {
30        START_GUIDER_COLOR: "green",
31        END_GUIDER_COLOR: "red",
32        DELTA_COLOR: "blue",
33        GUIDER_WIDTH: 2,
34        TOP_MARGIN: 20,
35        HEIGHT: 30,
36        START_PREFIX: "A = ",
37        END_PREFIX: "B = ",
38        DELTA_PREFIX: "A - B = ",
39        XPAD: 10,
40        YPAD: 5,
41        BOX_BUFFER: 2,
42        BOX_WIDTH_RATIO: 0.6
43    }
44
45    var search_data = function (data, key, value, left, right) {
46
47        var mid;
48
49        while (left < right) {
50
51            mid = Math.floor((left + right) / 2)
52            if (data[mid][key] > value)
53                right = mid;
54            else
55                left = mid + 1;
56        }
57        return left;
58    }
59
60
61    /* Return the information for the current process
62     * pointed by the mouse
63     */
64    var getCurrentInfo = function(ePlot, x0, y0) {
65
66        for (name in ePlot.items) {
67
68            var data = ePlot.items[name];
69            var xMax = ePlot.zoomScale.domain()[1];
70            var right = search_data(data, 0, xMax, 0, data.length - 1);
71            var left = search_data(data, 1, x0, 0, right);
72
73            if (data) {
74                var candidate = data[left];
75                if (candidate[0] <= x0 &&
76                        candidate[1] >= x0 &&
77                        candidate[2] == y0)
78                    return {
79                            name: name,
80                            info: candidate
81                        };
82            }
83        }
84    }
85
86    var generate = function (div_name, base, chart_data) {
87
88        var margin, brush, x, ext, yMain, chart, main,
89            mainAxis,
90            itemRects, items, colourAxis, tip, lanes;
91
92        var process_chart_data = function (d) {
93            items = d.data;
94            lanes = d.lanes;
95            var names = d.keys;
96            var showSummary = d.showSummary;
97            var div = $("#" + div_name);
98
99            margin = {
100                    top: 15,
101                    right: 15,
102                    bottom: 15,
103                    left: 70
104                }, width = div.width() - margin.left - margin.right,
105
106                mainHeight = 50 * lanes.length - margin.top - margin.bottom;
107
108            x = d3.scale.linear()
109                .domain(d.xDomain)
110                .range([0, width]);
111
112            var zoomScale = d3.scale.linear()
113                .domain(d.xDomain)
114                .range([0, width]);
115
116            var xMin = x.domain()[0];
117            var xMax = x.domain()[1];
118
119            if (!d.colorMap) {
120                // Colour Ordinal scale. Uses Category20 Colors
121                colours = d3.scale.category20().range();
122            } else {
123                // Use colours provided by user
124                var colours = [];
125                for (var i in names)
126                    if (names[i] in d.colorMap)
127                        colours.push(d.colorMap[names[i]]);
128            }
129            colourAxis = d3.scale.ordinal()
130                .range(colours)
131                .domain(names);
132
133            brushScale = d3.scale.linear()
134                .range([0, width]);
135            ext = d3.extent(lanes, function (d) {
136                return d.id;
137            });
138            yMain = d3.scale.linear()
139                .domain([ext[0], ext[1] +
140                    1
141                ])
142                .range([0, mainHeight]);
143
144
145            var ePlot;
146
147
148            $("#" + div_name)
149                .append('<div class="pull-right">' +
150                    '<button type="button" class="btn btn-sm btn-info" ' +
151                    'onclick="EventPlot.create_help_dialog(' + base +
152                    ')">Help</button></div>')
153
154            var iDesc = drawInfo(div_name, margin, width);
155
156            chart = d3.select('#' + div_name)
157                .append('svg:svg')
158                .attr('width', width + margin.right +
159                    margin.left)
160                .attr('height', mainHeight + margin.top +
161                    margin.bottom + 5)
162                .attr('class', 'chart')
163
164
165            main = chart.append('g')
166                .attr('transform', 'translate(' + margin.left +
167                    ',' + margin.top + ')')
168                .attr('width', width)
169                .attr('height', mainHeight)
170                .attr('class', 'main')
171
172            main.append('g')
173                .selectAll('.laneLines')
174                .data(lanes)
175                .enter()
176                .append('line')
177                .attr('x1', 0)
178                .attr('y1', function (d) {
179                    return d3.round(yMain(d.id)) + 0.5;
180                })
181                .attr('x2', width)
182                .attr('y2', function (d) {
183                    return d3.round(yMain(d.id)) + 0.5;
184                })
185                .attr('stroke', function (d) {
186                    return d.label === '' ? 'white' :
187                        'lightgray'
188                });
189
190            main.append('g')
191                .selectAll('.laneText')
192                .data(lanes)
193                .enter()
194                .append('text')
195                .attr('x', 0)
196                .text(function (d) {
197                    return d.label;
198                })
199                .attr('y', function (d) {
200                    return yMain(d.id + .5);
201                })
202                .attr('dy', '0.5ex')
203                .attr('text-anchor', 'end')
204                .attr('class', 'laneText');
205
206            mainAxis = d3.svg.axis()
207                .scale(brushScale)
208                .orient('bottom');
209
210            tip = d3.tip()
211                .attr('class', 'd3-tip')
212                .html(function (d) {
213                    return "<span style='color:white'>" +
214                        d.name + "</span>";
215                })
216
217            main.append('g')
218                .attr('transform', 'translate(0,' +
219                    mainHeight + ')')
220                .attr('class', 'main axis')
221                .call(mainAxis);
222
223            var ePlot;
224
225            ePlot = {
226                div: div,
227                div_name: div_name,
228                margin: margin,
229                chart: chart,
230                mainHeight: mainHeight,
231                width: width,
232                x: x,
233                brushScale: brushScale,
234                ext: ext,
235                yMain: yMain,
236                main: main,
237                mainAxis: mainAxis,
238                items: items,
239                colourAxis: colourAxis,
240                tip: tip,
241                lanes: lanes,
242                names: names,
243                iDesc: iDesc,
244            };
245            ePlot.zoomScale = zoomScale;
246
247            if (showSummary)
248                drawMini(ePlot);
249
250            var outgoing;
251            var zoomed = function () {
252
253                if (zoomScale.domain()[0] < xMin) {
254                    zoom.translate([zoom.translate()[
255                            0] - zoomScale(
256                            xMin) +
257                        zoomScale.range()[0],
258                        zoom.translate()[
259                            1]
260                    ]);
261                } else if (zoomScale.domain()[1] >
262                    xMax) {
263                    zoom.translate([zoom.translate()[
264                            0] - zoomScale(
265                            xMax) +
266                        zoomScale.range()[1],
267                        zoom.translate()[
268                            1]
269                    ]);
270
271                }
272
273                outgoing = main.selectAll(".mItem")
274                    .attr("visibility", "hidden");
275                drawMain(ePlot, zoomScale.domain()[0],
276                    zoomScale.domain()[1]);
277                if (showSummary) {
278                    brush.extent(zoomScale.domain());
279                    ePlot.mini.select(".brush")
280                        .call(
281                            brush);
282                }
283
284                brushScale.domain(zoomScale.domain());
285                ePlot.main.select('.main.axis')
286                    .call(ePlot.mainAxis)
287
288                updateInfo(ePlot);
289            };
290
291            var rightClickCtrlAltHandler = function(x0, y0) {
292
293                x0 = ePlot.zoomScale.invert(x0);
294                y0 = Math.floor(ePlot.yMain.invert(y0));
295                var current = getCurrentInfo(ePlot, x0, y0);
296
297                if (current) {
298                    ePlot.iDesc.currentProc.text(current.name)
299                    ePlot.iDesc.currentInfo.text(
300                        current.info[0].toFixed(6)
301                        + " to " +
302                        current.info[1].toFixed(6) +
303                        " (" + (current.info[1] - current.info[0])
304                        .toFixed(6) + ")")
305
306                    removeContextRect(ePlot);
307                    ePlot.contextRect = drawContextRect(ePlot, current.info[0], current.info[1], current.info[2], true)
308                    ePlot.iDesc.currentDisp.attr("stroke", ePlot.colourAxis(current.name));
309                }
310            }
311
312            var contextMenuHandler = function() {
313
314                var e = d3.event;
315                var x0 = d3.mouse(this)[0] - ePlot.margin.left;
316                var y0 = d3.mouse(this)[1] - ePlot.margin.top;
317
318                if (e.ctrlKey && e.altKey)
319                    rightClickCtrlAltHandler(x0, y0);
320
321                else if (e.ctrlKey) {
322
323                    if (ePlot.endGuider)
324                        ePlot.endGuider = ePlot.endGuider.remove();
325
326                    ePlot.endGuider = drawVerticalLine(ePlot, x0,
327                        infoProps.END_GUIDER_COLOR, "B");
328                    ePlot.endGuider._x_pos = ePlot.zoomScale.invert(x0);
329                    iDesc.endText.text(infoProps.END_PREFIX + ePlot.endGuider._x_pos.toFixed(6))
330
331                } else {
332
333                    if (ePlot.startGuider)
334                        ePlot.startGuider = ePlot.startGuider.remove();
335
336                    ePlot.startGuider = drawVerticalLine(ePlot, x0,
337                        infoProps.START_GUIDER_COLOR, "A");
338                    ePlot.startGuider._x_pos = ePlot.zoomScale.invert(x0);
339                    iDesc.startText.text(infoProps.START_PREFIX + ePlot.startGuider._x_pos.toFixed(6))
340                }
341
342                if (ePlot.endGuider && ePlot.startGuider)
343                    iDesc.deltaText.text(infoProps.DELTA_PREFIX +
344                            (ePlot.endGuider._x_pos - ePlot.startGuider._x_pos)
345                            .toFixed(6)
346                        )
347
348                d3.event.preventDefault();
349            }
350
351            chart.on("contextmenu", contextMenuHandler);
352
353            if (showSummary) {
354                var _brushed_event = function () {
355                    main.selectAll("path")
356                        .remove();
357                    var brush_xmin = brush.extent()[0];
358                    var brush_xmax = brush.extent()[1];
359
360                    var t = zoom.translate(),
361                        new_domain = brush.extent(),
362                        scale;
363
364                    /*
365                     *    scale = x.range()[1] - x.range[0]
366                     *          --------------------------
367                     *          x(x.domain()[1] - x.domain()[0])
368                     *
369                     *                             _                                   _
370                     *  new_domain[0] =  x.invert | x.range()[0]  -   z.translate()[0]  |
371                     *                            |                 ------------------- |
372                     *                            |_                     z.scale()     _|
373                     *
374                     *
375                     *
376                     *  translate[0] = x.range()[0] - x(new_domain[0])) * zoom.scale()
377                     */
378
379                    scale = (width) / x(x.domain()[0] +
380                        new_domain[1] -
381                        new_domain[0]);
382                    zoom.scale(scale);
383                    t[0] = x.range()[0] - (x(new_domain[
384                        0]) * scale);
385                    zoom.translate(t);
386
387
388                    brushScale.domain(brush.extent())
389                    drawMain(ePlot, brush_xmin,
390                        brush_xmax);
391                    ePlot.main.select('.main.axis')
392                        .call(ePlot.mainAxis)
393
394                    updateInfo(ePlot);
395                };
396
397                brush = d3.svg.brush()
398                    .x(x)
399                    .extent(x.domain())
400                    .on("brush", _brushed_event);
401
402                ePlot.mini.append('g')
403                    .attr('class', 'brush')
404                    .call(brush)
405                    .selectAll('rect')
406                    .attr('y', 1)
407                    .attr('height', ePlot.miniHeight - 1);
408            }
409
410            var zoom = d3.behavior.zoom()
411                .x(zoomScale)
412                .on(
413                    "zoom", zoomed)
414                .on("zoomend", function () {
415                    if (outgoing)
416                        outgoing.remove()
417                })
418                .scaleExtent([1, 4096]);
419            chart.call(zoom);
420
421            drawMain(ePlot, xMin, xMax);
422            ePlot.main.select('.main.axis')
423                .call(ePlot.mainAxis)
424
425            var resize = function() {
426
427                var width = div.width() - margin.left
428                    - margin.right;
429
430                /* Update scale ranges */
431                x.range([0, width]);
432                zoomScale.range([0, width]);
433                brushScale.range([0, width]);
434                ePlot.width = width;
435
436                resize_main(ePlot);
437                resize_info(ePlot);
438                resize_mini(ePlot);
439                zoomed();
440
441            }
442
443            d3.select(window)
444                .on("resize." + ePlot.div_name, resize)
445
446            return ePlot;
447
448        }
449
450        /*
451         * If chart_data is passed, process data directly
452         */
453        process_chart_data(chart_data);
454    };
455
456
457    var resize_mini = function(ePlot) {
458
459        d3.select(ePlot.mini.node().parentNode)
460            .attr("width", ePlot.div.width());
461        ePlot.iDesc.info_svg
462            .attr("width", ePlot.div.width());
463        ePlot.mini.selectAll("line")
464            .attr("x2", ePlot.width);
465        ePlot.mini.call(ePlot.miniAxis);
466        ePlot.mini.selectAll(".miniItem").remove();
467        drawMiniPaths(ePlot);
468    }
469
470    var resize_main = function(ePlot) {
471
472        d3.select(ePlot.main.node().parentNode)
473            .attr("width", ePlot.div.width());
474        ePlot.main.selectAll("line")
475            .attr("x2", ePlot.width);
476    }
477
478    var resize_info = function(ePlot) {
479
480        var width_box_one = infoProps.BOX_WIDTH_RATIO * ePlot.width;
481        var width_box_two = ePlot.width - width_box_one;
482
483        ePlot.iDesc.info
484            .attr("width", width);
485        ePlot.iDesc.guiderInfo
486            .attr("width", width_box_one - infoProps.BOX_BUFFER);
487        ePlot.iDesc.currentDisp
488            .attr("x", width_box_one + infoProps.BOX_BUFFER);
489        ePlot.iDesc.currentDisp
490            .attr("width", width_box_two - infoProps.BOX_BUFFER);
491        ePlot.iDesc.deltaText
492            .attr("x", (width_box_one / 2) - infoProps.XPAD)
493        ePlot.iDesc.endText
494            .attr("x", width_box_one - infoProps.XPAD)
495        ePlot.iDesc.currentProc
496            .attr("x", width_box_one + infoProps.XPAD + infoProps.BOX_BUFFER)
497        ePlot.iDesc.currentInfo
498            .attr("x", ePlot.width - infoProps.XPAD)
499    }
500
501    var drawInfo = function (div_name, margin, width) {
502
503        var infoHeight = 30;
504        var _top = 20;
505        var LINE_WIDTH = 2
506
507        var iDesc = {};
508
509        var width_box_one = infoProps.BOX_WIDTH_RATIO * width;
510        var width_box_two = width - width_box_one
511
512        iDesc.info_svg = d3.select("#" + div_name)
513            .append(
514                "svg:svg")
515            .attr('width', width + margin.right +
516                margin.left)
517            .attr('height', infoHeight + infoProps.TOP_MARGIN + LINE_WIDTH)
518            .attr('class', 'info')
519
520        iDesc.info = iDesc.info_svg.append("g")
521            .attr("transform", "translate(" + margin.left +
522                 "," + infoProps.TOP_MARGIN + ")")
523            .attr('width', width)
524            .attr("class", "main")
525            .attr('height', infoProps.HEIGHT)
526
527        iDesc.guiderInfo = iDesc.info.append("rect")
528            .attr("x", 0)
529            .attr("y", 0)
530            .attr("width", width_box_one - infoProps.BOX_BUFFER)
531            .attr("height", infoHeight)
532            .attr("stroke", "lightgray")
533            .attr("fill", "none")
534            .attr("stroke-width", 1);
535
536        iDesc.currentDisp = iDesc.info.append("rect")
537            .attr("x", width_box_one + infoProps.BOX_BUFFER)
538            .attr("y", 0)
539            .attr("width", width_box_two - infoProps.BOX_BUFFER)
540            .attr("height", infoHeight)
541            .attr("stroke", "lightgray")
542            .attr("fill", "none")
543            .attr("stroke-width", 1);
544
545       iDesc.startText = iDesc.info.append("text")
546            .text("")
547            .attr("x", infoProps.XPAD)
548            .attr("y", infoProps.HEIGHT / 2 + infoProps.YPAD)
549            .attr("fill", infoProps.START_GUIDER_COLOR);
550
551       iDesc.deltaText = iDesc.info.append("text")
552            .text("")
553            .attr("x", (width_box_one / 2) - infoProps.XPAD)
554            .attr("y", infoProps.HEIGHT / 2 + infoProps.YPAD)
555            .attr("fill", infoProps.DELTA_COLOR);
556
557       iDesc.endText = iDesc.info.append("text")
558            .text("")
559            .attr("x", width_box_one - infoProps.XPAD)
560            .attr("text-anchor", "end")
561            .attr("y", infoProps.HEIGHT / 2 + infoProps.YPAD)
562            .attr("fill", infoProps.END_GUIDER_COLOR);
563
564        iDesc.currentProc = iDesc.info.append("text")
565            .text("")
566            .attr("x", width_box_one + infoProps.XPAD + infoProps.BOX_BUFFER)
567            .attr("text-anchor", "start")
568            .attr("y", infoProps.HEIGHT / 2 + infoProps.YPAD)
569
570        iDesc.currentInfo = iDesc.info.append("text")
571            .text("")
572            .attr("x", width - infoProps.XPAD)
573            .attr("text-anchor", "end")
574            .attr("y", infoProps.HEIGHT / 2 + infoProps.YPAD)
575
576        return iDesc;
577
578    }
579
580    var drawVerticalLine = function (ePlot, x, color, text) {
581
582        var line = ePlot.main.append("g")
583
584        line.append("line")
585            .style("stroke", color)
586            .style("stroke-width", GUIDER_WIDTH)
587            .attr("x1", x)
588            .attr("x2", x)
589            .attr("y1", 0)
590            .attr("y2", ePlot.mainHeight)
591
592        line.append("text")
593            .text(text)
594            .attr("y", -1)
595            .attr("x", x)
596            .attr("text-anchor", "middle")
597            .attr("fill", color)
598
599        return line;
600    };
601
602    var removeContextRect = function(ePlot) {
603        if (ePlot.contextRect && ePlot.contextRect.rect)
604            ePlot.contextRect.rect.remove();
605    }
606
607    var drawContextRect = function (ePlot, x0, x1, y, animate) {
608
609        var xMin = ePlot.zoomScale.domain()[0];
610        var xMax = ePlot.zoomScale.domain()[1];
611        var bounds = [Math.max(x0, xMin), Math.min(x1,
612                xMax)]
613
614        if (bounds[0] >= bounds[1])
615            return {
616                rect: false,
617                x0: x0,
618                x1: x1,
619                y: y,
620            }
621
622        var rect = ePlot.main.selectAll(".contextRect").data([""])
623
624        if (animate)
625            rect.enter().append("rect")
626                .attr("x", ePlot.zoomScale(bounds[0]))
627                .attr("y", ePlot.yMain(y))
628                .attr("height", ePlot.yMain(1))
629                .attr("class", "contextRect")
630                .attr("width", 0)
631                .transition()
632                .attr("width", ePlot.zoomScale(bounds[1]) - ePlot.zoomScale(bounds[0]))
633        else
634            rect.enter().append("rect")
635                .attr("x", ePlot.zoomScale(bounds[0]))
636                .attr("y", ePlot.yMain(y))
637                .attr("class", "contextRect")
638                .attr("height", ePlot.yMain(1))
639                .attr("width", ePlot.zoomScale(bounds[1]) - ePlot.zoomScale(bounds[0]))
640
641        return {
642                rect: rect,
643                x0: x0,
644                x1: x1,
645                y: y,
646        }
647    }
648
649    var checkGuiderRange = function (ePlot, xpos) {
650
651        if (xpos >= ePlot.zoomScale.domain()[0] &&
652            xpos <= ePlot.zoomScale.domain()[1])
653            return true;
654        else
655            return false;
656    }
657
658    var updateInfo = function (ePlot) {
659
660        if (ePlot.endGuider) {
661
662            var xpos = ePlot.endGuider._x_pos;
663            ePlot.endGuider.remove();
664
665            if (checkGuiderRange(ePlot, xpos)) {
666                ePlot.endGuider = drawVerticalLine(ePlot, ePlot.zoomScale(xpos),
667                    infoProps.END_GUIDER_COLOR, "B");
668                ePlot.endGuider._x_pos = xpos;
669            }
670        }
671
672        if (ePlot.startGuider) {
673
674            var xpos = ePlot.startGuider._x_pos;
675            ePlot.startGuider.remove();
676
677            if (checkGuiderRange(ePlot, xpos)) {
678                ePlot.startGuider = drawVerticalLine(ePlot, ePlot.zoomScale(xpos),
679                    infoProps.START_GUIDER_COLOR, "A");
680                ePlot.startGuider._x_pos = xpos
681            }
682        }
683
684        if (ePlot.contextRect) {
685            removeContextRect(ePlot);
686            ePlot.contextRect = drawContextRect(ePlot, ePlot.contextRect.x0,
687                                    ePlot.contextRect.x1,
688                                    ePlot.contextRect.y,
689                                    false);
690        }
691
692    }
693
694    var drawMiniPaths = function(ePlot) {
695
696        ePlot.mini.append('g')
697            .selectAll('miniItems')
698            .data(getPaths(ePlot, ePlot.x, ePlot.yMini))
699            .enter()
700            .append('path')
701            .attr('class', function (d) {
702                return 'miniItem'
703            })
704            .attr('d', function (d) {
705                return d.path;
706            })
707            .attr("stroke", function (d) {
708                return d.color
709            })
710            .attr("class", "miniItem");
711    }
712
713    var drawMini = function (ePlot) {
714
715        var miniHeight = ePlot.lanes.length * 12 + 50;
716
717        var miniAxis = d3.svg.axis()
718            .scale(ePlot.x)
719            .orient('bottom');
720
721        var yMini = d3.scale.linear()
722            .domain([ePlot.ext[0], ePlot.ext[1] +
723                1
724            ])
725            .range([0, miniHeight]);
726
727        ePlot.yMini = yMini;
728        ePlot.miniAxis = miniAxis;
729        ePlot.miniHeight = miniHeight;
730
731        var summary = d3.select("#" + ePlot.div_name)
732            .append(
733                "svg:svg")
734            .attr('width', ePlot.width + ePlot.margin.right +
735                ePlot.margin.left)
736            .attr('height', miniHeight + ePlot.margin.bottom +
737                ePlot.margin.top)
738            .attr('class', 'chart')
739
740        var mini = summary.append('g')
741            .attr("transform", "translate(" + ePlot.margin.left +
742                "," + ePlot.margin.top + ")")
743            .attr('width', ePlot.width)
744            .attr('height', ePlot.miniHeight)
745            .attr('class', 'mini');
746
747        mini.append('g')
748            .selectAll('.laneLines')
749            .data(ePlot.lanes)
750            .enter()
751            .append('line')
752            .attr('x1', 0)
753            .attr('y1', function (d) {
754                return d3.round(ePlot.yMini(d.id)) + 0.5;
755            })
756            .attr('x2', ePlot.width)
757            .attr('y2', function (d) {
758                return d3.round(ePlot.yMini(d.id)) + 0.5;
759            })
760            .attr('stroke', function (d) {
761                return d.label === '' ? 'white' :
762                    'lightgray'
763            });
764
765        mini.append('g')
766            .attr('transform', 'translate(0,' +
767                ePlot.miniHeight + ')')
768            .attr('class', 'axis')
769            .call(ePlot.miniAxis);
770
771        ePlot.mini = mini
772        drawMiniPaths(ePlot);
773
774        mini.append('g')
775            .selectAll('.laneText')
776            .data(ePlot.lanes)
777            .enter()
778            .append('text')
779            .text(function (d) {
780                return d.label;
781            })
782            .attr('x', -10)
783            .attr('y', function (d) {
784                return ePlot.yMini(d.id + .5);
785            })
786            .attr('dy', '0.5ex')
787            .attr('text-anchor', 'end')
788            .attr('class', 'laneText');
789
790        return mini;
791    };
792
793
794    var drawMain = function (ePlot, xMin, xMax) {
795
796        var rects, labels;
797        var dMin = 10000;
798        var paths = getPaths(ePlot, ePlot.zoomScale, ePlot.yMain);
799        ePlot.brushScale.domain([xMin, xMax]);
800
801        if (paths.length == 0)
802            return;
803
804        ePlot.main
805            .selectAll('mainItems')
806            .data(paths)
807            .enter()
808            .append('path')
809            .attr("shape-rendering", "crispEdges")
810            .attr('d', function (d) {
811                return d.path;
812            })
813            .attr("class", "mItem")
814            .attr("stroke-width", function(d) {
815               return  0.8 * ePlot.yMain(1);
816            })
817            .attr("stroke", function (d) {
818                return d.color
819            })
820            .call(ePlot.tip)
821            .on("mouseover", ePlot.tip.show)
822            .on('mouseout', ePlot.tip.hide)
823            .on('mousemove', function () {
824                var xDisp = parseFloat(ePlot.tip.style("width")) /
825                    2.0
826                ePlot.tip.style("left", (d3.event.pageX - xDisp) +
827                        "px")
828                    .style("top", Math.max(0, d3.event.pageY -
829                        47) + "px");
830            })
831    };
832
833
834   function  _handle_equality(d, xMin, xMax, x, y, lane) {
835        var offset = 0.5 * y(1) + 0.5
836        var bounds = [Math.max(d[0], xMin), Math.min(d[1],
837            xMax)]
838        if (bounds[0] < bounds[1])
839            return 'M' + ' ' + x(bounds[0]) + ' ' + (y(lane) + offset) + ' H ' +  x(bounds[1]);
840        else
841            return '';
842    };
843
844    function _process(path, d, xMin, xMax, x, y, offset, lane) {
845
846        var start = d[0];
847        if (start < xMin)
848            start = xMin;
849        var end = d[1];
850        if (end > xMax)
851            end = xMax;
852
853        start = x(start);
854        end = x(end);
855
856        if ((end - start) < 0.01)
857            return path;
858        else if ((end - start) < 1)
859            end = start + 1;
860
861        path += 'M' + ' ' + start + ' ' + (y(lane) + offset) + ' H ' +  end;
862        return path;
863    }
864
865    var _get_path = function(new_data, xMin, xMax, offset, x, y, stride) {
866
867            var path = ''
868            var max_rects = 2000;
869
870            for (var lane in new_data) {
871                var data = new_data[lane];
872                var right = search_data(data, 0, xMax, 0, data.length - 1)
873                var left = search_data(data, 1, xMin, 0, right)
874
875                //Handle Equality
876                if (left == right)
877                    path += _handle_equality(data[left], xMin, xMax, x, y, lane);
878
879                data = data.slice(left, right + 1);
880
881                var stride_length = 1;
882                if (stride)
883                    stride_length = Math.max(Math.ceil(data.length / max_rects), 1);
884
885                for (var i = 0; i < data.length; i+= stride_length)
886                    path = _process(path, data[i], xMin, xMax, x, y, offset, lane);
887        }
888
889        return path;
890    }
891
892    var getPaths = function (ePlot, x, y, stride) {
893
894        var keys = ePlot.names;
895        var items = ePlot.items;
896        var colourAxis = ePlot.colourAxis;
897
898        var xMin = x.domain()[0];
899        var xMax = x.domain()[1];
900        var paths = {},
901            d, offset = 0.5 * y(1) + 0.5,
902            result = [];
903
904        for (var i in keys) {
905            var name = keys[i];
906            var path = _get_path(items[name], xMin, xMax, offset, x, y, stride)
907            /* This is critical. Adding paths for non
908             * existent processes in the window* can be
909             * very expensive as there is one SVG per process
910             * and SVG rendering is expensive
911             */
912            if (!path || path == "")
913                continue
914
915            result.push({
916                color: colourAxis(name),
917                path: path,
918                name: name
919            });
920        }
921
922        return result;
923
924    }
925
926    var create_dialog_body = function (body, title) {
927
928        var element = $("<div/>")
929            .addClass("modal fade")
930            .attr("role", "dialog")
931            .attr("tabindex", -1)
932
933        element.append(
934            $("<div/>")
935            .addClass("modal-dialog")
936            .attr("role", "document")
937            .append(
938                $("<div/>")
939                .addClass("modal-content")
940                .append(
941                    $("<div/>")
942                    .addClass("modal-header")
943                    .append(
944                        $("<button>")
945                        .addClass("close")
946                        .attr("data-dismiss",
947                            "modal")
948                        .append($("<span/>")
949                            .html("&times;"))
950                    )
951                    .append($("<h4/>")
952                        .addClass("modal-title")
953                        .text(title)
954                    )
955                    .append($("<div/>")
956                        .addClass("modal-body")
957                        .append(body)
958                    )
959                    .append(
960                        $("<div/>")
961                        .addClass("modal-footer")
962                        .append(
963                            $("<button>")
964                            .addClass("btn btn-default")
965                            .attr("data-dismiss", "modal")
966                            .text("Close")
967                        )
968                    )
969                )
970            )
971        )
972
973        return element.modal();
974
975    }
976
977    var create_help_dialog = function (base) {
978
979        var HELP_IMAGE = "plotter_scripts/EventPlot/EventPlot_help.jpg"
980
981        var element = $('<div/>');
982
983        // The documentation
984        var doc = $('<div/>')
985            .addClass('alert alert-info');
986
987        doc.append(
988            'EventPlot is a multi-lane timeline plot ' +
989            'which supports interative zooming and timing calculation'
990        );
991
992        element.append(doc);
993
994        var zoom = $("<div/>")
995            .addClass("media-left");
996
997        var addLabel = function (txt, cls) {
998            return '<span class="label label-' + cls + '" + ">' +
999                txt + '</span>'
1000        }
1001
1002        var addListItem = function (txt) {
1003            return '<li class="list-group-item">' + txt +
1004                '</li>'
1005        }
1006
1007        var addPlus = function () {
1008            return " + "
1009        }
1010
1011        var addBadge = function (txt) {
1012            return '<span class="label label-default" style="border-radius: 10px">' +
1013                txt + '</span>'
1014        }
1015
1016        element.append(
1017            '<img style="width: 100%;" class="media-object" src="' + base +
1018            HELP_IMAGE + '"/>'
1019        )
1020
1021        element.append('<ul class="list-group">')
1022        element.append(addListItem('Scroll in the main area ' +
1023            addBadge("1") + " to zoom interactively"))
1024        element.append(addListItem(
1025            'Click and drag in the main area ' + addBadge(
1026                "1") + " to pan the zoom"))
1027        element.append(addListItem(
1028            'The summary of the plot is shown in ' +
1029            addBadge("2")))
1030        element.append(addListItem('Adjust the size of window ' +
1031            addBadge("4") +
1032            " set the X-Limits of the chart"))
1033
1034        element.append(addListItem(addLabel("Right-Click",
1035                "default") +
1036            " to place marker " + addLabel("A", "success")))
1037
1038        element.append(addListItem(addLabel("Ctrl", "primary") +
1039            " + " + addLabel("Right-Click", "default") +
1040            " to place marker " + addLabel("B", "danger")))
1041
1042        element.append(addListItem(
1043            "The marker positions and delta will be shown in " +
1044            addBadge("3")))
1045
1046        element.append(
1047            addListItem(addLabel("Ctrl", "primary") + addPlus() +
1048                addLabel("Alt", "primary") + addPlus() +
1049                addLabel("Right-Click", "default") +
1050                " on the rectange (eg. " + addBadge("6") +
1051                " ) to show info in " + addBadge("5")))
1052
1053        element.append('</ul>')
1054
1055        var dialog = create_dialog_body(element, "Help: EventPlot");
1056        dialog.show();
1057
1058    }
1059
1060    return {
1061        generate: generate,
1062        create_help_dialog: create_help_dialog
1063    };
1064
1065}());
1066