var micropolar = {
    version: "0.2.2"
};

var µ = micropolar;

µ.Axis = function module() {
    var config = {
        data: [],
        layout: {}
    }, inputConfig = {}, liveConfig = {};
    var svg, container, dispatch = d3.dispatch("hover"), radialScale, angularScale;
    var angularTooltip, radialTooltip, geometryTooltip;
    var exports = {};
    function render(_container) {
        container = _container || container;
        var data = config.data;
        var axisConfig = config.layout;
        if (typeof container === "string" || container.nodeName) {
            container = d3.select(container);
        }
        container.datum(data).each(function(_data, _index) {
            var dataOriginal = _data.slice();
            liveConfig = {
                data: µ.util.cloneJson(dataOriginal),
                layout: µ.util.cloneJson(axisConfig)
            };
            var colorIndex = 0;
            dataOriginal.forEach(function(d, i) {
                if (!d.color) {
                    d.color = axisConfig.defaultColorRange[colorIndex];
                    colorIndex = (colorIndex + 1) % axisConfig.defaultColorRange.length;
                }
                if (!d.strokeColor) {
                    d.strokeColor = d.geometry === "LinePlot" ? d.color : d3.rgb(d.color).darker().toString();
                }
                liveConfig.data[i].color = d.color;
                liveConfig.data[i].strokeColor = d.strokeColor;
                liveConfig.data[i].strokeDash = d.strokeDash;
                liveConfig.data[i].strokeSize = d.strokeSize;
            });
            var data = dataOriginal.filter(function(d, i) {
                var visible = d.visible;
                return typeof visible === "undefined" || visible === true;
            });
            var isStacked = false;
            var dataWithGroupId = data.map(function(d, i) {
                isStacked = isStacked || typeof d.groupId !== "undefined";
                return d;
            });
            var dataYStack = [];
            if (isStacked) {
                var grouped = d3.nest().key(function(d, i) {
                    return typeof d.groupId !== "undefined" ? d.groupId : "unstacked";
                }).entries(dataWithGroupId);
                var stacked = grouped.map(function(d, i) {
                    if (d.key === "unstacked") {
                        return d.values;
                    } else {
                        var prevArray = d.values[0].r.map(function(d, i) {
                            return 0;
                        });
                        d.values.forEach(function(d, i, a) {
                            d.yStack = [ prevArray ];
                            dataYStack.push(prevArray);
                            prevArray = µ.util.sumArrays(d.r, prevArray);
                        });
                        return d.values;
                    }
                });
                data = d3.merge(stacked);
            }
            data.forEach(function(d, i) {
                d.t = Array.isArray(d.t[0]) ? d.t : [ d.t ];
                d.r = Array.isArray(d.r[0]) ? d.r : [ d.r ];
            });
            var radius = Math.min(axisConfig.width - axisConfig.margin.left - axisConfig.margin.right, axisConfig.height - axisConfig.margin.top - axisConfig.margin.bottom) / 2;
            radius = Math.max(10, radius);
            var chartCenter = [ axisConfig.margin.left + radius, axisConfig.margin.top + radius ];
            var extent;
            if (isStacked) {
                var highestStackedValue = d3.max(µ.util.sumArrays(µ.util.arrayLast(data).r[0], µ.util.arrayLast(dataYStack)));
                extent = [ 0, highestStackedValue ];
            } else {
                extent = d3.extent(µ.util.flattenArray(data.map(function(d, i) {
                    return d.r;
                })));
            }
            if (axisConfig.radialAxis.domain !== µ.DATAEXTENT) {
                extent[0] = 0;
            }
            radialScale = d3.scale.linear().domain(axisConfig.radialAxis.domain !== µ.DATAEXTENT && axisConfig.radialAxis.domain ? axisConfig.radialAxis.domain : extent).range([ 0, radius ]);
            liveConfig.layout.radialAxis.domain = radialScale.domain();
            var angularDataMerged = µ.util.flattenArray(data.map(function(d, i) {
                return d.t;
            }));
            var isOrdinal = typeof angularDataMerged[0] === "string";
            var ticks;
            if (isOrdinal) {
                if (isStacked) {
                    angularDataMerged = µ.util.deduplicate(angularDataMerged);
                }
                ticks = angularDataMerged.slice();
                angularDataMerged = d3.range(angularDataMerged.length);
                data = data.map(function(d, i) {
                    var result = d;
                    d.t = [ angularDataMerged ];
                    if (isStacked) {
                        result.yStack = d.yStack;
                    }
                    return result;
                });
            }
            var hasOnlyLineOrDotPlot = data.filter(function(d, i) {
                return d.geometry === "LinePlot" || d.geometry === "DotPlot";
            }).length === data.length;
            var needsEndSpacing = axisConfig.needsEndSpacing === null ? isOrdinal || !hasOnlyLineOrDotPlot : axisConfig.needsEndSpacing;
            var useProvidedDomain = axisConfig.angularAxis.domain && axisConfig.angularAxis.domain !== µ.DATAEXTENT && !isOrdinal && axisConfig.angularAxis.domain[0] >= 0;
            var angularDomain = useProvidedDomain ? axisConfig.angularAxis.domain : d3.extent(angularDataMerged);
            var angularDomainStep = Math.abs(angularDataMerged[1] - angularDataMerged[0]);
            if (hasOnlyLineOrDotPlot && !isOrdinal) {
                angularDomainStep = 0;
            }
            var angularDomainWithPadding = angularDomain.slice();
            if (needsEndSpacing && isOrdinal) {
                angularDomainWithPadding[1] += angularDomainStep;
            }
            var tickCount = axisConfig.angularAxis.ticksCount || 4;
            if (tickCount > 8) {
                tickCount = tickCount / (tickCount / 8) + tickCount % 8;
            }
            if (axisConfig.angularAxis.ticksStep) {
                tickCount = (angularDomainWithPadding[1] - angularDomainWithPadding[0]) / tickCount;
            }
            var angularTicksStep = axisConfig.angularAxis.ticksStep || (angularDomainWithPadding[1] - angularDomainWithPadding[0]) / (tickCount * (axisConfig.minorTicks + 1));
            if (ticks) {
                angularTicksStep = Math.max(Math.round(angularTicksStep), 1);
            }
            if (!angularDomainWithPadding[2]) {
                angularDomainWithPadding[2] = angularTicksStep;
            }
            var angularAxisRange = d3.range.apply(this, angularDomainWithPadding);
            angularAxisRange = angularAxisRange.map(function(d, i) {
                return parseFloat(d.toPrecision(12));
            });
            angularScale = d3.scale.linear().domain(angularDomainWithPadding.slice(0, 2)).range(axisConfig.direction === "clockwise" ? [ 0, 360 ] : [ 360, 0 ]);
            liveConfig.layout.angularAxis.domain = angularScale.domain();
            liveConfig.layout.angularAxis.endPadding = needsEndSpacing ? angularDomainStep : 0;
            svg = d3.select(this).select("svg.chart-root");
            if (typeof svg === "undefined" || svg.empty()) {
                var skeleton = '<svg xmlns="http://www.w3.org/2000/svg" class="chart-root">' + '<g class="outer-group">' + '<g class="chart-group">' + '<circle class="background-circle"></circle>' + '<g class="geometry-group"></g>' + '<g class="radial axis-group">' + '<circle class="outside-circle"></circle>' + "</g>" + '<g class="angular axis-group"></g>' + '<g class="guides-group"><line></line><circle r="0"></circle></g>' + "</g>" + '<g class="legend-group"></g>' + '<g class="tooltips-group"></g>' + '<g class="title-group"><text></text></g>' + "</g>" + "</svg>";
                var doc = new DOMParser().parseFromString(skeleton, "application/xml");
                var newSvg = this.appendChild(this.ownerDocument.importNode(doc.documentElement, true));
                svg = d3.select(newSvg);
            }
            svg.select(".guides-group").style({
                "pointer-events": "none"
            });
            svg.select(".angular.axis-group").style({
                "pointer-events": "none"
            });
            svg.select(".radial.axis-group").style({
                "pointer-events": "none"
            });
            var chartGroup = svg.select(".chart-group");
            var lineStyle = {
                fill: "none",
                stroke: axisConfig.tickColor
            };
            var fontStyle = {
                "font-size": axisConfig.font.size,
                "font-family": axisConfig.font.family,
                fill: axisConfig.font.color,
                "text-shadow": [ "-1px 0px", "1px -1px", "-1px 1px", "1px 1px" ].map(function(d, i) {
                    return " " + d + " 0 " + axisConfig.font.outlineColor;
                }).join(",")
            };
            var legendContainer, legendBBox;
            if (axisConfig.showLegend) {
                legendContainer = svg.select(".legend-group").attr({
                    transform: "translate(" + [ radius, axisConfig.margin.top ] + ")"
                }).style({
                    display: "block"
                });
                var elements = data.map(function(d, i) {
                    var datumClone = µ.util.cloneJson(d);
                    datumClone.symbol = d.geometry === "DotPlot" ? d.dotType || "circle" : d.geometry !== "LinePlot" ? "square" : "line";
                    datumClone.visibleInLegend = typeof d.visibleInLegend === "undefined" || d.visibleInLegend;
                    datumClone.color = d.geometry === "LinePlot" ? d.strokeColor : d.color;
                    return datumClone;
                });
                var legendConfigMixin1 = µ.util.deepExtend({}, µ.Legend.defaultConfig().legendConfig);
                var legendConfigMixin2 = µ.util.deepExtend(legendConfigMixin1, {
                    container: legendContainer,
                    elements: elements,
                    reverseOrder: axisConfig.legend.reverseOrder
                });
                var legendConfigMixin3 = {
                    data: data.map(function(d, i) {
                        return d.name || "Element" + i;
                    }),
                    legendConfig: legendConfigMixin2
                };
                µ.Legend().config(legendConfigMixin3)();
                legendBBox = legendContainer.node().getBBox();
                radius = Math.min(axisConfig.width - legendBBox.width - axisConfig.margin.left - axisConfig.margin.right, axisConfig.height - axisConfig.margin.top - axisConfig.margin.bottom) / 2;
                radius = Math.max(10, radius);
                chartCenter = [ axisConfig.margin.left + radius, axisConfig.margin.top + radius ];
                radialScale.range([ 0, radius ]);
                liveConfig.layout.radialAxis.domain = radialScale.domain();
                legendContainer.attr("transform", "translate(" + [ chartCenter[0] + radius, chartCenter[1] - radius ] + ")");
            } else {
                legendContainer = svg.select(".legend-group").style({
                    display: "none"
                });
            }
            svg.attr({
                width: axisConfig.width,
                height: axisConfig.height
            }).style({
                opacity: axisConfig.opacity
            });
            chartGroup.attr("transform", "translate(" + chartCenter + ")").style({
                cursor: "crosshair"
            });
            var centeringOffset = [ (axisConfig.width - (axisConfig.margin.left + axisConfig.margin.right + radius * 2 + (legendBBox ? legendBBox.width : 0))) / 2, (axisConfig.height - (axisConfig.margin.top + axisConfig.margin.bottom + radius * 2)) / 2 ];
            centeringOffset[0] = Math.max(0, centeringOffset[0]);
            centeringOffset[1] = Math.max(0, centeringOffset[1]);
            svg.select(".outer-group").attr("transform", "translate(" + centeringOffset + ")");
            if (axisConfig.title) {
                var title = svg.select("g.title-group text").style(fontStyle).text(axisConfig.title);
                var titleBBox = title.node().getBBox();
                title.attr({
                    x: chartCenter[0] - titleBBox.width / 2,
                    y: chartCenter[1] - radius - 20
                });
            }
            var radialAxis = svg.select(".radial.axis-group");
            if (axisConfig.radialAxis.gridLinesVisible) {
                var gridCircles = radialAxis.selectAll("circle.grid-circle").data(radialScale.ticks(5));
                gridCircles.enter().append("circle").attr({
                    "class": "grid-circle"
                }).style(lineStyle);
                gridCircles.attr("r", radialScale);
                gridCircles.exit().remove();
            }
            radialAxis.select("circle.outside-circle").attr({
                r: radius
            }).style(lineStyle);
            var backgroundCircle = svg.select("circle.background-circle").attr({
                r: radius
            }).style({
                fill: axisConfig.backgroundColor,
                stroke: axisConfig.stroke
            });
            function currentAngle(d, i) {
                return angularScale(d) % 360 + axisConfig.orientation;
            }
            if (axisConfig.radialAxis.visible) {
                var axis = d3.svg.axis().scale(radialScale).ticks(5).tickSize(5);
                radialAxis.call(axis).attr({
                    transform: "rotate(" + axisConfig.radialAxis.orientation + ")"
                });
                radialAxis.selectAll(".domain").style(lineStyle);
                radialAxis.selectAll("g>text").text(function(d, i) {
                    return this.textContent + axisConfig.radialAxis.ticksSuffix;
                }).style(fontStyle).style({
                    "text-anchor": "start"
                }).attr({
                    x: 0,
                    y: 0,
                    dx: 0,
                    dy: 0,
                    transform: function(d, i) {
                        if (axisConfig.radialAxis.tickOrientation === "horizontal") {
                            return "rotate(" + -axisConfig.radialAxis.orientation + ") translate(" + [ 0, fontStyle["font-size"] ] + ")";
                        } else {
                            return "translate(" + [ 0, fontStyle["font-size"] ] + ")";
                        }
                    }
                });
                radialAxis.selectAll("g>line").style({
                    stroke: "black"
                });
            }
            var angularAxis = svg.select(".angular.axis-group").selectAll("g.angular-tick").data(angularAxisRange);
            var angularAxisEnter = angularAxis.enter().append("g").classed("angular-tick", true);
            angularAxis.attr({
                transform: function(d, i) {
                    return "rotate(" + currentAngle(d, i) + ")";
                }
            }).style({
                display: axisConfig.angularAxis.visible ? "block" : "none"
            });
            angularAxis.exit().remove();
            angularAxisEnter.append("line").classed("grid-line", true).classed("major", function(d, i) {
                return i % (axisConfig.minorTicks + 1) === 0;
            }).classed("minor", function(d, i) {
                return i % (axisConfig.minorTicks + 1) !== 0;
            }).style(lineStyle);
            angularAxisEnter.selectAll(".minor").style({
                stroke: axisConfig.minorTickColor
            });
            angularAxis.select("line.grid-line").attr({
                x1: axisConfig.tickLength ? radius - axisConfig.tickLength : 0,
                x2: radius
            }).style({
                display: axisConfig.angularAxis.gridLinesVisible ? "block" : "none"
            });
            angularAxisEnter.append("text").classed("axis-text", true).style(fontStyle);
            var ticksText = angularAxis.select("text.axis-text").attr({
                x: radius + axisConfig.labelOffset,
                dy: ".35em",
                transform: function(d, i) {
                    var angle = currentAngle(d, i);
                    var rad = radius + axisConfig.labelOffset;
                    var orient = axisConfig.angularAxis.tickOrientation;
                    if (orient === "horizontal") {
                        return "rotate(" + -angle + " " + rad + " 0)";
                    } else if (orient === "radial") {
                        return angle < 270 && angle > 90 ? "rotate(180 " + rad + " 0)" : null;
                    } else {
                        return "rotate(" + (angle <= 180 && angle > 0 ? -90 : 90) + " " + rad + " 0)";
                    }
                }
            }).style({
                "text-anchor": "middle",
                display: axisConfig.angularAxis.labelsVisible ? "block" : "none"
            }).text(function(d, i) {
                if (i % (axisConfig.minorTicks + 1) !== 0) {
                    return "";
                }
                if (ticks) {
                    return ticks[d] + axisConfig.angularAxis.ticksSuffix;
                } else {
                    return d + axisConfig.angularAxis.ticksSuffix;
                }
            }).style(fontStyle);
            if (axisConfig.angularAxis.rewriteTicks) {
                ticksText.text(function(d, i) {
                    if (i % (axisConfig.minorTicks + 1) !== 0) {
                        return "";
                    }
                    return axisConfig.angularAxis.rewriteTicks(this.textContent, i);
                });
            }
            var rightmostTickEndX = d3.max(chartGroup.selectAll(".angular-tick text")[0].map(function(d, i) {
                return d.getCTM().e + d.getBBox().width;
            }));
            legendContainer.attr({
                transform: "translate(" + [ radius + rightmostTickEndX, axisConfig.margin.top ] + ")"
            });
            var hasGeometry = svg.select("g.geometry-group").selectAll("g").size() > 0;
            var geometryContainer = svg.select("g.geometry-group").selectAll("g.geometry").data(data);
            geometryContainer.enter().append("g").attr({
                "class": function(d, i) {
                    return "geometry geometry" + i;
                }
            });
            geometryContainer.exit().remove();
            if (data[0] || hasGeometry) {
                var geometryConfigs = [];
                data.forEach(function(d, i) {
                    var geometryConfig = {};
                    geometryConfig.radialScale = radialScale;
                    geometryConfig.angularScale = angularScale;
                    geometryConfig.container = geometryContainer.filter(function(dB, iB) {
                        return iB === i;
                    });
                    geometryConfig.geometry = d.geometry;
                    geometryConfig.orientation = axisConfig.orientation;
                    geometryConfig.direction = axisConfig.direction;
                    geometryConfig.index = i;
                    geometryConfigs.push({
                        data: d,
                        geometryConfig: geometryConfig
                    });
                });
                var geometryConfigsGrouped = d3.nest().key(function(d, i) {
                    return typeof d.data.groupId !== "undefined" || "unstacked";
                }).entries(geometryConfigs);
                var geometryConfigsGrouped2 = [];
                geometryConfigsGrouped.forEach(function(d, i) {
                    if (d.key === "unstacked") {
                        geometryConfigsGrouped2 = geometryConfigsGrouped2.concat(d.values.map(function(d, i) {
                            return [ d ];
                        }));
                    } else {
                        geometryConfigsGrouped2.push(d.values);
                    }
                });
                geometryConfigsGrouped2.forEach(function(d, i) {
                    var geometry;
                    if (Array.isArray(d)) {
                        geometry = d[0].geometryConfig.geometry;
                    } else {
                        geometry = d.geometryConfig.geometry;
                    }
                    var finalGeometryConfig = d.map(function(dB, iB) {
                        return µ.util.deepExtend(µ[geometry].defaultConfig(), dB);
                    });
                    µ[geometry]().config(finalGeometryConfig)();
                });
            }
            var guides = svg.select(".guides-group");
            var tooltipContainer = svg.select(".tooltips-group");
            angularTooltip = angularTooltip || µ.tooltipPanel().config({
                container: tooltipContainer,
                fontSize: 8
            })();
            radialTooltip = radialTooltip || µ.tooltipPanel().config({
                container: tooltipContainer,
                fontSize: 8
            })();
            geometryTooltip = geometryTooltip || µ.tooltipPanel().config({
                container: tooltipContainer,
                fontSize: 8
            })();
            var angularValue, radialValue;
            if (!isOrdinal) {
                var angularGuideLine = guides.select("line").attr({
                    x1: 0,
                    y1: 0,
                    y2: 0
                }).style({
                    stroke: "grey",
                    "pointer-events": "none"
                });
                // chartGroup.on("mousemove.angular-guide", function(d, i) {
                //     var mouseAngle = µ.util.getMousePos(backgroundCircle).angle;
                //     angularGuideLine.attr({
                //         x2: -radius,
                //         transform: "rotate(" + mouseAngle + ")"
                //     }).style({
                //         opacity: .5
                //     });
                //     var angleWithOriginOffset = (mouseAngle + 180 + 360 - axisConfig.orientation) % 360;
                //     angularValue = angularScale.invert(angleWithOriginOffset);
                //     var pos = µ.util.convertToCartesian(radius + 12, mouseAngle + 180);
                //     angularTooltip.text(µ.util.round(angularValue)).move([ pos[0] + chartCenter[0], pos[1] + chartCenter[1] ]);
                // }).on("mouseout.angular-guide", function(d, i) {
                //     guides.select("line").style({
                //         opacity: 0
                //     });
                // });
            }
            if (axisConfig.radialAxis.visible !== false) {
                var angularGuideCircle = guides.select("circle").style({
                    stroke: "grey",
                    fill: "none"
                });
                // chartGroup.on("mousemove.radial-guide", function(d, i) {
                //     var r = µ.util.getMousePos(backgroundCircle).radius;
                //     angularGuideCircle.attr({
                //         r: r
                //     }).style({
                //         opacity: .5
                //     });
                //     radialValue = radialScale.invert(µ.util.getMousePos(backgroundCircle).radius);
                //     var pos = µ.util.convertToCartesian(r, axisConfig.radialAxis.orientation);
                //     radialTooltip.text(µ.util.round(radialValue)).move([ pos[0] + chartCenter[0], pos[1] + chartCenter[1] ]);
                // }).on("mouseout.radial-guide", function(d, i) {
                //     angularGuideCircle.style({
                //         opacity: 0
                //     });
                //     geometryTooltip.hide();
                //     angularTooltip.hide();
                //     radialTooltip.hide();
                // });
            }
            // svg.selectAll(".geometry-group .mark").on("mouseover.tooltip", function(d, i) {
            //     var el = d3.select(this);
            //     var color = el.style("fill");
            //     var newColor = "black";
            //     var opacity = el.style("opacity") || 1;
            //     el.attr({
            //         "data-opacity": opacity
            //     });
            //     if (color !== "none") {
            //         el.attr({
            //             "data-fill": color
            //         });
            //         newColor = d3.hsl(color).darker().toString();
            //         el.style({
            //             fill: newColor,
            //             opacity: 1
            //         });
            //         var textData = {
            //             t: µ.util.round(d[0]),
            //             r: µ.util.round(d[1])
            //         };
            //         if (isOrdinal) {
            //             textData.t = ticks[d[0]];
            //         }
            //         var text = "t: " + textData.t + ", r: " + textData.r;
            //         var bbox = this.getBoundingClientRect();
            //         var svgBBox = svg.node().getBoundingClientRect();
            //         var pos = [ bbox.left + bbox.width / 2 - centeringOffset[0] - svgBBox.left, bbox.top + bbox.height / 2 - centeringOffset[1] - svgBBox.top ];
            //         geometryTooltip.config({
            //             color: newColor
            //         }).text(text);
            //         geometryTooltip.move(pos);
            //     } else {
            //         color = el.style("stroke");
            //         el.attr({
            //             "data-stroke": color
            //         });
            //         newColor = d3.hsl(color).darker().toString();
            //         el.style({
            //             stroke: newColor,
            //             opacity: 1
            //         });
            //     }
            // }).on("mousemove.tooltip", function(d, i) {
            //     if (d3.event.which !== 0) {
            //         return false;
            //     }
            //     if (d3.select(this).attr("data-fill")) {
            //         geometryTooltip.show();
            //     }
            // }).on("mouseout.tooltip", function(d, i) {
            //     geometryTooltip.hide();
            //     var el = d3.select(this);
            //     var fillColor = el.attr("data-fill");
            //     if (fillColor) {
            //         el.style({
            //             fill: fillColor,
            //             opacity: el.attr("data-opacity")
            //         });
            //     } else {
            //         el.style({
            //             stroke: el.attr("data-stroke"),
            //             opacity: el.attr("data-opacity")
            //         });
            //     }
            // });
        });
        return exports;
    }
    exports.render = function(_container) {
        render(_container);
        return this;
    };
    exports.config = function(_x) {
        if (!arguments.length) {
            return config;
        }
        var xClone = µ.util.cloneJson(_x);
        xClone.data.forEach(function(d, i) {
            if (!config.data[i]) {
                config.data[i] = {};
            }
            µ.util.deepExtend(config.data[i], µ.Axis.defaultConfig().data[0]);
            µ.util.deepExtend(config.data[i], d);
        });
        µ.util.deepExtend(config.layout, µ.Axis.defaultConfig().layout);
        µ.util.deepExtend(config.layout, xClone.layout);
        return this;
    };
    exports.getLiveConfig = function() {
        return liveConfig;
    };
    exports.getinputConfig = function() {
        return inputConfig;
    };
    exports.radialScale = function(_x) {
        return radialScale;
    };
    exports.angularScale = function(_x) {
        return angularScale;
    };
    exports.svg = function() {
        return svg;
    };
    d3.rebind(exports, dispatch, "on");
    return exports;
};

