Home | History | Annotate | Download | only in js
      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 
     17 var 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