Project

General

Profile

« Previous | Next » 

Revision d2a91e8a

Added by Marcos M over 1 year ago

Update nvd3. Implement #13537

View differences:

src/usr/local/www/vendor/nvd3/nv.d3.css
1
/* nvd3 version 1.8.3 (https://github.com/novus/nvd3) 2016-04-26 */
1
/* nvd3 version 1.8.6 (https://github.com/novus/nvd3) 2017-08-23 */
2 2
.nvd3 .nv-axis {
3 3
    pointer-events:none;
4 4
    opacity: 1;
......
37 37
.nvd3 .x  .nv-axis .nv-axisMaxMin text,
38 38
.nvd3 .x2 .nv-axis .nv-axisMaxMin text,
39 39
.nvd3 .x3 .nv-axis .nv-axisMaxMin text {
40
    text-anchor: middle
40
    text-anchor: middle;
41 41
}
42 42

  
43 43
.nvd3 .nv-axis.nv-disabled {
......
48 48
    fill-opacity: .75;
49 49

  
50 50
    transition: fill-opacity 250ms linear;
51
    -moz-transition: fill-opacity 250ms linear;
52
    -webkit-transition: fill-opacity 250ms linear;
53 51
}
54 52

  
55 53
.nvd3 .nv-bars rect.hover {
......
74 72
    stroke-opacity: 0;
75 73

  
76 74
    transition: fill-opacity 250ms linear;
77
    -moz-transition: fill-opacity 250ms linear;
78
    -webkit-transition: fill-opacity 250ms linear;
79 75
}
80 76

  
81 77
.nvd3 .nv-multibar .nv-groups rect:hover,
......
118 114
.nvd3.nv-bullet .nv-measure:hover { fill-opacity: 1; }
119 115
.nvd3.nv-bullet .nv-marker { stroke: #000; stroke-width: 2px; }
120 116
.nvd3.nv-bullet .nv-markerTriangle { stroke: #000; fill: #fff; stroke-width: 1.5px; }
117
.nvd3.nv-bullet .nv-markerLine { stroke: #000; stroke-width: 1.5px; }
121 118
.nvd3.nv-bullet .nv-tick line { stroke: #666; stroke-width: .5px; }
122 119
.nvd3.nv-bullet .nv-range.nv-s0 { fill: #eee; }
123 120
.nvd3.nv-bullet .nv-range.nv-s1 { fill: #ddd; }
......
125 122
.nvd3.nv-bullet .nv-title { font-size: 14px; font-weight: bold; }
126 123
.nvd3.nv-bullet .nv-subtitle { fill: #999; }
127 124

  
128

  
129 125
.nvd3.nv-bullet .nv-range {
130 126
    fill: #bababa;
131 127
    fill-opacity: .4;
132 128
}
129

  
133 130
.nvd3.nv-bullet .nv-range:hover {
134 131
    fill-opacity: .7;
135 132
}
......
154 151

  
155 152
.with-transitions .nv-candlestickBar .nv-ticks .nv-tick {
156 153
    transition: stroke-width 250ms linear, stroke-opacity 250ms linear;
157
    -moz-transition: stroke-width 250ms linear, stroke-opacity 250ms linear;
158
    -webkit-transition: stroke-width 250ms linear, stroke-opacity 250ms linear;
159

  
160 154
}
161 155

  
162 156
.nvd3.nv-candlestickBar .nv-ticks line {
163 157
    stroke: #333;
164 158
}
165 159

  
166

  
167 160
.nv-force-node {
168
  stroke: #fff;
169
  stroke-width: 1.5px;
161
    stroke: #fff;
162
    stroke-width: 1.5px;
170 163
}
164

  
171 165
.nv-force-link {
172
  stroke: #999;
173
  stroke-opacity: .6;
166
    stroke: #999;
167
    stroke-opacity: .6;
174 168
}
169

  
175 170
.nv-force-node text {
176
  stroke-width: 0px
171
    stroke-width: 0px;
177 172
}
178 173

  
179 174
.nvd3 .nv-legend .nv-disabled rect {
......
228 223

  
229 224
.with-transitions .nvd3 .nv-groups .nv-point {
230 225
    transition: stroke-width 250ms linear, stroke-opacity 250ms linear;
231
    -moz-transition: stroke-width 250ms linear, stroke-opacity 250ms linear;
232
    -webkit-transition: stroke-width 250ms linear, stroke-opacity 250ms linear;
233

  
234 226
}
235 227

  
236 228
.nvd3.nv-scatter .nv-groups .nv-point.hover,
......
249 241
}
250 242

  
251 243

  
252

  
253 244
.nvd3 .nv-indexLine {
254 245
    cursor: ew-resize;
255 246
}
......
262 253
  Default CSS for an svg element nvd3 used
263 254
*/
264 255
svg.nvd3-svg {
265
    -webkit-touch-callout: none;
266 256
    -webkit-user-select: none;
267
    -khtml-user-select: none;
268
    -ms-user-select: none;
269
    -moz-user-select: none;
270
    user-select: none;
257
       -moz-user-select: none;
258
        -ms-user-select: none;
259
            user-select: none;
271 260
    display: block;
272 261
    width:100%;
273 262
    height:100%;
......
277 266
  Box shadow and border radius styling
278 267
*/
279 268
.nvtooltip.with-3d-shadow, .with-3d-shadow .nvtooltip {
280
    -moz-box-shadow: 0 5px 10px rgba(0,0,0,.2);
281
    -webkit-box-shadow: 0 5px 10px rgba(0,0,0,.2);
282 269
    box-shadow: 0 5px 10px rgba(0,0,0,.2);
283

  
284
    -webkit-border-radius: 5px;
285
    -moz-border-radius: 5px;
286 270
    border-radius: 5px;
287 271
}
288 272

  
289 273

  
290 274
.nvd3 text {
291
    font: normal 12px Arial;
275
    font: normal 12px Arial, sans-serif;
292 276
}
293 277

  
294 278
.nvd3 .title {
295
    font: bold 14px Arial;
279
    font: bold 14px Arial, sans-serif;
296 280
}
297 281

  
298 282
.nvd3 .nv-background {
......
350 334
*/
351 335

  
352 336
@media print {
353
  .nvd3 text {
354
    stroke-width: 0;
355
    fill-opacity: 1;
356
  }
337
    .nvd3 text {
338
        stroke-width: 0;
339
        fill-opacity: 1;
340
    }
357 341
}
358 342

  
359 343
.nvd3.nv-ohlcBar .nv-ticks .nv-tick {
......
385 369
    stroke-opacity: .7;
386 370
}
387 371

  
388
.nvd3 .nv-parallelCoordinates-brush .extent 
389
{
372
.nvd3 .nv-parallelCoordinates-brush .extent {
390 373
    fill: #fff;
391 374
    fill-opacity: .6;
392 375
    stroke: gray;
......
395 378

  
396 379
.nvd3 .nv-parallelCoordinates .hover  {
397 380
    fill-opacity: 1;
398
    stroke-width: 3px;
381
	stroke-width: 3px;
399 382
}
400 383

  
401 384

  
......
404 387
  stroke: black;
405 388
  stroke-width: 1;
406 389
  stroke-opacity: 1;
407
  stroke-dasharray: 5, 5; 
390
  stroke-dasharray: 5, 5;
408 391
}
392

  
409 393
.nvd3.nv-pie path {
410 394
    stroke-opacity: 0;
411 395
    transition: fill-opacity 250ms linear, stroke-width 250ms linear, stroke-opacity 250ms linear;
412
    -moz-transition: fill-opacity 250ms linear, stroke-width 250ms linear, stroke-opacity 250ms linear;
413
    -webkit-transition: fill-opacity 250ms linear, stroke-width 250ms linear, stroke-opacity 250ms linear;
414

  
415 396
}
416 397

  
417 398
.nvd3.nv-pie .nv-pie-title {
......
433 414
.nvd3.nv-pie path {
434 415
    fill-opacity: .7;
435 416
}
417

  
436 418
.nvd3.nv-pie .hover path {
437 419
    fill-opacity: 1;
438 420
}
421

  
439 422
.nvd3.nv-pie .nv-label {
440 423
    pointer-events: none;
441 424
}
425

  
442 426
.nvd3.nv-pie .nv-label rect {
443 427
    fill-opacity: 0;
444 428
    stroke-opacity: 0;
......
453 437
.nvd3 .nv-scatter .nv-point.hover {
454 438
    fill-opacity: 1;
455 439
}
440

  
456 441
.nv-noninteractive {
457 442
    pointer-events: none;
458 443
}
......
515 500
    fill-opacity: .7;
516 501
    stroke-opacity: 0;
517 502
    transition: fill-opacity 250ms linear, stroke-opacity 250ms linear;
518
    -moz-transition: fill-opacity 250ms linear, stroke-opacity 250ms linear;
519
    -webkit-transition: fill-opacity 250ms linear, stroke-opacity 250ms linear;
520 503
}
521 504

  
522 505
.nvd3.nv-stackedarea path.nv-area.hover {
......
529 512
    fill-opacity: 0;
530 513
}
531 514

  
532

  
533 515
.nvtooltip {
534 516
    position: absolute;
535 517
    background-color: rgba(255,255,255,1.0);
......
539 521
    z-index: 10000;
540 522
    display: block;
541 523

  
542
    font-family: Arial;
524
    font-family: Arial, sans-serif;
543 525
    font-size: 13px;
544 526
    text-align: left;
545 527
    pointer-events: none;
546 528

  
547 529
    white-space: nowrap;
548 530

  
549
    -webkit-touch-callout: none;
550 531
    -webkit-user-select: none;
551
    -khtml-user-select: none;
552
    -moz-user-select: none;
553
    -ms-user-select: none;
554
    user-select: none;
532

  
533
       -moz-user-select: none;
534

  
535
        -ms-user-select: none;
536

  
537
            user-select: none;
555 538
}
556 539

  
557 540
.nvtooltip {
......
565 548
*/
566 549
.nvtooltip.with-transitions, .with-transitions .nvtooltip {
567 550
    transition: opacity 50ms linear;
568
    -moz-transition: opacity 50ms linear;
569
    -webkit-transition: opacity 50ms linear;
570 551

  
571 552
    transition-delay: 200ms;
572
    -moz-transition-delay: 200ms;
573
    -webkit-transition-delay: 200ms;
574 553
}
575 554

  
576 555
.nvtooltip.x-nvtooltip,
......
589 568

  
590 569
    border-bottom: 1px solid #ebebeb;
591 570

  
592
    -webkit-border-radius: 5px 5px 0 0;
593
    -moz-border-radius: 5px 5px 0 0;
594 571
    border-radius: 5px 5px 0 0;
595 572
}
596 573

  
......
619 596
.nvtooltip table td.key {
620 597
    font-weight: normal;
621 598
}
599

  
622 600
.nvtooltip table td.key.total {
623 601
    font-weight: bold;
624 602
}
603

  
625 604
.nvtooltip table td.value {
626 605
    text-align: right;
627 606
    font-weight: bold;
628 607
}
629 608

  
609
.nvtooltip table td.percent {
610
    color: darkgray;
611
}
612

  
630 613
.nvtooltip table tr.highlight td {
631 614
    padding: 1px 9px 1px 0;
632 615
    border-bottom-style: solid;
......
664 647
.nvd3 .nv-interactiveGuideLine {
665 648
    pointer-events:none;
666 649
}
650

  
667 651
.nvd3 line.nv-guideline {
668 652
    stroke: #ccc;
669 653
}
src/usr/local/www/vendor/nvd3/nv.d3.js
1
/* nvd3 version 1.8.3 (https://github.com/novus/nvd3) 2016-04-26 */
1
/* nvd3 version 1.8.6 (https://github.com/novus/nvd3) 2017-08-23 */
2 2
(function(){
3 3

  
4 4
// set up main nv object
......
161 161
 * implementations in the future.
162 162
 */
163 163
nv.dom.write = function(callback) {
164
    if (window.fastdom !== undefined) {
165
        return fastdom.mutate(callback);
166
    }
167
    return callback();
164
	if (window.fastdom !== undefined) {
165
		return fastdom.mutate(callback);
166
	}
167
	return callback();
168 168
};
169 169

  
170 170
/* Facade for queueing DOM read operations
......
174 174
 * implementations in the future.
175 175
 */
176 176
nv.dom.read = function(callback) {
177
    if (window.fastdom !== undefined) {
178
        return fastdom.measure(callback);
179
    }
180
    return callback();
177
	if (window.fastdom !== undefined) {
178
		return fastdom.measure(callback);
179
	}
180
	return callback();
181 181
};
182 182
/* Utility class to handle creation of an interactive layer.
183 183
 This places a rectangle on top of the chart. When you mouse move over it, it sends a dispatch
......
198 198
        ,   showGuideLine = true
199 199
        ,   svgContainer = null // Must pass the chart's svg, we'll use its mousemove event.
200 200
        ,   tooltip = nv.models.tooltip()
201
        ,   isMSIE = "ActiveXObject" in window // Checkt if IE by looking for activeX.
201
        ,   isMSIE =  window.ActiveXObject// Checkt if IE by looking for activeX. (excludes IE11)
202 202
    ;
203 203

  
204 204
    tooltip
......
221 221
            }
222 222

  
223 223
            function mouseHandler() {
224
                var d3mouse = d3.mouse(this);
225
                var mouseX = d3mouse[0];
226
                var mouseY = d3mouse[1];
224
                var mouseX = d3.event.clientX - this.getBoundingClientRect().left;
225
                var mouseY = d3.event.clientY - this.getBoundingClientRect().top;
226

  
227 227
                var subtractMargin = true;
228 228
                var mouseOutAnyReason = false;
229 229
                if (isMSIE) {
......
342 342

  
343 343
                // if user presses mouse down the layer, fire elementMouseDown
344 344
                if (d3.event.type === 'mousedown') {
345
                    dispatch.elementMouseDown({
346
                        mouseX: mouseX,
347
                        mouseY: mouseY,
348
                        pointXValue: pointXValue
349
                    });
345
                	dispatch.elementMouseDown({
346
                		mouseX: mouseX,
347
                		mouseY: mouseY,
348
                		pointXValue: pointXValue
349
                	});
350 350
                }
351 351

  
352 352
                // if user presses mouse down the layer, fire elementMouseUp
353 353
                if (d3.event.type === 'mouseup') {
354
                    dispatch.elementMouseUp({
355
                        mouseX: mouseX,
356
                        mouseY: mouseY,
357
                        pointXValue: pointXValue
358
                    });
354
                	dispatch.elementMouseUp({
355
                		mouseX: mouseX,
356
                		mouseY: mouseY,
357
                		pointXValue: pointXValue
358
                	});
359 359
                }
360 360
            }
361 361

  
......
545 545
        ,   distance = 25 // Distance to offset tooltip from the mouse location.
546 546
        ,   snapDistance = 0   // Tolerance allowed before tooltip is moved from its current position (creates 'snapping' effect)
547 547
        ,   classes = null  // Attaches additional CSS classes to the tooltip DIV that is created.
548
        ,   chartContainer = null // Parent dom element of the SVG that holds the chart.
549 548
        ,   hidden = true  // Start off hidden, toggle with hide/show functions below.
550 549
        ,   hideDelay = 200  // Delay (in ms) before the tooltip hides after calling hide().
551 550
        ,   tooltip = null // d3 select of the tooltip div.
......
556 555
        ,   nvPointerEventsClass = "nv-pointer-events-none" // CSS class to specify whether element should not have mouse events.
557 556
    ;
558 557

  
559
    /*
560
     Function that returns the position (relative to the viewport) the tooltip should be placed in.
561
     Should return: {
562
        left: <leftPos>,
563
        top: <topPos>
564
     }
565
     */
566
    var position = function() {
567
        return {
568
            left: d3.event !== null ? d3.event.clientX : 0,
569
            top: d3.event !== null ? d3.event.clientY : 0
570
        };
571
    };
572

  
573 558
    // Format function for the tooltip values column.
574
    var valueFormatter = function(d, i) {
559
    // d is value,
560
    // i is series index
561
    // p is point containing the value
562
    var valueFormatter = function(d, i, p) {
575 563
        return d;
576 564
    };
577 565

  
......
584 572
        return d;
585 573
    };
586 574

  
587
    // By default, the tooltip model renders a beautiful table inside a DIV.
588
    // You can override this function if a custom tooltip is desired.
589
    var contentGenerator = function(d) {
575
    // By default, the tooltip model renders a beautiful table inside a DIV, returned as HTML
576
    // You can override this function if a custom tooltip is desired. For instance, you could directly manipulate
577
    // the DOM by accessing elem and returning false.
578
    var contentGenerator = function(d, elem) {
590 579
        if (d === null) {
591 580
            return '';
592 581
        }
......
627 616

  
628 617
        trowEnter.append("td")
629 618
            .classed("value",true)
630
            .html(function(p, i) { return valueFormatter(p.value, i) });
619
            .html(function(p, i) { return valueFormatter(p.value, i, p) });
620

  
621
        trowEnter.filter(function (p,i) { return p.percent !== undefined }).append("td")
622
            .classed("percent", true)
623
            .html(function(p, i) { return "(" + d3.format('%')(p.percent) + ")" });
631 624

  
632 625
        trowEnter.selectAll("td").each(function(p) {
633 626
            if (p.highlight) {
......
647 640

  
648 641
    };
649 642

  
643
    /*
644
     Function that returns the position (relative to the viewport/document.body)
645
     the tooltip should be placed in.
646
     Should return: {
647
        left: <leftPos>,
648
        top: <topPos>
649
     }
650
     */
651
    var position = function() {
652
        var pos = {
653
            left: d3.event !== null ? d3.event.clientX : 0,
654
            top: d3.event !== null ? d3.event.clientY : 0
655
        };
656

  
657
        if(getComputedStyle(document.body).transform != 'none') {
658
            // Take the offset into account, as now the tooltip is relative
659
            // to document.body.
660
            var client = document.body.getBoundingClientRect();
661
            pos.left -= client.left;
662
            pos.top -= client.top;
663
        }
664

  
665
        return pos;
666
    };
667

  
650 668
    var dataSeriesExists = function(d) {
651 669
        if (d && d.series) {
652 670
            if (nv.utils.isArray(d.series)) {
......
734 752
            } else {
735 753
                // using tooltip.style('transform') returns values un-usable for tween
736 754
                var old_translate = 'translate(' + lastPosition.left + 'px, ' + lastPosition.top + 'px)';
737
                var new_translate = 'translate(' + left + 'px, ' + top + 'px)';
755
                var new_translate = 'translate(' + Math.round(left) + 'px, ' + Math.round(top) + 'px)';
738 756
                var translateInterpolator = d3.interpolateString(old_translate, new_translate);
739 757
                var is_hidden = tooltip.style('opacity') < 0.1;
740 758

  
......
762 780
    // Creates new tooltip container, or uses existing one on DOM.
763 781
    function initTooltip() {
764 782
        if (!tooltip || !tooltip.node()) {
765
            var container = chartContainer ? chartContainer : document.body;
766 783
            // Create new tooltip div if it doesn't exist on DOM.
767 784

  
768 785
            var data = [1];
769
            tooltip = d3.select(container).selectAll('.nvtooltip').data(data);
786
            tooltip = d3.select(document.body).select('#'+id).data(data);
770 787

  
771 788
            tooltip.enter().append('div')
772 789
                   .attr("class", "nvtooltip " + (classes ? classes : "xy-tooltip"))
......
789 806
        nv.dom.write(function () {
790 807
            initTooltip();
791 808
            // Generate data and set it into tooltip.
792
            // Bonus - If you override contentGenerator and return falsey you can use something like
793
            //         React or Knockout to bind the data for your tooltip.
794
            var newContent = contentGenerator(data);
809
            // Bonus - If you override contentGenerator and return false, you can use something like
810
            //         Angular, React or Knockout to bind the data for your tooltip directly to the DOM.
811
            var newContent = contentGenerator(data, tooltip.node());
795 812
            if (newContent) {
796 813
                tooltip.node().innerHTML = newContent;
797 814
            }
......
812 829
        distance: {get: function(){return distance;}, set: function(_){distance=_;}},
813 830
        snapDistance: {get: function(){return snapDistance;}, set: function(_){snapDistance=_;}},
814 831
        classes: {get: function(){return classes;}, set: function(_){classes=_;}},
815
        chartContainer: {get: function(){return chartContainer;}, set: function(_){chartContainer=_;}},
816 832
        enabled: {get: function(){return enabled;}, set: function(_){enabled=_;}},
817 833
        hideDelay: {get: function(){return hideDelay;}, set: function(_){hideDelay=_;}},
818 834
        contentGenerator: {get: function(){return contentGenerator;}, set: function(_){contentGenerator=_;}},
......
823 839
        position: {get: function(){return position;}, set: function(_){position=_;}},
824 840

  
825 841
        // Deprecated options
842
        chartContainer: {get: function(){return document.body;}, set: function(_){
843
            // deprecated after 1.8.3
844
            nv.deprecated('chartContainer', 'feature removed after 1.8.3');
845
        }},
826 846
        fixedTop: {get: function(){return null;}, set: function(_){
827 847
            // deprecated after 1.8.1
828 848
            nv.deprecated('fixedTop', 'feature removed after 1.8.1');
......
1552 1572
    if (!array1 || !array2)
1553 1573
        return false;
1554 1574

  
1555
    // compare lengths - can save a lot of time 
1575
    // compare lengths - can save a lot of time
1556 1576
    if (array1.length != array2.length)
1557 1577
        return false;
1558 1578

  
......
1569 1589
        }
1570 1590
    }
1571 1591
    return true;
1572
};nv.models.axis = function() {
1592
};
1593

  
1594
/*
1595
 Check if a point within an arc
1596
 */
1597
nv.utils.pointIsInArc = function(pt, ptData, d3Arc) {
1598
    // Center of the arc is assumed to be 0,0
1599
    // (pt.x, pt.y) are assumed to be relative to the center
1600
    var r1 = d3Arc.innerRadius()(ptData), // Note: Using the innerRadius
1601
      r2 = d3Arc.outerRadius()(ptData),
1602
      theta1 = d3Arc.startAngle()(ptData),
1603
      theta2 = d3Arc.endAngle()(ptData);
1604

  
1605
    var dist = pt.x * pt.x + pt.y * pt.y,
1606
      angle = Math.atan2(pt.x, -pt.y); // Note: different coordinate system.
1607

  
1608
    angle = (angle < 0) ? (angle + Math.PI * 2) : angle;
1609

  
1610
    return (r1 * r1 <= dist) && (dist <= r2 * r2) &&
1611
      (theta1 <= angle) && (angle <= theta2);
1612
};
1613

  
1614
nv.models.axis = function() {
1573 1615
    "use strict";
1574 1616

  
1575 1617
    //============================================================
......
1593 1635
        , fontSize = undefined
1594 1636
        , duration = 250
1595 1637
        , dispatch = d3.dispatch('renderEnd')
1638
        , tickFormatMaxMin
1596 1639
        ;
1597 1640
    axis
1598 1641
        .scale(scale)
......
1677 1720
                            .attr('y', -axis.tickPadding())
1678 1721
                            .attr('text-anchor', 'middle')
1679 1722
                            .text(function(d,i) {
1680
                                var v = fmt(d);
1723
                                var formatter = tickFormatMaxMin || fmt;
1724
                                var v = formatter(d);
1681 1725
                                return ('' + v).match('NaN') ? '' : v;
1682 1726
                            });
1683 1727
                        axisMaxMin.watchTransition(renderWatch, 'min-max top')
......
1694 1738
                    var rotateLabelsRule = '';
1695 1739
                    if (rotateLabels%360) {
1696 1740
                        //Reset transform on ticks so textHeight can be calculated correctly
1697
                        xTicks.attr('transform', ''); 
1741
                        xTicks.attr('transform', '');
1698 1742
                        //Calculate the longest xTick width
1699 1743
                        xTicks.each(function(d,i){
1700 1744
                            var box = this.getBoundingClientRect();
......
1752 1796
                            .attr('transform', rotateLabelsRule)
1753 1797
                            .style('text-anchor', rotateLabels ? (rotateLabels%360 > 0 ? 'start' : 'end') : 'middle')
1754 1798
                            .text(function(d,i) {
1755
                                var v = fmt(d);
1799
                                var formatter = tickFormatMaxMin || fmt;
1800
                                var v = formatter(d);
1756 1801
                                return ('' + v).match('NaN') ? '' : v;
1757 1802
                            });
1758 1803
                        axisMaxMin.watchTransition(renderWatch, 'min-max bottom')
......
1767 1812
                    axisLabel
1768 1813
                        .style('text-anchor', rotateYLabel ? 'middle' : 'begin')
1769 1814
                        .attr('transform', rotateYLabel ? 'rotate(90)' : '')
1770
                        .attr('y', rotateYLabel ? (-Math.max(margin.right, width) + 12) : -10) //TODO: consider calculating this based on largest tick width... OR at least expose this on chart
1815
                        .attr('y', rotateYLabel ? (-Math.max(margin.right, width) + 12 - (axisLabelDistance || 0)) : -10) //TODO: consider calculating this based on largest tick width... OR at least expose this on chart
1771 1816
                        .attr('x', rotateYLabel ? (d3.max(scale.range()) / 2) : axis.tickPadding());
1772 1817
                    if (showMaxMin) {
1773 1818
                        axisMaxMin = wrap.selectAll('g.nv-axisMaxMin')
1774 1819
                            .data(scale.domain());
1775
                        axisMaxMin.enter().append('g').attr('class',function(d,i){
1820
                       	axisMaxMin.enter().append('g').attr('class',function(d,i){
1776 1821
                                return ['nv-axisMaxMin','nv-axisMaxMin-y',(i == 0 ? 'nv-axisMin-y':'nv-axisMax-y')].join(' ')
1777 1822
                        }).append('text')
1778 1823
                            .style('opacity', 0);
......
1787 1832
                            .attr('x', axis.tickPadding())
1788 1833
                            .style('text-anchor', 'start')
1789 1834
                            .text(function(d, i) {
1790
                                var v = fmt(d);
1835
                                var formatter = tickFormatMaxMin || fmt;
1836
                                var v = formatter(d);
1791 1837
                                return ('' + v).match('NaN') ? '' : v;
1792 1838
                            });
1793 1839
                        axisMaxMin.watchTransition(renderWatch, 'min-max right')
......
1831 1877
                            .attr('x', -axis.tickPadding())
1832 1878
                            .attr('text-anchor', 'end')
1833 1879
                            .text(function(d,i) {
1834
                                var v = fmt(d);
1880
                                var formatter = tickFormatMaxMin || fmt;
1881
                                var v = formatter(d);
1835 1882
                                return ('' + v).match('NaN') ? '' : v;
1836 1883
                            });
1837 1884
                        axisMaxMin.watchTransition(renderWatch, 'min-max right')
......
1902 1949
                    and the arithmetic trick below solves that.
1903 1950
                    */
1904 1951
                    return !parseFloat(Math.round(d * 100000) / 1000000) && (d !== undefined)
1905
                }) 
1952
                })
1906 1953
                .classed('zero', true);
1907
            
1954

  
1908 1955
            //store old scales for use in transitions on update
1909 1956
            scale0 = scale.copy();
1910 1957

  
......
1935 1982
        ticks:             {get: function(){return ticks;}, set: function(_){ticks=_;}},
1936 1983
        width:             {get: function(){return width;}, set: function(_){width=_;}},
1937 1984
        fontSize:          {get: function(){return fontSize;}, set: function(_){fontSize=_;}},
1985
        tickFormatMaxMin:  {get: function(){return tickFormatMaxMin;}, set: function(_){tickFormatMaxMin=_;}},
1938 1986

  
1939 1987
        // options that require extra logic in the setter
1940 1988
        margin: {get: function(){return margin;}, set: function(_){
......
2070 2118
            boxEnter.each(function(d,i) {
2071 2119
                var box = d3.select(this);
2072 2120
                [getWl, getWh].forEach(function (f) {
2073
                    if (f(d)) {
2121
                    if (f(d) !== undefined && f(d) !== null) {
2074 2122
                        var key = (f === getWl) ? 'low' : 'high';
2075 2123
                        box.append('line')
2076 2124
                          .style('stroke', getColor(d) || color(d,i))
......
2535 2583
        , reverse = false
2536 2584
        , ranges = function(d) { return d.ranges }
2537 2585
        , markers = function(d) { return d.markers ? d.markers : [] }
2586
        , markerLines = function(d) { return d.markerLines ? d.markerLines : [0] }
2538 2587
        , measures = function(d) { return d.measures }
2539 2588
        , rangeLabels = function(d) { return d.rangeLabels ? d.rangeLabels : [] }
2540 2589
        , markerLabels = function(d) { return d.markerLabels ? d.markerLabels : []  }
2590
        , markerLineLabels = function(d) { return d.markerLineLabels ? d.markerLineLabels : []  }
2541 2591
        , measureLabels = function(d) { return d.measureLabels ? d.measureLabels : []  }
2542 2592
        , forceX = [0] // List of numbers to Force into the X scale (ie. 0, or a max / min, etc.)
2543 2593
        , width = 380
......
2548 2598
        , dispatch = d3.dispatch('elementMouseover', 'elementMouseout', 'elementMousemove')
2549 2599
        , defaultRangeLabels = ["Maximum", "Mean", "Minimum"]
2550 2600
        , legacyRangeClassNames = ["Max", "Avg", "Min"]
2601
        , duration = 1000
2551 2602
        ;
2552 2603

  
2553 2604
    function sortLabels(labels, values){
......
2569 2620

  
2570 2621
            var rangez = ranges.call(this, d, i).slice(),
2571 2622
                markerz = markers.call(this, d, i).slice(),
2623
                markerLinez = markerLines.call(this, d, i).slice(),
2572 2624
                measurez = measures.call(this, d, i).slice(),
2573 2625
                rangeLabelz = rangeLabels.call(this, d, i).slice(),
2574 2626
                markerLabelz = markerLabels.call(this, d, i).slice(),
2627
                markerLineLabelz = markerLineLabels.call(this, d, i).slice(),
2575 2628
                measureLabelz = measureLabels.call(this, d, i).slice();
2576 2629

  
2577 2630
            // Sort labels according to their sorted values
2578 2631
            sortLabels(rangeLabelz, rangez);
2579 2632
            sortLabels(markerLabelz, markerz);
2633
            sortLabels(markerLineLabelz, markerLinez);
2580 2634
            sortLabels(measureLabelz, measurez);
2581 2635

  
2582 2636
            // sort values descending
2583 2637
            rangez.sort(d3.descending);
2584 2638
            markerz.sort(d3.descending);
2639
            markerLinez.sort(d3.descending);
2585 2640
            measurez.sort(d3.descending);
2586 2641

  
2587 2642
            // Setup Scales
......
2628 2683
            for(var i=0,il=rangez.length; i<il; i++){
2629 2684
                var range = rangez[i];
2630 2685
                g.select('rect.nv-range'+i)
2686
                    .datum(range)
2631 2687
                    .attr('height', availableHeight)
2688
                    .transition()
2689
                    .duration(duration)
2632 2690
                    .attr('width', w1(range))
2633 2691
                    .attr('x', xp1(range))
2634
                    .datum(range)
2635 2692
            }
2636 2693

  
2637 2694
            g.select('rect.nv-measure')
2638 2695
                .style('fill', color)
2639 2696
                .attr('height', availableHeight / 3)
2640 2697
                .attr('y', availableHeight / 3)
2641
                .attr('width', measurez < 0 ?
2642
                    x1(0) - x1(measurez[0])
2643
                    : x1(measurez[0]) - x1(0))
2644
                .attr('x', xp1(measurez))
2645 2698
                .on('mouseover', function() {
2646 2699
                    dispatch.elementMouseover({
2647 2700
                        value: measurez[0],
......
2662 2715
                        label: measureLabelz[0] || 'Current',
2663 2716
                        color: d3.select(this).style("fill")
2664 2717
                    })
2665
                });
2718
                })
2719
                .transition()
2720
                .duration(duration)
2721
                .attr('width', measurez < 0 ?
2722
                    x1(0) - x1(measurez[0])
2723
                    : x1(measurez[0]) - x1(0))
2724
                .attr('x', xp1(measurez));
2666 2725

  
2667 2726
            var h3 =  availableHeight / 6;
2668 2727

  
......
2702 2761

  
2703 2762
            g.selectAll("path.nv-markerTriangle")
2704 2763
              .data(markerData)
2764
              .transition()
2765
              .duration(duration)
2705 2766
              .attr('transform', function(d) { return 'translate(' + x1(d.value) + ',' + (availableHeight / 2) + ')' });
2706 2767

  
2768
            var markerLinesData = markerLinez.map( function(marker, index) {
2769
                return {value: marker, label: markerLineLabelz[index]}
2770
            });
2771
            gEnter
2772
              .selectAll("line.nv-markerLine")
2773
              .data(markerLinesData)
2774
              .enter()
2775
              .append('line')
2776
              .attr('cursor', '')
2777
              .attr('class', 'nv-markerLine')
2778
              .attr('x1', function(d) { return x1(d.value) })
2779
              .attr('y1', '2')
2780
              .attr('x2', function(d) { return x1(d.value) })
2781
              .attr('y2', availableHeight - 2)
2782
              .on('mouseover', function(d) {
2783
                dispatch.elementMouseover({
2784
                  value: d.value,
2785
                  label: d.label || 'Previous',
2786
                  color: d3.select(this).style("fill"),
2787
                  pos: [x1(d.value), availableHeight/2]
2788
                })
2789

  
2790
              })
2791
              .on('mousemove', function(d) {
2792
                  dispatch.elementMousemove({
2793
                      value: d.value,
2794
                      label: d.label || 'Previous',
2795
                      color: d3.select(this).style("fill")
2796
                  })
2797
              })
2798
              .on('mouseout', function(d, i) {
2799
                  dispatch.elementMouseout({
2800
                      value: d.value,
2801
                      label: d.label || 'Previous',
2802
                      color: d3.select(this).style("fill")
2803
                  })
2804
              });
2805

  
2806
            g.selectAll("line.nv-markerLine")
2807
              .data(markerLinesData)
2808
              .transition()
2809
              .duration(duration)
2810
              .attr('x1', function(d) { return x1(d.value) })
2811
              .attr('x2', function(d) { return x1(d.value) });
2812

  
2707 2813
            wrap.selectAll('.nv-range')
2708 2814
                .on('mouseover', function(d,i) {
2709 2815
                    var label = rangeLabelz[i] || defaultRangeLabels[i];
......
2749 2855
        width:    {get: function(){return width;}, set: function(_){width=_;}},
2750 2856
        height:    {get: function(){return height;}, set: function(_){height=_;}},
2751 2857
        tickFormat:    {get: function(){return tickFormat;}, set: function(_){tickFormat=_;}},
2858
        duration:    {get: function(){return duration;}, set: function(_){duration=_;}},
2752 2859

  
2753 2860
        // options that require extra logic in the setter
2754 2861
        margin: {get: function(){return margin;}, set: function(_){
......
2794 2901
        , width = null
2795 2902
        , height = 55
2796 2903
        , tickFormat = null
2797
    , ticks = null
2904
        , ticks = null
2798 2905
        , noData = null
2799 2906
        , dispatch = d3.dispatch()
2800 2907
        ;
......
2868 2975

  
2869 2976
            bullet
2870 2977
                .width(availableWidth)
2871
                .height(availableHeight)
2978
                .height(availableHeight);
2872 2979

  
2873 2980
            var bulletWrap = g.select('.nv-bulletWrap');
2874 2981
            d3.transition(bulletWrap).call(bullet);
......
2900 3007

  
2901 3008
            // Transition the updating ticks to the new scale, x1.
2902 3009
            var tickUpdate = d3.transition(tick)
3010
                .transition()
3011
                .duration(bullet.duration())
2903 3012
                .attr('transform', function(d) { return 'translate(' + x1(d) + ',0)' })
2904 3013
                .style('opacity', 1);
2905 3014

  
......
2912 3021

  
2913 3022
            // Transition the exiting ticks to the new scale, x1.
2914 3023
            d3.transition(tick.exit())
3024
                .transition()
3025
                .duration(bullet.duration())
2915 3026
                .attr('transform', function(d) { return 'translate(' + x1(d) + ',0)' })
2916 3027
                .style('opacity', 1e-6)
2917 3028
                .remove();
......
3227 3338
        ;
3228 3339

  
3229 3340
    var margin = {top: 30, right: 30, bottom: 50, left: 60}
3341
        , marginTop = null
3230 3342
        , color = nv.utils.defaultColor()
3231 3343
        , width = null
3232 3344
        , height = null
......
3271 3383
    var dx = d3.scale.linear()
3272 3384
        , index = {i: 0, x: 0}
3273 3385
        , renderWatch = nv.utils.renderWatch(dispatch, duration)
3386
        , currentYDomain
3274 3387
        ;
3275 3388

  
3276 3389
    var stateGetter = function(data) {
......
3375 3488
            x = lines.xScale();
3376 3489
            y = lines.yScale();
3377 3490

  
3378
            if (!rescaleY) {
3379
                var seriesDomains = data
3380
                    .filter(function(series) { return !series.disabled })
3381
                    .map(function(series,i) {
3382
                        var initialDomain = d3.extent(series.values, lines.y());
3383

  
3384
                        //account for series being disabled when losing 95% or more
3385
                        if (initialDomain[0] < -.95) initialDomain[0] = -.95;
3386

  
3387
                        return [
3388
                                (initialDomain[0] - initialDomain[1]) / (1 + initialDomain[1]),
3389
                                (initialDomain[1] - initialDomain[0]) / (1 + initialDomain[0])
3390
                        ];
3391
                    });
3392

  
3393
                var completeDomain = [
3394
                    d3.min(seriesDomains, function(d) { return d[0] }),
3395
                    d3.max(seriesDomains, function(d) { return d[1] })
3396
                ];
3397

  
3398
                lines.yDomain(completeDomain);
3399
            } else {
3400
                lines.yDomain(null);
3401
            }
3402 3491

  
3403 3492
            dx.domain([0, data[0].values.length - 1]) //Assumes all series have same length
3404 3493
                .range([0, availableWidth])
......
3406 3495

  
3407 3496
            var data = indexify(index.i, data);
3408 3497

  
3498
            // initialize the starting yDomain for the not-rescale case after indexify (to have calculated point.display)
3499
            if (typeof(currentYDomain) === "undefined") {
3500
                currentYDomain = getCurrentYDomain(data);
3501
            }
3502

  
3503
            if (!rescaleY) {
3504
                lines.yDomain(currentYDomain);
3505
                lines.clipEdge(true);
3506
            } else {
3507
                lines.yDomain(null);
3508
            }
3509

  
3409 3510
            // Setup containers and skeleton of chart
3410 3511
            var interactivePointerEvents = (useInteractiveGuideline) ? "none" : "all";
3411 3512
            var wrap = container.selectAll('g.nv-wrap.nv-cumulativeLine').data([data]);
......
3431 3532
                    .datum(data)
3432 3533
                    .call(legend);
3433 3534

  
3434
                if ( margin.top != legend.height()) {
3535
                if (!marginTop && legend.height() !== margin.top) {
3435 3536
                    margin.top = legend.height();
3436 3537
                    availableHeight = nv.utils.availableHeight(height, container, margin);
3437 3538
                }
......
3468 3569
                    .attr("transform", "translate(" + availableWidth + ",0)");
3469 3570
            }
3470 3571

  
3471
            // Show error if series goes below 100%
3572
            // Show error if index point value is 0 (division by zero avoided)
3472 3573
            var tempDisabled = data.filter(function(d) { return d.tempDisabled });
3473 3574

  
3474 3575
            wrap.select('.tempDisabled').remove(); //clean-up and prevent duplicates
......
3638 3739
            controls.dispatch.on('legendClick', function(d,i) {
3639 3740
                d.disabled = !d.disabled;
3640 3741
                rescaleY = !d.disabled;
3641

  
3642 3742
                state.rescaleY = rescaleY;
3743
                if (!rescaleY) {
3744
                    currentYDomain = getCurrentYDomain(data); // rescale is turned off, so set the currentYDomain
3745
                }
3643 3746
                dispatch.stateChange(state);
3644 3747
                chart.update();
3645 3748
            });
......
3658 3761
                data
3659 3762
                    .filter(function(series, i) {
3660 3763
                        series.seriesIndex = i;
3661
                        return !series.disabled;
3764
                        return !(series.disabled || series.tempDisabled);
3662 3765
                    })
3663 3766
                    .forEach(function(series,i) {
3664 3767
                        pointIndex = nv.interactiveBisect(series.values, e.pointXValue, chart.x());
......
3686 3789

  
3687 3790
                var xValue = xAxis.tickFormat()(chart.x()(singlePoint,pointIndex), pointIndex);
3688 3791
                interactiveLayer.tooltip
3689
                    .chartContainer(that.parentNode)
3690 3792
                    .valueFormatter(function(d,i) {
3691 3793
                        return yAxis.tickFormat()(d);
3692 3794
                    })
......
3774 3876
            }
3775 3877
            var v = indexifyYGetter(indexValue, idx);
3776 3878

  
3777
            //TODO: implement check below, and disable series if series loses 100% or more cause divide by 0 issue
3778
            if (v < -.95 && !noErrorCheck) {
3779
                //if a series loses more than 100%, calculations fail.. anything close can cause major distortion (but is mathematically correct till it hits 100)
3780

  
3879
            // avoid divide by zero
3880
            if (Math.abs(v) < 0.00001 && !noErrorCheck) {
3781 3881
                line.tempDisabled = true;
3782 3882
                return line;
3783 3883
            }
......
3785 3885
            line.tempDisabled = false;
3786 3886

  
3787 3887
            line.values = line.values.map(function(point, pointIndex) {
3788
                point.display = {'y': (indexifyYGetter(point, pointIndex) - v) / (1 + v) };
3888
                point.display = {'y': (indexifyYGetter(point, pointIndex) - v) / v };
3789 3889
                return point;
3790 3890
            });
3791 3891

  
......
3793 3893
        })
3794 3894
    }
3795 3895

  
3896
    function getCurrentYDomain(data) {
3897
        var seriesDomains = data
3898
            .filter(function(series) { return !(series.disabled || series.tempDisabled)})
3899
            .map(function(series,i) {
3900
                return d3.extent(series.values, function (d) { return d.display.y });
3901
            });
3902

  
3903
        return [
3904
            d3.min(seriesDomains, function(d) { return d[0] }),
3905
            d3.max(seriesDomains, function(d) { return d[1] })
3906
        ];
3907
    }
3908

  
3796 3909
    //============================================================
3797 3910
    // Expose Public Variables
3798 3911
    //------------------------------------------------------------
......
3814 3927
        // simple options, just get/set the necessary values
3815 3928
        width:      {get: function(){return width;}, set: function(_){width=_;}},
3816 3929
        height:     {get: function(){return height;}, set: function(_){height=_;}},
3817
        rescaleY:     {get: function(){return rescaleY;}, set: function(_){rescaleY=_;}},
3818 3930
        showControls:     {get: function(){return showControls;}, set: function(_){showControls=_;}},
3819 3931
        showLegend: {get: function(){return showLegend;}, set: function(_){showLegend=_;}},
3820 3932
        average: {get: function(){return average;}, set: function(_){average=_;}},
......
3825 3937
        noErrorCheck:    {get: function(){return noErrorCheck;}, set: function(_){noErrorCheck=_;}},
3826 3938

  
3827 3939
        // options that require extra logic in the setter
3940
        rescaleY:     {get: function(){return rescaleY;}, set: function(_){
3941
            rescaleY = _;
3942
            chart.state.rescaleY = _; // also update state
3943
        }},
3828 3944
        margin: {get: function(){return margin;}, set: function(_){
3829
            margin.top    = _.top    !== undefined ? _.top    : margin.top;
3945
            if (_.top !== undefined) {
3946
                margin.top = _.top;
3947
                marginTop = _.top;
3948
            }
3830 3949
            margin.right  = _.right  !== undefined ? _.right  : margin.right;
3831 3950
            margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom;
3832 3951
            margin.left   = _.left   !== undefined ? _.left   : margin.left;
......
4125 4244
    var discretebar = nv.models.discreteBar()
4126 4245
        , xAxis = nv.models.axis()
4127 4246
        , yAxis = nv.models.axis()
4128
    , legend = nv.models.legend()
4247
	, legend = nv.models.legend()
4129 4248
        , tooltip = nv.models.tooltip()
4130 4249
        ;
4131 4250

  
4132 4251
    var margin = {top: 15, right: 10, bottom: 50, left: 60}
4252
        , marginTop = null
4133 4253
        , width = null
4134 4254
        , height = null
4135 4255
        , color = nv.utils.getColor()
4136
    , showLegend = false
4256
	, showLegend = false
4137 4257
        , showXAxis = true
4138 4258
        , showYAxis = true
4139 4259
        , rightAlignYAxis = false
......
4216 4336
                .append('line');
4217 4337

  
4218 4338
            gEnter.append('g').attr('class', 'nv-barsWrap');
4219
        gEnter.append('g').attr('class', 'nv-legendWrap');
4339
	    gEnter.append('g').attr('class', 'nv-legendWrap');
4220 4340

  
4221 4341
            g.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
4222 4342

  
......
4230 4350
                    .datum(data)
4231 4351
                    .call(legend);
4232 4352

  
4233
                if ( margin.top != legend.height()) {
4353
                if (!marginTop && legend.height() !== margin.top) {
4234 4354
                    margin.top = legend.height();
4235 4355
                    availableHeight = nv.utils.availableHeight(height, container, margin);
4236 4356
                }
......
4238 4358
                wrap.select('.nv-legendWrap')
4239 4359
                    .attr('transform', 'translate(0,' + (-margin.top) +')')
4240 4360
            }
4241
            
4361

  
4242 4362
            if (rightAlignYAxis) {
4243 4363
                g.select(".nv-y.nv-axis")
4244 4364
                    .attr("transform", "translate(" + availableWidth + ",0)");
4245
            }       
4365
            }
4246 4366

  
4247 4367
            // Main Chart Component(s)
4248 4368
            discretebar
......
4355 4475
        // simple options, just get/set the necessary values
4356 4476
        width:      {get: function(){return width;}, set: function(_){width=_;}},
4357 4477
        height:     {get: function(){return height;}, set: function(_){height=_;}},
4358
    showLegend: {get: function(){return showLegend;}, set: function(_){showLegend=_;}},
4478
	showLegend: {get: function(){return showLegend;}, set: function(_){showLegend=_;}},
4359 4479
        staggerLabels: {get: function(){return staggerLabels;}, set: function(_){staggerLabels=_;}},
4360 4480
        rotateLabels:  {get: function(){return rotateLabels;}, set: function(_){rotateLabels=_;}},
4361 4481
        wrapLabels:  {get: function(){return wrapLabels;}, set: function(_){wrapLabels=!!_;}},
......
4365 4485

  
4366 4486
        // options that require extra logic in the setter
4367 4487
        margin: {get: function(){return margin;}, set: function(_){
4368
            margin.top    = _.top    !== undefined ? _.top    : margin.top;
4488
            if (_.top !== undefined) {
4489
                margin.top = _.top;
4490
                marginTop = _.top;
4491
            }
4369 4492
            margin.right  = _.right  !== undefined ? _.right  : margin.right;
4370 4493
            margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom;
4371 4494
            margin.left   = _.left   !== undefined ? _.left   : margin.left;
......
4380 4503
        color:  {get: function(){return color;}, set: function(_){
4381 4504
            color = nv.utils.getColor(_);
4382 4505
            discretebar.color(color);
4383
        legend.color(color);
4506
	    legend.color(color);
4384 4507
        }},
4385 4508
        rightAlignYAxis: {get: function(){return rightAlignYAxis;}, set: function(_){
4386 4509
            rightAlignYAxis = _;
......
4553 4676

  
4554 4677
    return chart;
4555 4678
}
4556
nv.models.forceDirectedGraph = function() {
4679
nv.models.focus = function(content) {
4557 4680
    "use strict";
4558 4681

  
4559 4682
    //============================================================
4560 4683
    // Public Variables with Default Settings
4561 4684
    //------------------------------------------------------------
4562
    var margin = {top: 2, right: 0, bottom: 2, left: 0}
4563
        , width = 400
4564
        , height = 32
4565
        , container = null
4566
        , dispatch = d3.dispatch('renderEnd')
4567
        , color = nv.utils.getColor(['#000'])
4568
        , tooltip      = nv.models.tooltip()
4569
        , noData = null
4570
        // Force directed graph specific parameters [default values]
4571
        , linkStrength = 0.1
4572
        , friction = 0.9
4573
        , linkDist = 30
4574
        , charge = -120
4575
        , gravity = 0.1
4576
        , theta = 0.8
4577
        , alpha = 0.1
4578
        , radius = 5
4579
        // These functions allow to add extra attributes to ndes and links
4580
        ,nodeExtras = function(nodes) { /* Do nothing */ }
4581
        ,linkExtras = function(links) { /* Do nothing */ }
4685

  
4686
    var content = content || nv.models.line()
4687
        , xAxis = nv.models.axis()
4688
        , yAxis = nv.models.axis()
4689
        , brush = d3.svg.brush()
4690
        ;
4691

  
4692
    var margin = {top: 10, right: 0, bottom: 30, left: 0}
4693
        , color = nv.utils.defaultColor()
4694
        , width = null
4695
        , height = 70
4696
        , showXAxis = true
4697
        , showYAxis = false
4698
        , rightAlignYAxis = false
4699
        , ticks = null
4700
        , x
4701
        , y
4702
        , brushExtent = null
4703
        , duration = 250
4704
        , dispatch = d3.dispatch('brush', 'onBrush', 'renderEnd')
4705
        , syncBrushing = true
4582 4706
        ;
4583 4707

  
4708
    content.interactive(false);
4709
    content.pointActive(function(d) { return false; });
4584 4710

  
4585 4711
    //============================================================
4586 4712
    // Private Variables
4587 4713
    //------------------------------------------------------------
4588 4714

  
4589
    var renderWatch = nv.utils.renderWatch(dispatch);
4715
    var renderWatch = nv.utils.renderWatch(dispatch, duration);
4590 4716

  
4591 4717
    function chart(selection) {
4592 4718
        renderWatch.reset();
4719
        renderWatch.models(content);
4720
        if (showXAxis) renderWatch.models(xAxis);
4721
        if (showYAxis) renderWatch.models(yAxis);
4593 4722

  
4594 4723
        selection.each(function(data) {
4595
          container = d3.select(this);
4596
          nv.utils.initSVG(container);
4724
            var container = d3.select(this);
4725
            nv.utils.initSVG(container);
4726
            var availableWidth = nv.utils.availableWidth(width, container, margin),
4727
                availableHeight = height - margin.top - margin.bottom;
4597 4728

  
4598
          var availableWidth = nv.utils.availableWidth(width, container, margin),
4599
              availableHeight = nv.utils.availableHeight(height, container, margin);
4729
            chart.update = function() { 
4730
                if( duration === 0 ) {
4731
                    container.call( chart );
4732
                } else {
4733
                    container.transition().duration(duration).call(chart);
4734
                }
4735
            };
4736
            chart.container = this;
4600 4737

  
4601
          container
4602
                  .attr("width", availableWidth)
4603
                  .attr("height", availableHeight);
4738
            // Setup Scales
4739
            x = content.xScale();
4740
            y = content.yScale();
4604 4741

  
4605
          // Display No Data message if there's nothing to show.
4606
          if (!data || !data.links || !data.nodes) {
4607
              nv.utils.noData(chart, container)
4608
              return chart;
4609
          } else {
4610
              container.selectAll('.nv-noData').remove();
4611
          }
4612
          container.selectAll('*').remove();
4742
            // Setup containers and skeleton of chart
4743
            var wrap = container.selectAll('g.nv-focus').data([data]);
4744
            var gEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-focus').append('g');
4745
            var g = wrap.select('g');
4613 4746

  
4614
          // Collect names of all fields in the nodes
4615
          var nodeFieldSet = new Set();
4616
          data.nodes.forEach(function(node) {
4617
            var keys = Object.keys(node);
4618
            keys.forEach(function(key) {
4619
              nodeFieldSet.add(key);
4620
            });
4621
          });
4747
            wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
4622 4748

  
4623
          var force = d3.layout.force()
4624
                .nodes(data.nodes)
4625
                .links(data.links)
4626
                .size([availableWidth, availableHeight])
4627
                .linkStrength(linkStrength)
4628
                .friction(friction)
4629
                .linkDistance(linkDist)
4630
                .charge(charge)
4631
                .gravity(gravity)
4632
                .theta(theta)
4633
                .alpha(alpha)
4634
                .start();
4749
            gEnter.append('g').attr('class', 'nv-background').append('rect');
4750
            gEnter.append('g').attr('class', 'nv-x nv-axis');
4751
            gEnter.append('g').attr('class', 'nv-y nv-axis');
4752
            gEnter.append('g').attr('class', 'nv-contentWrap');
4753
            gEnter.append('g').attr('class', 'nv-brushBackground');
4754
            gEnter.append('g').attr('class', 'nv-x nv-brush');
4635 4755

  
4636
          var link = container.selectAll(".link")
4637
                .data(data.links)
4638
                .enter().append("line")
4639
                .attr("class", "nv-force-link")
4640
                .style("stroke-width", function(d) { return Math.sqrt(d.value); });
4756
            if (rightAlignYAxis) {
4757
                g.select(".nv-y.nv-axis")
4758
                    .attr("transform", "translate(" + availableWidth + ",0)");
4759
            }
4641 4760

  
4642
          var node = container.selectAll(".node")
4643
                .data(data.nodes)
4644
                .enter()
4645
                .append("g")
4646
                .attr("class", "nv-force-node")
4647
                .call(force.drag);
4761
            g.select('.nv-background rect')
4762
                .attr('width', availableWidth)
4763
                .attr('height', availableHeight);
4764
                
4765
            content
4766
                .width(availableWidth)
4767
                .height(availableHeight)
4768
                .color(data.map(function(d,i) {
4769
                    return d.color || color(d, i);
4770
                }).filter(function(d,i) { return !data[i].disabled; }));
4648 4771

  
4649
          node
4650
            .append("circle")
4651
            .attr("r", radius)
4652
            .style("fill", function(d) { return color(d) } )
4653
            .on("mouseover", function(evt) {
4654
              container.select('.nv-series-' + evt.seriesIndex + ' .nv-distx-' + evt.pointIndex)
4655
                  .attr('y1', evt.py);
4656
              container.select('.nv-series-' + evt.seriesIndex + ' .nv-disty-' + evt.pointIndex)
4772
            var contentWrap = g.select('.nv-contentWrap')
4773
                .datum(data.filter(function(d) { return !d.disabled; }));
4774

  
4775
            d3.transition(contentWrap).call(content);
4776
            
4777
            // Setup Brush
4778
            brush
4779
                .x(x)
4780
                .on('brush', function() {
4781
                    onBrush(syncBrushing);
4782
                });
4783

  
4784
            brush.on('brushend', function () {
4785
                if (!syncBrushing) {
4786
                    dispatch.onBrush(brush.empty() ? x.domain() : brush.extent());
4787
                }
4788
            });
4789

  
4790
            if (brushExtent) brush.extent(brushExtent);
4791

  
4792
            var brushBG = g.select('.nv-brushBackground').selectAll('g')
4793
                .data([brushExtent || brush.extent()]);
4794
    
4795
            var brushBGenter = brushBG.enter()
4796
                .append('g');
4797

  
4798
            brushBGenter.append('rect')
4799
                .attr('class', 'left')
4800
                .attr('x', 0)
4801
                .attr('y', 0)
4802
                .attr('height', availableHeight);
4803

  
4804
            brushBGenter.append('rect')
4805
                .attr('class', 'right')
4806
                .attr('x', 0)
4807
                .attr('y', 0)
4808
                .attr('height', availableHeight);
4809

  
4810
            var gBrush = g.select('.nv-x.nv-brush')
4811
                .call(brush);
4812
            gBrush.selectAll('rect')
4813
                .attr('height', availableHeight);
4814
            gBrush.selectAll('.resize').append('path').attr('d', resizePath);
4815

  
4816
            onBrush(true);
4817

  
4818
            g.select('.nv-background rect')
4819
                .attr('width', availableWidth)
4820
                .attr('height', availableHeight);
4821

  
4822
            if (showXAxis) {
4823
                xAxis.scale(x)
4824
                    ._ticks( nv.utils.calcTicksX(availableWidth/100, data) )
4825
                    .tickSize(-availableHeight, 0);
4826
  
4827
                g.select('.nv-x.nv-axis')
4828
                    .attr('transform', 'translate(0,' + y.range()[0] + ')');
4829
                d3.transition(g.select('.nv-x.nv-axis'))
4830
                    .call(xAxis);
4831
            }
4832

  
4833
            if (showYAxis) {
4834
                yAxis
4835
                    .scale(y)
4836
                    ._ticks( nv.utils.calcTicksY(availableHeight/36, data) )
4837
                    .tickSize( -availableWidth, 0);
4838

  
4839
                d3.transition(g.select('.nv-y.nv-axis'))
4840
                    .call(yAxis);
4841
            }
4842
            
4843
            g.select('.nv-x.nv-axis')
4844
                .attr('transform', 'translate(0,' + y.range()[0] + ')');
4845

  
4846
            //============================================================
4847
            // Event Handling/Dispatching (in chart's scope)
4848
            //------------------------------------------------------------
4849

  
4850
            //============================================================
4851
            // Functions
4852
            //------------------------------------------------------------
4853
    
4854
            // Taken from crossfilter (http://square.github.com/crossfilter/)
4855
            function resizePath(d) {
4856
                var e = +(d == 'e'),
4857
                    x = e ? 1 : -1,
4858
                    y = availableHeight / 3;
4859
                return 'M' + (0.5 * x) + ',' + y
4860
                    + 'A6,6 0 0 ' + e + ' ' + (6.5 * x) + ',' + (y + 6)
4861
                    + 'V' + (2 * y - 6)
4862
                    + 'A6,6 0 0 ' + e + ' ' + (0.5 * x) + ',' + (2 * y)
4863
                    + 'Z'
4864
                    + 'M' + (2.5 * x) + ',' + (y + 8)
4865
                    + 'V' + (2 * y - 8)
4866
                    + 'M' + (4.5 * x) + ',' + (y + 8)
4867
                    + 'V' + (2 * y - 8);
4868
            }
4869
    
4870
    
4871
            function updateBrushBG() {
4872
                if (!brush.empty()) brush.extent(brushExtent);
4873
                brushBG
4874
                    .data([brush.empty() ? x.domain() : brushExtent])
4875
                    .each(function(d,i) {
4876
                        var leftWidth = x(d[0]) - x.range()[0],
4877
                            rightWidth = availableWidth - x(d[1]);
4878
                        d3.select(this).select('.left')
4879
                            .attr('width',  leftWidth < 0 ? 0 : leftWidth);
4880
    
4881
                        d3.select(this).select('.right')
4882
                            .attr('x', x(d[1]))
4883
                            .attr('width', rightWidth < 0 ? 0 : rightWidth);
4884
                    });
4885
            }
4886

  
4887

  
4888
            function onBrush(shouldDispatch) {
4889
                brushExtent = brush.empty() ? null : brush.extent();
4890
                var extent = brush.empty() ? x.domain() : brush.extent();
4891
                dispatch.brush({extent: extent, brush: brush});
4892
                updateBrushBG();
4893
                if (shouldDispatch) {
4894
                    dispatch.onBrush(extent);
4895
                }
4896
            }
4897
        });
4898

  
4899
        renderWatch.renderEnd('focus immediate');
4900
        return chart;
4901
    }
4902

  
4903

  
4904
    //============================================================
4905
    // Event Handling/Dispatching (out of chart's scope)
4906
    //------------------------------------------------------------
4907

  
4908
    //============================================================
4909
    // Expose Public Variables
4910
    //------------------------------------------------------------
4911

  
4912
    // expose chart's sub-components
4913
    chart.dispatch = dispatch;
4914
    chart.content = content;
4915
    chart.brush = brush;
4916
    chart.xAxis = xAxis;
4917
    chart.yAxis = yAxis;
4918
    chart.options = nv.utils.optionsFunc.bind(chart);
4919

  
4920
    chart._options = Object.create({}, {
4921
        // simple options, just get/set the necessary values
4922
        width:      {get: function(){return width;}, set: function(_){width=_;}},
4923
        height:     {get: function(){return height;}, set: function(_){height=_;}},
4924
        showXAxis:      {get: function(){return showXAxis;}, set: function(_){showXAxis=_;}},
4925
        showYAxis:    {get: function(){return showYAxis;}, set: function(_){showYAxis=_;}},
4926
        brushExtent: {get: function(){return brushExtent;}, set: function(_){brushExtent=_;}},
4927
        syncBrushing: {get: function(){return syncBrushing;}, set: function(_){syncBrushing=_;}},
4928

  
4929
        // options that require extra logic in the setter
4930
        margin: {get: function(){return margin;}, set: function(_){
4931
            margin.top    = _.top    !== undefined ? _.top    : margin.top;
4932
            margin.right  = _.right  !== undefined ? _.right  : margin.right;
4933
            margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom;
4934
            margin.left   = _.left   !== undefined ? _.left   : margin.left;
4935
        }},
4936
        duration: {get: function(){return duration;}, set: function(_){
4937
            duration = _;
4938
            renderWatch.reset(duration);
4939
            content.duration(duration);
4940
            xAxis.duration(duration);
4941
            yAxis.duration(duration);
4942
        }},
4943
        color:  {get: function(){return color;}, set: function(_){
4944
            color = nv.utils.getColor(_);
4945
            content.color(color);
4946
        }},
... This diff was truncated because it exceeds the maximum size that can be displayed.

Also available in: Unified diff