µ.Axis.defaultConfig = function(d, i) {
    var config = {
        data: [ {
            t: [ 1, 2, 3, 4 ],
            r: [ 10, 11, 12, 13 ],
            name: "Line1",
            geometry: "LinePlot",
            color: null,
            strokeDash: "solid",
            strokeColor: null,
            strokeSize: "1",
            visibleInLegend: true,
            opacity: 1
        } ],
        layout: {
            defaultColorRange: d3.scale.category10().range(),
            title: null,
            height: 450,
            width: 500,
            margin: {
                top: 40,
                right: 40,
                bottom: 40,
                left: 40
            },
            font: {
                size: 12,
                color: "gray",
                outlineColor: "white",
                family: "Tahoma, sans-serif"
            },
            direction: "clockwise",
            orientation: 0,
            labelOffset: 10,
            radialAxis: {
                domain: null,
                orientation: -45,
                ticksSuffix: "",
                visible: true,
                gridLinesVisible: true,
                tickOrientation: "horizontal",
                rewriteTicks: null
            },
            angularAxis: {
                domain: [ 0, 360 ],
                ticksSuffix: "",
                visible: true,
                gridLinesVisible: true,
                labelsVisible: true,
                tickOrientation: "horizontal",
                rewriteTicks: null,
                ticksCount: null,
                ticksStep: null
            },
            minorTicks: 0,
            tickLength: null,
            tickColor: "silver",
            minorTickColor: "#eee",
            backgroundColor: "none",
            needsEndSpacing: null,
            showLegend: true,
            legend: {
                reverseOrder: false
            },
            opacity: 1
        }
    };
    return config;
};

µ.util = {};

µ.DATAEXTENT = "dataExtent";

µ.AREA = "AreaChart";

µ.LINE = "LinePlot";

µ.DOT = "DotPlot";

µ.BAR = "BarChart";

µ.PIE = "PieChart";

µ.util._override = function(_objA, _objB) {
    for (var x in _objA) {
        if (x in _objB) {
            _objB[x] = _objA[x];
        }
    }
};

µ.util._extend = function(_objA, _objB) {
    for (var x in _objA) {
        _objB[x] = _objA[x];
    }
};

µ.util._rndSnd = function() {
    return Math.random() * 2 - 1 + (Math.random() * 2 - 1) + (Math.random() * 2 - 1);
};

µ.util.dataFromEquation2 = function(_equation, _step) {
    var step = _step || 6;
    var data = d3.range(0, 360 + step, step).map(function(deg, index) {
        var theta = deg * Math.PI / 180;
        var radius = _equation(theta);
        return [ deg, radius ];
    });
    return data;
};

µ.util.dataFromEquation = function(_equation, _step, _name) {
    var step = _step || 6;
    var t = [], r = [];
    d3.range(0, 360 + step, step).forEach(function(deg, index) {
        var theta = deg * Math.PI / 180;
        var radius = _equation(theta);
        t.push(deg);
        r.push(radius);
    });
    var result = {
        t: t,
        r: r
    };
    if (_name) {
        result.name = _name;
    }
    return result;
};

µ.util.ensureArray = function(_val, _count) {
    if (typeof _val === "undefined") {
        return null;
    }
    var arr = [].concat(_val);
    return d3.range(_count).map(function(d, i) {
        return arr[i] || arr[0];
    });
};

µ.util.fillArrays = function(_obj, _valueNames, _count) {
    _valueNames.forEach(function(d, i) {
        _obj[d] = µ.util.ensureArray(_obj[d], _count);
    });
    return _obj;
};

µ.util.cloneJson = function(json) {
    return JSON.parse(JSON.stringify(json));
};

µ.util.deepExtend = function(destination, source) {
    for (var property in source) {
        if (source[property] && source[property].constructor && source[property].constructor === Object) {
            destination[property] = destination[property] || {};
            arguments.callee(destination[property], source[property]);
        } else {
            destination[property] = source[property];
        }
    }
    return destination;
};

µ.util.validateKeys = function(obj, keys) {
    if (typeof keys === "string") {
        keys = keys.split(".");
    }
    var next = keys.shift();
    return obj[next] && (!keys.length || objHasKeys(obj[next], keys));
};

µ.util.sumArrays = function(a, b) {
    return d3.zip(a, b).map(function(d, i) {
        return d3.sum(d);
    });
};

µ.util.arrayLast = function(a) {
    return a[a.length - 1];
};

µ.util.arrayEqual = function(a, b) {
    var i = Math.max(a.length, b.length, 1);
    while (i-- >= 0 && a[i] === b[i]) ;
    return i === -2;
};

µ.util.flattenArray = function(arr) {
    var r = [];
    while (!µ.util.arrayEqual(r, arr)) {
        r = arr;
        arr = [].concat.apply([], arr);
    }
    return arr;
};

µ.util.deduplicate = function(arr) {
    return arr.filter(function(v, i, a) {
        return a.indexOf(v) === i;
    });
};

µ.util.convertToCartesian = function(radius, theta) {
    var thetaRadians = theta * Math.PI / 180;
    var x = radius * Math.cos(thetaRadians);
    var y = radius * Math.sin(thetaRadians);
    return [ x, y ];
};

µ.util.round = function(_value, _digits) {
    var digits = _digits || 2;
    var mult = Math.pow(10, digits);
    return Math.round(_value * mult) / mult;
};

µ.util.getMousePos = function(_referenceElement) {
    var mousePos = d3.mouse(_referenceElement.node());
    var mouseX = mousePos[0];
    var mouseY = mousePos[1];
    var mouse = {};
    mouse.x = mouseX;
    mouse.y = mouseY;
    mouse.pos = mousePos;
    mouse.angle = (Math.atan2(mouseY, mouseX) + Math.PI) * 180 / Math.PI;
    mouse.radius = Math.sqrt(mouseX * mouseX + mouseY * mouseY);
    return mouse;
};

µ.util.duplicatesCount = function(arr) {
    var uniques = {}, val;
    var dups = {};
    for (var i = 0, len = arr.length; i < len; i++) {
        val = arr[i];
        if (val in uniques) {
            uniques[val]++;
            dups[val] = uniques[val];
        } else {
            uniques[val] = 1;
        }
    }
    return dups;
};

µ.util.duplicates = function(arr) {
    return Object.keys(µ.util.duplicatesCount(arr));
};

µ.util.translator = function(obj, sourceBranch, targetBranch, reverse) {
    if (reverse) {
        var targetBranchCopy = targetBranch.slice();
        targetBranch = sourceBranch;
        sourceBranch = targetBranchCopy;
    }
    var value = sourceBranch.reduce(function(previousValue, currentValue) {
        if (typeof previousValue !== "undefined") {
            return previousValue[currentValue];
        }
    }, obj);
    if (typeof value === "undefined") {
        return;
    }
    sourceBranch.reduce(function(previousValue, currentValue, index) {
        if (typeof previousValue === "undefined") {
            return;
        }
        if (index === sourceBranch.length - 1) {
            delete previousValue[currentValue];
        }
        return previousValue[currentValue];
    }, obj);
    targetBranch.reduce(function(previousValue, currentValue, index) {
        if (typeof previousValue[currentValue] === "undefined") {
            previousValue[currentValue] = {};
        }
        if (index === targetBranch.length - 1) {
            previousValue[currentValue] = value;
        }
        return previousValue[currentValue];
    }, obj);
};

µ.PolyChart = function module() {
    var config = [ µ.PolyChart.defaultConfig() ];
    var dispatch = d3.dispatch("hover");
    var dashArray = {
        solid: "none",
        dash: [ 5, 2 ],
        dot: [ 2, 5 ]
    };
    var colorScale;
    function exports() {
        var geometryConfig = config[0].geometryConfig;
        var container = geometryConfig.container;
        if (typeof container === "string") {
            container = d3.select(container);
        }
        container.datum(config).each(function(_config, _index) {
            var isStack = !!_config[0].data.yStack;
            var data = _config.map(function(d, i) {
                if (isStack) {
                    return d3.zip(d.data.t[0], d.data.r[0], d.data.yStack[0]);
                } else {
                    return d3.zip(d.data.t[0], d.data.r[0]);
                }
            });
            var angularScale = geometryConfig.angularScale;
            var domainMin = geometryConfig.radialScale.domain()[0];
            var generator = {};
            generator.bar = function(d, i, pI) {
                var dataConfig = _config[pI].data;
                var h = geometryConfig.radialScale(d[1]) - geometryConfig.radialScale(0);
                var stackTop = geometryConfig.radialScale(d[2] || 0);
                var w = dataConfig.barWidth;
                d3.select(this).attr({
                    "class": "mark bar",
                    d: "M" + [ [ h + stackTop, -w / 2 ], [ h + stackTop, w / 2 ], [ stackTop, w / 2 ], [ stackTop, -w / 2 ] ].join("L") + "Z",
                    transform: function(d, i) {
                        return "rotate(" + (geometryConfig.orientation + angularScale(d[0])) + ")";
                    }
                });
            };
            generator.dot = function(d, i, pI) {
                var stackedData = d[2] ? [ d[0], d[1] + d[2] ] : d;
                var symbol = d3.svg.symbol().size(_config[pI].data.dotSize).type(_config[pI].data.dotType)(d, i);
                d3.select(this).attr({
                    "class": "mark dot",
                    d: symbol,
                    transform: function(d, i) {
                        var coord = convertToCartesian(getPolarCoordinates(stackedData));
                        return "translate(" + [ coord.x, coord.y ] + ")";
                    }
                });
            };
            var line = d3.svg.line.radial().interpolate(_config[0].data.lineInterpolation).radius(function(d) {
                return geometryConfig.radialScale(d[1]);
            }).angle(function(d) {
                return geometryConfig.angularScale(d[0]) * Math.PI / 180;
            });
            generator.line = function(d, i, pI) {
                var lineData = d[2] ? data[pI].map(function(d, i) {
                    return [ d[0], d[1] + d[2] ];
                }) : data[pI];
                d3.select(this).each(generator.dot).style({
                    opacity: function(dB, iB) {
                        return +_config[pI].data.dotVisible;
                    },
                    fill: markStyle.stroke(d, i, pI)
                }).attr({
                    "class": "mark dot"
                });
                if (i > 0) {
                    return;
                }
                var lineSelection = d3.select(this.parentNode).selectAll("path.line").data([ 0 ]);
                lineSelection.enter().insert("path");
                lineSelection.attr({
                    "class": "line",
                    d: line(lineData),
                    transform: function(dB, iB) {
                        return "rotate(" + (geometryConfig.orientation + 90) + ")";
                    },
                    "pointer-events": "none"
                }).style({
                    fill: function(dB, iB) {
                        return markStyle.fill(d, i, pI);
                    },
                    "fill-opacity": 0,
                    stroke: function(dB, iB) {
                        return markStyle.stroke(d, i, pI);
                    },
                    "stroke-width": function(dB, iB) {
                        return markStyle["stroke-width"](d, i, pI);
                    },
                    "stroke-dasharray": function(dB, iB) {
                        return markStyle["stroke-dasharray"](d, i, pI);
                    },
                    opacity: function(dB, iB) {
                        return markStyle.opacity(d, i, pI);
                    },
                    display: function(dB, iB) {
                        return markStyle.display(d, i, pI);
                    }
                });
            };
            var angularRange = geometryConfig.angularScale.range();
            var triangleAngle = Math.abs(angularRange[1] - angularRange[0]) / data[0].length * Math.PI / 180;
            var arc = d3.svg.arc().startAngle(function(d) {
                return -triangleAngle / 2;
            }).endAngle(function(d) {
                return triangleAngle / 2;
            }).innerRadius(function(d) {
                return geometryConfig.radialScale(domainMin + (d[2] || 0));
            }).outerRadius(function(d) {
                return geometryConfig.radialScale(domainMin + (d[2] || 0)) + geometryConfig.radialScale(d[1]);
            });
            generator.arc = function(d, i, pI) {
                d3.select(this).attr({
                    "class": "mark arc",
                    d: arc,
                    transform: function(d, i) {
                        return "rotate(" + (geometryConfig.orientation + angularScale(d[0]) + 90) + ")";
                    }
                });
            };
            var pieArc = d3.svg.arc().outerRadius(geometryConfig.radialScale.range()[1]);
            var pie = d3.layout.pie().value(function(d) {
                return d[1];
            });
            var pieData = pie(data[0]);
            generator.pie = function(d, i, pI) {
                d3.select(this).attr({
                    "class": "mark arc",
                    d: pieArc(pieData[i], i)
                });
            };
            var markStyle = {
                fill: function(d, i, pI) {
                    return _config[pI].data.color;
                },
                stroke: function(d, i, pI) {
                    return _config[pI].data.strokeColor;
                },
                "stroke-width": function(d, i, pI) {
                    return _config[pI].data.strokeSize + "px";
                },
                "stroke-dasharray": function(d, i, pI) {
                    return dashArray[_config[pI].data.strokeDash];
                },
                opacity: function(d, i, pI) {
                    return _config[pI].data.opacity;
                },
                display: function(d, i, pI) {
                    return typeof _config[pI].data.visible === "undefined" || _config[pI].data.visible ? "block" : "none";
                }
            };
            var geometryLayer = d3.select(this).selectAll("g.layer").data(data);
            geometryLayer.enter().append("g").attr({
                "class": "layer"
            });
            var geometry = geometryLayer.selectAll("path.mark").data(function(d, i) {
                return d;
            });
            geometry.enter().append("path").attr({
                "class": "mark"
            });
            geometry.style(markStyle).each(generator[geometryConfig.geometryType]);
            geometry.exit().remove();
            geometryLayer.exit().remove();
            function getPolarCoordinates(d, i) {
                var r = geometryConfig.radialScale(d[1]);
                var t = (geometryConfig.angularScale(d[0]) + geometryConfig.orientation) * Math.PI / 180;
                return {
                    r: r,
                    t: t
                };
            }
            function convertToCartesian(polarCoordinates) {
                var x = polarCoordinates.r * Math.cos(polarCoordinates.t);
                var y = polarCoordinates.r * Math.sin(polarCoordinates.t);
                return {
                    x: x,
                    y: y
                };
            }
        });
    }
    exports.config = function(_x) {
        if (!arguments.length) {
            return config;
        }
        _x.forEach(function(d, i) {
            if (!config[i]) {
                config[i] = {};
            }
            µ.util.deepExtend(config[i], µ.PolyChart.defaultConfig());
            µ.util.deepExtend(config[i], d);
        });
        return this;
    };
    exports.getColorScale = function() {
        return colorScale;
    };
    d3.rebind(exports, dispatch, "on");
    return exports;
};

µ.PolyChart.defaultConfig = function() {
    var config = {
        data: {
            name: "geom1",
            t: [ [ 1, 2, 3, 4 ] ],
            r: [ [ 1, 2, 3, 4 ] ],
            dotType: "circle",
            dotSize: 64,
            dotVisible: false,
            barWidth: 20,
            color: "#ffa500",
            strokeSize: 1,
            strokeColor: "silver",
            strokeDash: "solid",
            opacity: 1,
            index: 0,
            visible: true,
            visibleInLegend: true
        },
        geometryConfig: {
            geometry: "LinePlot",
            geometryType: "arc",
            direction: "clockwise",
            orientation: 0,
            container: "body",
            radialScale: null,
            angularScale: null,
            colorScale: d3.scale.category20()
        }
    };
    return config;
};

µ.BarChart = function module() {
    return µ.PolyChart();
};

µ.BarChart.defaultConfig = function() {
    var config = {
        geometryConfig: {
            geometryType: "bar"
        }
    };
    return config;
};

µ.AreaChart = function module() {
    return µ.PolyChart();
};

µ.AreaChart.defaultConfig = function() {
    var config = {
        geometryConfig: {
            geometryType: "arc"
        }
    };
    return config;
};

µ.DotPlot = function module() {
    return µ.PolyChart();
};

µ.DotPlot.defaultConfig = function() {
    var config = {
        geometryConfig: {
            geometryType: "dot",
            dotType: "circle"
        }
    };
    return config;
};

µ.LinePlot = function module() {
    return µ.PolyChart();
};

µ.LinePlot.defaultConfig = function() {
    var config = {
        geometryConfig: {
            geometryType: "line"
        }
    };
    return config;
};

µ.Legend = function module() {
    var config = µ.Legend.defaultConfig();
    var dispatch = d3.dispatch("hover");
    function exports() {
        var legendConfig = config.legendConfig;
        var flattenData = config.data.map(function(d, i) {
            return [].concat(d).map(function(dB, iB) {
                var element = µ.util.deepExtend({}, legendConfig.elements[i]);
                element.name = dB;
                element.color = [].concat(legendConfig.elements[i].color)[iB];
                return element;
            });
        });
        var data = d3.merge(flattenData);
        data = data.filter(function(d, i) {
            return legendConfig.elements[i] && (legendConfig.elements[i].visibleInLegend || typeof legendConfig.elements[i].visibleInLegend === "undefined");
        });
        if (legendConfig.reverseOrder) {
            data = data.reverse();
        }
        var container = legendConfig.container;
        if (typeof container === "string" || container.nodeName) {
            container = d3.select(container);
        }
        var colors = data.map(function(d, i) {
            return d.color;
        });
        var lineHeight = legendConfig.fontSize;
        var isContinuous = legendConfig.isContinuous === null ? typeof data[0] === "number" : legendConfig.isContinuous;
        var height = isContinuous ? legendConfig.height : lineHeight * data.length;
        var legendContainerGroup = container.classed("legend-group", true);
        var svg = legendContainerGroup.selectAll("svg").data([ 0 ]);
        var svgEnter = svg.enter().append("svg").attr({
            width: 300,
            height: height + lineHeight,
            xmlns: "http://www.w3.org/2000/svg",
            "xmlns:xlink": "http://www.w3.org/1999/xlink",
            version: "1.1"
        });
        svgEnter.append("g").classed("legend-axis", true);
        svgEnter.append("g").classed("legend-marks", true);
        var dataNumbered = d3.range(data.length);
        var colorScale = d3.scale[isContinuous ? "linear" : "ordinal"]().domain(dataNumbered).range(colors);
        var dataScale = d3.scale[isContinuous ? "linear" : "ordinal"]().domain(dataNumbered)[isContinuous ? "range" : "rangePoints"]([ 0, height ]);
        var shapeGenerator = function(_type, _size) {
            var squareSize = _size * 3;
            if (_type === "line") {
                return "M" + [ [ -_size / 2, -_size / 12 ], [ _size / 2, -_size / 12 ], [ _size / 2, _size / 12 ], [ -_size / 2, _size / 12 ] ] + "Z";
            } else if (d3.svg.symbolTypes.indexOf(_type) !== -1) {
                return d3.svg.symbol().type(_type).size(squareSize)();
            } else {
                return d3.svg.symbol().type("square").size(squareSize)();
            }
        };
        if (isContinuous) {
            var gradient = svg.select(".legend-marks").append("defs").append("linearGradient").attr({
                id: "grad1",
                x1: "0%",
                y1: "0%",
                x2: "0%",
                y2: "100%"
            }).selectAll("stop").data(colors);
            gradient.enter().append("stop");
            gradient.attr({
                offset: function(d, i) {
                    return i / (colors.length - 1) * 100 + "%";
                }
            }).style({
                "stop-color": function(d, i) {
                    return d;
                }
            });
            svg.append("rect").classed("legend-mark", true).attr({
                height: legendConfig.height,
                width: legendConfig.colorBandWidth,
                fill: "url(#grad1)"
            });
        } else {
            var legendElement = svg.select(".legend-marks").selectAll("path.legend-mark").data(data);
            legendElement.enter().append("path").classed("legend-mark", true);
            legendElement.attr({
                transform: function(d, i) {
                    return "translate(" + [ lineHeight / 2, dataScale(i) + lineHeight / 2 ] + ")";
                },
                d: function(d, i) {
                    var symbolType = d.symbol;
                    return shapeGenerator(symbolType, lineHeight);
                },
                fill: function(d, i) {
                    return colorScale(i);
                }
            });
            legendElement.exit().remove();
        }
        var legendAxis = d3.svg.axis().scale(dataScale).orient("right");
        var axis = svg.select("g.legend-axis").attr({
            transform: "translate(" + [ isContinuous ? legendConfig.colorBandWidth : lineHeight, lineHeight / 2 ] + ")"
        }).call(legendAxis);
        axis.selectAll(".domain").style({
            fill: "none",
            stroke: "none"
        });
        axis.selectAll("line").style({
            fill: "none",
            stroke: isContinuous ? legendConfig.textColor : "none"
        });
        axis.selectAll("text").style({
            fill: legendConfig.textColor,
            "font-size": legendConfig.fontSize
        }).text(function(d, i) {
            return data[i].name;
        });
        return exports;
    }
    exports.config = function(_x) {
        if (!arguments.length) {
            return config;
        }
        µ.util.deepExtend(config, _x);
        return this;
    };
    d3.rebind(exports, dispatch, "on");
    return exports;
};

µ.Legend.defaultConfig = function(d, i) {
    var config = {
        data: [ "a", "b", "c" ],
        legendConfig: {
            elements: [ {
                symbol: "line",
                color: "red"
            }, {
                symbol: "square",
                color: "yellow"
            }, {
                symbol: "diamond",
                color: "limegreen"
            } ],
            height: 150,
            colorBandWidth: 30,
            fontSize: 12,
            container: "body",
            isContinuous: null,
            textColor: "grey",
            reverseOrder: false
        }
    };
    return config;
};

µ.tooltipPanel = function() {
    var tooltipEl, tooltipTextEl, backgroundEl;
    var config = {
        container: null,
        hasTick: false,
        fontSize: 12,
        color: "white",
        padding: 5
    };
    var id = "tooltip-" + µ.tooltipPanel.uid++;
    var tickSize = 10;
    var exports = function() {
        tooltipEl = config.container.selectAll("g." + id).data([ 0 ]);
        var tooltipEnter = tooltipEl.enter().append("g").classed(id, true).style({
            "pointer-events": "none",
            display: "none"
        });
        backgroundEl = tooltipEnter.append("path").style({
            fill: "white",
            "fill-opacity": .9
        }).attr({
            d: "M0 0"
        });
        tooltipTextEl = tooltipEnter.append("text").attr({
            dx: config.padding + tickSize,
            dy: +config.fontSize * .3
        });
        return exports;
    };
    exports.text = function(_text) {
        var l = d3.hsl(config.color).l;
        var strokeColor = l >= .5 ? "#aaa" : "white";
        var fillColor = l >= .5 ? "black" : "white";
        var text = _text || "";
        tooltipTextEl.style({
            fill: fillColor,
            "font-size": config.fontSize + "px"
        }).text(text);
        var padding = config.padding;
        var bbox = tooltipTextEl.node().getBBox();
        var boxStyle = {
            fill: config.color,
            stroke: strokeColor,
            "stroke-width": "2px"
        };
        var backGroundW = bbox.width + padding * 2 + tickSize;
        var backGroundH = bbox.height + padding * 2;
        backgroundEl.attr({
            d: "M" + [ [ tickSize, -backGroundH / 2 ], [ tickSize, -backGroundH / 4 ], [ config.hasTick ? 0 : tickSize, 0 ], [ tickSize, backGroundH / 4 ], [ tickSize, backGroundH / 2 ], [ backGroundW, backGroundH / 2 ], [ backGroundW, -backGroundH / 2 ] ].join("L") + "Z"
        }).style(boxStyle);
        tooltipEl.attr({
            transform: "translate(" + [ tickSize, -backGroundH / 2 + padding * 2 ] + ")"
        });
        tooltipEl.style({
            display: "block"
        });
        return exports;
    };
    exports.move = function(_pos) {
        if (!tooltipEl) {
            return;
        }
        tooltipEl.attr({
            transform: "translate(" + [ _pos[0], _pos[1] ] + ")"
        }).style({
            display: "block"
        });
        return exports;
    };
    exports.hide = function() {
        if (!tooltipEl) {
            return;
        }
        tooltipEl.style({
            display: "none"
        });
        return exports;
    };
    exports.show = function() {
        if (!tooltipEl) {
            return;
        }
        tooltipEl.style({
            display: "block"
        });
        return exports;
    };
    exports.config = function(_x) {
        µ.util.deepExtend(config, _x);
        return exports;
    };
    return exports;
};

µ.tooltipPanel.uid = 1;

µ.adapter = {};

µ.adapter.plotly = function module() {
    var exports = {};
    exports.convert = function(_inputConfig, reverse) {
        var outputConfig = {};
        if (_inputConfig.data) {
            outputConfig.data = _inputConfig.data.map(function(d, i) {
                var r = µ.util.deepExtend({}, d);
                var toTranslate = [ [ r, [ "marker", "color" ], [ "color" ] ], [ r, [ "marker", "opacity" ], [ "opacity" ] ], [ r, [ "marker", "line", "color" ], [ "strokeColor" ] ], [ r, [ "marker", "line", "dash" ], [ "strokeDash" ] ], [ r, [ "marker", "line", "width" ], [ "strokeSize" ] ], [ r, [ "marker", "symbol" ], [ "dotType" ] ], [ r, [ "marker", "size" ], [ "dotSize" ] ], [ r, [ "marker", "barWidth" ], [ "barWidth" ] ], [ r, [ "line", "interpolation" ], [ "lineInterpolation" ] ], [ r, [ "showlegend" ], [ "visibleInLegend" ] ] ];
                toTranslate.forEach(function(d, i) {
                    µ.util.translator.apply(null, d.concat(reverse));
                });
                if (!reverse) {
                    delete r.marker;
                }
                if (reverse) {
                    delete r.groupId;
                }
                if (!reverse) {
                    if (r.type === "scatter") {
                        if (r.mode === "lines") {
                            r.geometry = "LinePlot";
                        } else if (r.mode === "markers") {
                            r.geometry = "DotPlot";
                        } else if (r.mode === "lines+markers") {
                            r.geometry = "LinePlot";
                            r.dotVisible = true;
                        }
                    } else if (r.type === "area") {
                        r.geometry = "AreaChart";
                    } else if (r.type === "bar") {
                        r.geometry = "BarChart";
                    }
                    delete r.mode;
                    delete r.type;
                } else {
                    if (r.geometry === "LinePlot") {
                        r.type = "scatter";
                        if (r.dotVisible === true) {
                            delete r.dotVisible;
                            r.mode = "lines+markers";
                        } else {
                            r.mode = "lines";
                        }
                    } else if (r.geometry === "DotPlot") {
                        r.type = "scatter";
                        r.mode = "markers";
                    } else if (r.geometry === "AreaChart") {
                        r.type = "area";
                    } else if (r.geometry === "BarChart") {
                        r.type = "bar";
                    }
                    delete r.geometry;
                }
                return r;
            });
            if (!reverse && _inputConfig.layout && _inputConfig.layout.barmode === "stack") {
                var duplicates = µ.util.duplicates(outputConfig.data.map(function(d, i) {
                    return d.geometry;
                }));
                outputConfig.data.forEach(function(d, i) {
                    var idx = duplicates.indexOf(d.geometry);
                    if (idx !== -1) {
                        outputConfig.data[i].groupId = idx;
                    }
                });
            }
        }
        if (_inputConfig.layout) {
            var r = µ.util.deepExtend({}, _inputConfig.layout);
            var toTranslate = [ [ r, [ "plot_bgcolor" ], [ "backgroundColor" ] ], [ r, [ "showlegend" ], [ "showLegend" ] ], [ r, [ "radialaxis" ], [ "radialAxis" ] ], [ r, [ "angularaxis" ], [ "angularAxis" ] ], [ r.angularaxis, [ "showline" ], [ "gridLinesVisible" ] ], [ r.angularaxis, [ "showticklabels" ], [ "labelsVisible" ] ], [ r.angularaxis, [ "nticks" ], [ "ticksCount" ] ], [ r.angularaxis, [ "tickorientation" ], [ "tickOrientation" ] ], [ r.angularaxis, [ "ticksuffix" ], [ "ticksSuffix" ] ], [ r.angularaxis, [ "range" ], [ "domain" ] ], [ r.angularaxis, [ "endpadding" ], [ "endPadding" ] ], [ r.radialaxis, [ "showline" ], [ "gridLinesVisible" ] ], [ r.radialaxis, [ "tickorientation" ], [ "tickOrientation" ] ], [ r.radialaxis, [ "ticksuffix" ], [ "ticksSuffix" ] ], [ r.radialaxis, [ "range" ], [ "domain" ] ], [ r.angularAxis, [ "showline" ], [ "gridLinesVisible" ] ], [ r.angularAxis, [ "showticklabels" ], [ "labelsVisible" ] ], [ r.angularAxis, [ "nticks" ], [ "ticksCount" ] ], [ r.angularAxis, [ "tickorientation" ], [ "tickOrientation" ] ], [ r.angularAxis, [ "ticksuffix" ], [ "ticksSuffix" ] ], [ r.angularAxis, [ "range" ], [ "domain" ] ], [ r.angularAxis, [ "endpadding" ], [ "endPadding" ] ], [ r.radialAxis, [ "showline" ], [ "gridLinesVisible" ] ], [ r.radialAxis, [ "tickorientation" ], [ "tickOrientation" ] ], [ r.radialAxis, [ "ticksuffix" ], [ "ticksSuffix" ] ], [ r.radialAxis, [ "range" ], [ "domain" ] ], [ r.font, [ "outlinecolor" ], [ "outlineColor" ] ], [ r.legend, [ "traceorder" ], [ "reverseOrder" ] ], [ r, [ "labeloffset" ], [ "labelOffset" ] ], [ r, [ "defaultcolorrange" ], [ "defaultColorRange" ] ] ];
            toTranslate.forEach(function(d, i) {
                µ.util.translator.apply(null, d.concat(reverse));
            });
            if (!reverse) {
                if (r.angularAxis && typeof r.angularAxis.ticklen !== "undefined") {
                    r.tickLength = r.angularAxis.ticklen;
                }
                if (r.angularAxis && typeof r.angularAxis.tickcolor !== "undefined") {
                    r.tickColor = r.angularAxis.tickcolor;
                }
            } else {
                if (typeof r.tickLength !== "undefined") {
                    r.angularaxis.ticklen = r.tickLength;
                    delete r.tickLength;
                }
                if (r.tickColor) {
                    r.angularaxis.tickcolor = r.tickColor;
                    delete r.tickColor;
                }
            }
            if (r.legend && typeof r.legend.reverseOrder !== "boolean") {
                r.legend.reverseOrder = r.legend.reverseOrder !== "normal";
            }
            if (r.legend && typeof r.legend.traceorder === "boolean") {
                r.legend.traceorder = r.legend.traceorder ? "reversed" : "normal";
                delete r.legend.reverseOrder;
            }
            if (r.margin && typeof r.margin.t !== "undefined") {
                var source = [ "t", "r", "b", "l", "pad" ];
                var target = [ "top", "right", "bottom", "left", "pad" ];
                var margin = {};
                d3.entries(r.margin).forEach(function(dB, iB) {
                    margin[target[source.indexOf(dB.key)]] = dB.value;
                });
                r.margin = margin;
            }
            if (reverse) {
                delete r.needsEndSpacing;
                delete r.minorTickColor;
                delete r.minorTicks;
                if (r.angularaxis) {
                    delete r.angularaxis.ticksCount;
                    delete r.angularaxis.ticksCount;
                    delete r.angularaxis.ticksStep;
                    delete r.angularaxis.rewriteTicks;
                    delete r.angularaxis.nticks;
                }
                if (r.radialaxis) {
                    delete r.radialaxis.ticksCount;
                    delete r.radialaxis.ticksCount;
                    delete r.radialaxis.ticksStep;
                    delete r.radialaxis.rewriteTicks;
                    delete r.radialaxis.nticks;
                }
            }
            outputConfig.layout = r;
        }
        return outputConfig;
    };
    return exports;
};