<commit_before>nv.models.OTRSmultiBarChart = function() {
    "use strict";

    //============================================================
    // Public Variables with Default Settings
    //------------------------------------------------------------

    var multibar = nv.models.multiBar()
        , xAxis = nv.models.axis()
        , yAxis = nv.models.axis()
        , legend = nv.models.legend()
        , controls = nv.models.legend()
        ;

    var margin = {top: 30, right: 20, bottom: 50, left: 60}
        , width = null
        , height = null
        , color = nv.utils.defaultColor()
        , showControls = true
        , controlLabels = {}
        , showLegend = true
        , showXAxis = true
        , showYAxis = true
        , rightAlignYAxis = false
        , reduceXTicks = true // if false a tick will show for every data point
        , staggerLabels = false
        , rotateLabels = 0
        , tooltips = true
        , tooltip = function(key, x, y, e, graph) {
            return '
' + key + '
' +
                '
' +  y + ' on ' + x + '
'
        }
        , x //can be accessed via chart.xScale()
        , y //can be accessed via chart.yScale()
        , state = nv.utils.state()
        , defaultState = null
        , noData = "No Data Available."
        , dispatch = d3.dispatch('tooltipShow', 'tooltipHide', 'stateChange', 'changeState', 'renderEnd')
        , controlWidth = function() { return showControls ? 180 : 0 }
        , duration = 250
        ;

    state.stacked = false // DEPRECATED Maintained for backward compatibility

    multibar
        .stacked(false)
    ;
    xAxis
        .orient('bottom')
        .tickPadding(7)
        .highlightZero(true)
        .showMaxMin(false)
        .tickFormat(function(d) { return d })
    ;
    yAxis
        .orient((rightAlignYAxis) ? 'right' : 'left')
        .tickFormat(d3.format(',.1f'))
    ;

    controls.updateState(false);

    //============================================================
    // Private Variables
    //------------------------------------------------------------

    var renderWatch = nv.utils.renderWatch(dispatch);
    var stacked = false;

    var showTooltip = function(e, offsetElement) {
        var left = e.pos[0] + ( offsetElement.offsetLeft || 0 ),
            top = e.pos[1] + ( offsetElement.offsetTop || 0),
            x = xAxis.tickFormat()(multibar.x()(e.point, e.pointIndex)),
            y = yAxis.tickFormat()(multibar.y()(e.point, e.pointIndex)),
            content = tooltip(e.series.key, x, y, e, chart);

// ---
// OTRS
// ---
      // because it could happen that x headings occurr multiple
      // times (such as Thu 18 for two different months), we
      // need to make sure the custom label which has been added to
      // the data is removed properly for displaying
      if ( content.match(/__LABEL_START__\d+__LABEL_END__/) ) {
        content = content.replace(/__LABEL_START__\d+__LABEL_END__/, '');
      }
// ---

        nv.tooltip.show([left, top], content, e.value < 0 ? 'n' : 's', null, offsetElement);
    };

    var stateGetter = function(data) {
        return function(){
            return {
                active: data.map(function(d) { return !d.disabled }),
                stacked: stacked
            };
        }
    };

    var stateSetter = function(data) {
        return function(state) {
            if (state.stacked !== undefined)
                stacked = state.stacked;
            if (state.active !== undefined)
                data.forEach(function(series,i) {
                    series.disabled = !state.active[i];
                });
        }
    };

    function chart(selection) {
        renderWatch.reset();
        renderWatch.models(multibar);
        if (showXAxis) renderWatch.models(xAxis);
        if (showYAxis) renderWatch.models(yAxis);

        selection.each(function(data) {
            var container = d3.select(this),
                that = this;
            nv.utils.initSVG(container);
            var availableWidth = (width  || parseInt(container.style('width')) || 960)
                    - margin.left - margin.right,
                availableHeight = (height || parseInt(container.style('height')) || 400)
                    - margin.top - margin.bottom;

            chart.update = function() {
                if (duration === 0)
                    container.call(chart);
                else
                    container.transition()
                        .duration(duration)
                        .call(chart);
            };
            chart.container = this;

            state
                .setter(stateSetter(data), chart.update)
                .getter(stateGetter(data))
                .update();

            // DEPRECATED set state.disableddisabled
            state.disabled = data.map(function(d) { return !!d.disabled });

            if (!defaultState) {
                var key;
                defaultState = {};
                for (key in state) {
                    if (state[key] instanceof Array)
                        defaultState[key] = state[key].slice(0);
                    else
                        defaultState[key] = state[key];
                }
            }

            // Display noData message if there's nothing to show.
            if (!data || !data.length || !data.filter(function(d) { return d.values.length }).length) {
                var noDataText = container.selectAll('.nv-noData').data([noData]);

                noDataText.enter().append('text')
                    .attr('class', 'nvd3 nv-noData')
                    .attr('dy', '-.7em')
                    .style('text-anchor', 'middle');

                noDataText
                    .attr('x', margin.left + availableWidth / 2)
                    .attr('y', margin.top + availableHeight / 2)
                    .text(function(d) { return d });

                return chart;
            } else {
                container.selectAll('.nv-noData').remove();
            }

            // Setup Scales
            x = multibar.xScale();
            y = multibar.yScale();

            // Setup containers and skeleton of chart
            var wrap = container.selectAll('g.nv-wrap.nv-multiBarWithLegend').data([data]);
            var gEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-multiBarWithLegend').append('g');
            var g = wrap.select('g');

            gEnter.append('g').attr('class', 'nv-x nv-axis');
            gEnter.append('g').attr('class', 'nv-y nv-axis');
            gEnter.append('g').attr('class', 'nv-barsWrap');
            gEnter.append('g').attr('class', 'nv-legendWrap');
            gEnter.append('g').attr('class', 'nv-controlsWrap');

            // Legend
            if (showLegend) {
                legend.width(availableWidth - controlWidth());

                if (multibar.barColor())
                    data.forEach(function(series,i) {
                        series.color = d3.rgb('#ccc').darker(i * 1.5).toString();
                    });

                g.select('.nv-legendWrap')
                    .datum(data)
                    .call(legend);

                if ( margin.top != legend.height()) {
                    margin.top = legend.height();
                    availableHeight = (height || parseInt(container.style('height')) || 400)
                        - margin.top - margin.bottom;
                }

                g.select('.nv-legendWrap')
                    .attr('transform', 'translate(' + controlWidth() + ',' + (-margin.top) +')');
            }

            // Controls
            if (showControls) {
                var controlsData = [
                    { key: controlLabels.grouped || 'Grouped', disabled: multibar.stacked() },
                    { key: controlLabels.stacked || 'Stacked', disabled: !multibar.stacked() }
                ];

                controls.width(controlWidth()).color(['#444', '#444', '#444']);
                g.select('.nv-controlsWrap')
                    .datum(controlsData)
                    .attr('transform', 'translate(0,' + (-margin.top) +')')
                    .call(controls);
            }

            wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
            if (rightAlignYAxis) {
                g.select(".nv-y.nv-axis")
                    .attr("transform", "translate(" + availableWidth + ",0)");
            }

            // Main Chart Component(s)
            multibar
                .disabled(data.map(function(series) { return series.disabled }))
                .width(availableWidth)
                .height(availableHeight)
                .color(data.map(function(d,i) {
                    return d.color || color(d, i);
                }).filter(function(d,i) { return !data[i].disabled }));

            var barsWrap = g.select('.nv-barsWrap')
                .datum(data.filter(function(d) { return !d.disabled }));

            barsWrap.call(multibar);

            // Setup Axes
            if (showXAxis) {
                xAxis
                    .scale(x)
                    .ticks( nv.utils.calcTicksX(availableWidth/100, data) )
                    .tickSize(-availableHeight, 0);

                g.select('.nv-x.nv-axis')
                    .attr('transform', 'translate(0,' + y.range()[0] + ')');
                g.select('.nv-x.nv-axis')
                    .call(xAxis);

                var xTicks = g.select('.nv-x.nv-axis > g').selectAll('g');

                xTicks
                    .selectAll('line, text')
                    .style('opacity', 1)

                if (staggerLabels) {
                    var getTranslate = function(x,y) {
                        return "translate(" + x + "," + y + ")";
                    };

                    var staggerUp = 5, staggerDown = 17;  //pixels to stagger by
                    // Issue #140
                    xTicks
                        .selectAll("text")
                        .attr('transform', function(d,i,j) {
                            return  getTranslate(0, (j % 2 == 0 ? staggerUp : staggerDown));
                        });

                    var totalInBetweenTicks = d3.selectAll(".nv-x.nv-axis .nv-wrap g g text")[0].length;
                    g.selectAll(".nv-x.nv-axis .nv-axisMaxMin text")
                        .attr("transform", function(d,i) {
                            return getTranslate(0, (i === 0 || totalInBetweenTicks % 2 !== 0) ? staggerDown : staggerUp);
                        });
                }

                if (reduceXTicks)
                    xTicks
                        .filter(function(d,i) {
                            return i % Math.ceil(data[0].values.length / (availableWidth / 100)) !== 0;
                        })
                        .selectAll('text, line')
                        .style('opacity', 0);

                if(rotateLabels)
                    xTicks
                        .selectAll('.tick text')
                        .attr('transform', 'rotate(' + rotateLabels + ' 0,0)')
                        .style('text-anchor', rotateLabels > 0 ? 'start' : 'end');

                g.select('.nv-x.nv-axis').selectAll('g.nv-axisMaxMin text')
                    .style('opacity', 1);
            }

            if (showYAxis) {
                yAxis
                    .scale(y)
                    .ticks( nv.utils.calcTicksY(availableHeight/36, data) )
                    .tickSize( -availableWidth, 0);

                g.select('.nv-y.nv-axis')
                    .call(yAxis);
            }

            //============================================================
            // Event Handling/Dispatching (in chart's scope)
            //------------------------------------------------------------

            legend.dispatch.on('stateChange', function(newState) {
                for (var key in newState)
                    state[key] = newState[key];
                dispatch.stateChange(state);
                chart.update();
            });

            controls.dispatch.on('legendClick', function(d,i) {
                if (!d.disabled) return;
                controlsData = controlsData.map(function(s) {
                    s.disabled = true;
                    return s;
                });
                d.disabled = false;

                switch (d.key) {
                    case 'Grouped':
                        multibar.stacked(false);
                        break;
                    case 'Stacked':
                        multibar.stacked(true);
                        break;
                }

                state.stacked = multibar.stacked();
                dispatch.stateChange(state);

                chart.update();
            });

            dispatch.on('tooltipShow', function(e) {
                if (tooltips) showTooltip(e, that.parentNode)
            });

            // Update chart from a state object passed to event handler
            dispatch.on('changeState', function(e) {

                if (typeof e.disabled !== 'undefined') {
                    data.forEach(function(series,i) {
                        series.disabled = e.disabled[i];
                    });

                    state.disabled = e.disabled;
                }

                if (typeof e.stacked !== 'undefined') {
                    multibar.stacked(e.stacked);
                    state.stacked = e.stacked;
                    stacked = e.stacked;
                }

                chart.update();
            });
// ---
// OTRS
// ---
            // because it could happen that x axis headings occur multiple
            // times (such as Thu 18 for two different months), we
            // need to make sure the custom label which has been added to
            // the data is removed properly for displaying
            var TicksText = container.selectAll('.tick');
            TicksText.each(function() {
                var Content = $(this).text(),
                    NewContent = '';
                if ( Content.match(/__LABEL_START__\d+__LABEL_END__/) ) {
                    NewContent = Content.replace(/__LABEL_START__\d+__LABEL_END__/, '');
                    $(this).find('text').text(NewContent);
                }
            });
// ---
        });

        renderWatch.renderEnd('multibarchart immediate');
        return chart;
    }

    //============================================================
    // Event Handling/Dispatching (out of chart's scope)
    //------------------------------------------------------------

    multibar.dispatch.on('elementMouseover.tooltip', function(e) {
        e.pos = [e.pos[0] +  margin.left, e.pos[1] + margin.top];
        dispatch.tooltipShow(e);
    });

    multibar.dispatch.on('elementMouseout.tooltip', function(e) {
        dispatch.tooltipHide(e);
    });
    dispatch.on('tooltipHide', function() {
        if (tooltips) nv.tooltip.cleanup();
    });

    //============================================================
    // Expose Public Variables
    //------------------------------------------------------------

    // expose chart's sub-components
    chart.dispatch = dispatch;
    chart.multibar = multibar;
    chart.legend = legend;
    chart.xAxis = xAxis;
    chart.yAxis = yAxis;
    chart.state = state;

    chart.options = nv.utils.optionsFunc.bind(chart);

    chart._options = Object.create({}, {
        // simple options, just get/set the necessary values
        width:      {get: function(){return width;}, set: function(_){width=_;}},
        height:     {get: function(){return height;}, set: function(_){height=_;}},
        showLegend: {get: function(){return showLegend;}, set: function(_){showLegend=_;}},
        showControls: {get: function(){return showControls;}, set: function(_){showControls=_;}},
        controlLabels: {get: function(){return controlLabels;}, set: function(_){controlLabels=_;}},
        showXAxis:      {get: function(){return showXAxis;}, set: function(_){showXAxis=_;}},
        showYAxis:    {get: function(){return showYAxis;}, set: function(_){showYAxis=_;}},
        tooltips:    {get: function(){return tooltips;}, set: function(_){tooltips=_;}},
        tooltipContent:    {get: function(){return tooltip;}, set: function(_){tooltip=_;}},
        defaultState:    {get: function(){return defaultState;}, set: function(_){defaultState=_;}},
        noData:    {get: function(){return noData;}, set: function(_){noData=_;}},
        reduceXTicks:    {get: function(){return reduceXTicks;}, set: function(_){reduceXTicks=_;}},
        rotateLabels:    {get: function(){return rotateLabels;}, set: function(_){rotateLabels=_;}},
        staggerLabels:    {get: function(){return staggerLabels;}, set: function(_){staggerLabels=_;}},

        // options that require extra logic in the setter
        margin: {get: function(){return margin;}, set: function(_){
            margin.top    = _.top    !== undefined ? _.top    : margin.top;
            margin.right  = _.right  !== undefined ? _.right  : margin.right;
            margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom;
            margin.left   = _.left   !== undefined ? _.left   : margin.left;
        }},
        duration: {get: function(){return duration;}, set: function(_){
            duration = _;
            multibar.duration(duration);
            xAxis.duration(duration);
            yAxis.duration(duration);
            renderWatch.reset(duration);
        }},
        color:  {get: function(){return color;}, set: function(_){
            color = nv.utils.getColor(_);
            legend.color(color);
        }},
        rightAlignYAxis: {get: function(){return rightAlignYAxis;}, set: function(_){
            rightAlignYAxis = _;
            yAxis.orient( rightAlignYAxis ? 'right' : 'left');
        }}
    });

    nv.utils.inheritOptions(chart, multibar);
    nv.utils.initOptions(chart);

    return chart;
};<commit_msg>Fixed chart module.<commit_after>nv.models.OTRSmultiBarChart = function() {
    "use strict";

    //============================================================
    // Public Variables with Default Settings
    //------------------------------------------------------------

    var multibar = nv.models.multiBar()
        , xAxis = nv.models.axis()
        , yAxis = nv.models.axis()
        , legend = nv.models.legend()
        , controls = nv.models.legend()
        ;

    var margin = {top: 30, right: 20, bottom: 50, left: 60}
        , width = null
        , height = null
        , color = nv.utils.defaultColor()
        , showControls = true
        , controlLabels = {}
        , showLegend = true
        , showXAxis = true
        , showYAxis = true
        , rightAlignYAxis = false
        , reduceXTicks = true // if false a tick will show for every data point
        , staggerLabels = false
        , rotateLabels = 0
        , tooltips = true
        , tooltip = function(key, x, y, e, graph) {
            return '
' + key + '
' +
                '
' +  y + ' on ' + x + '
'
        }
        , x //can be accessed via chart.xScale()
        , y //can be accessed via chart.yScale()
        , state = nv.utils.state()
        , defaultState = null
        , noData = "No Data Available."
        , dispatch = d3.dispatch('tooltipShow', 'tooltipHide', 'stateChange', 'changeState', 'renderEnd')
        , controlWidth = function() { return showControls ? 180 : 0 }
        , duration = 250
        ;

    state.stacked = false // DEPRECATED Maintained for backward compatibility

    multibar
        .stacked(false)
    ;
    xAxis
        .orient('bottom')
        .tickPadding(7)
        .showMaxMin(false)
        .tickFormat(function(d) { return d })
    ;
    yAxis
        .orient((rightAlignYAxis) ? 'right' : 'left')
        .tickFormat(d3.format(',.1f'))
    ;

    controls.updateState(false);

    //============================================================
    // Private Variables
    //------------------------------------------------------------

    var renderWatch = nv.utils.renderWatch(dispatch);
    var stacked = false;

    var showTooltip = function(e, offsetElement) {
        var left = e.pos[0] + ( offsetElement.offsetLeft || 0 ),
            top = e.pos[1] + ( offsetElement.offsetTop || 0),
            x = xAxis.tickFormat()(multibar.x()(e.point, e.pointIndex)),
            y = yAxis.tickFormat()(multibar.y()(e.point, e.pointIndex)),
            content = tooltip(e.series.key, x, y, e, chart);

// ---
// OTRS
// ---
      // because it could happen that x headings occurr multiple
      // times (such as Thu 18 for two different months), we
      // need to make sure the custom label which has been added to
      // the data is removed properly for displaying
      if ( content.match(/__LABEL_START__\d+__LABEL_END__/) ) {
        content = content.replace(/__LABEL_START__\d+__LABEL_END__/, '');
      }
// ---

        nv.tooltip.show([left, top], content, e.value < 0 ? 'n' : 's', null, offsetElement);
    };

    var stateGetter = function(data) {
        return function(){
            return {
                active: data.map(function(d) { return !d.disabled }),
                stacked: stacked
            };
        }
    };

    var stateSetter = function(data) {
        return function(state) {
            if (state.stacked !== undefined)
                stacked = state.stacked;
            if (state.active !== undefined)
                data.forEach(function(series,i) {
                    series.disabled = !state.active[i];
                });
        }
    };

    function chart(selection) {
        renderWatch.reset();
        renderWatch.models(multibar);
        if (showXAxis) renderWatch.models(xAxis);
        if (showYAxis) renderWatch.models(yAxis);

        selection.each(function(data) {
            var container = d3.select(this),
                that = this;
            nv.utils.initSVG(container);
            var availableWidth = (width  || parseInt(container.style('width')) || 960)
                    - margin.left - margin.right,
                availableHeight = (height || parseInt(container.style('height')) || 400)
                    - margin.top - margin.bottom;

            chart.update = function() {
                if (duration === 0)
                    container.call(chart);
                else
                    container.transition()
                        .duration(duration)
                        .call(chart);
            };
            chart.container = this;

            state
                .setter(stateSetter(data), chart.update)
                .getter(stateGetter(data))
                .update();

            // DEPRECATED set state.disableddisabled
            state.disabled = data.map(function(d) { return !!d.disabled });

            if (!defaultState) {
                var key;
                defaultState = {};
                for (key in state) {
                    if (state[key] instanceof Array)
                        defaultState[key] = state[key].slice(0);
                    else
                        defaultState[key] = state[key];
                }
            }

            // Display noData message if there's nothing to show.
            if (!data || !data.length || !data.filter(function(d) { return d.values.length }).length) {
                var noDataText = container.selectAll('.nv-noData').data([noData]);

                noDataText.enter().append('text')
                    .attr('class', 'nvd3 nv-noData')
                    .attr('dy', '-.7em')
                    .style('text-anchor', 'middle');

                noDataText
                    .attr('x', margin.left + availableWidth / 2)
                    .attr('y', margin.top + availableHeight / 2)
                    .text(function(d) { return d });

                return chart;
            } else {
                container.selectAll('.nv-noData').remove();
            }

            // Setup Scales
            x = multibar.xScale();
            y = multibar.yScale();

            // Setup containers and skeleton of chart
            var wrap = container.selectAll('g.nv-wrap.nv-multiBarWithLegend').data([data]);
            var gEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-multiBarWithLegend').append('g');
            var g = wrap.select('g');

            gEnter.append('g').attr('class', 'nv-x nv-axis');
            gEnter.append('g').attr('class', 'nv-y nv-axis');
            gEnter.append('g').attr('class', 'nv-barsWrap');
            gEnter.append('g').attr('class', 'nv-legendWrap');
            gEnter.append('g').attr('class', 'nv-controlsWrap');

            // Legend
            if (showLegend) {
                legend.width(availableWidth - controlWidth());

                if (multibar.barColor())
                    data.forEach(function(series,i) {
                        series.color = d3.rgb('#ccc').darker(i * 1.5).toString();
                    });

                g.select('.nv-legendWrap')
                    .datum(data)
                    .call(legend);

                if ( margin.top != legend.height()) {
                    margin.top = legend.height();
                    availableHeight = (height || parseInt(container.style('height')) || 400)
                        - margin.top - margin.bottom;
                }

                g.select('.nv-legendWrap')
                    .attr('transform', 'translate(' + controlWidth() + ',' + (-margin.top) +')');
            }

            // Controls
            if (showControls) {
                var controlsData = [
                    { key: controlLabels.grouped || 'Grouped', disabled: multibar.stacked() },
                    { key: controlLabels.stacked || 'Stacked', disabled: !multibar.stacked() }
                ];

                controls.width(controlWidth()).color(['#444', '#444', '#444']);
                g.select('.nv-controlsWrap')
                    .datum(controlsData)
                    .attr('transform', 'translate(0,' + (-margin.top) +')')
                    .call(controls);
            }

            wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
            if (rightAlignYAxis) {
                g.select(".nv-y.nv-axis")
                    .attr("transform", "translate(" + availableWidth + ",0)");
            }

            // Main Chart Component(s)
            multibar
                .disabled(data.map(function(series) { return series.disabled }))
                .width(availableWidth)
                .height(availableHeight)
                .color(data.map(function(d,i) {
                    return d.color || color(d, i);
                }).filter(function(d,i) { return !data[i].disabled }));

            var barsWrap = g.select('.nv-barsWrap')
                .datum(data.filter(function(d) { return !d.disabled }));

            barsWrap.call(multibar);

            // Setup Axes
            if (showXAxis) {
                xAxis
                    .scale(x)
                    .ticks( nv.utils.calcTicksX(availableWidth/100, data) )
                    .tickSize(-availableHeight, 0);

                g.select('.nv-x.nv-axis')
                    .attr('transform', 'translate(0,' + y.range()[0] + ')');
                g.select('.nv-x.nv-axis')
                    .call(xAxis);

                var xTicks = g.select('.nv-x.nv-axis > g').selectAll('g');

                xTicks
                    .selectAll('line, text')
                    .style('opacity', 1)

                if (staggerLabels) {
                    var getTranslate = function(x,y) {
                        return "translate(" + x + "," + y + ")";
                    };

                    var staggerUp = 5, staggerDown = 17;  //pixels to stagger by
                    // Issue #140
                    xTicks
                        .selectAll("text")
                        .attr('transform', function(d,i,j) {
                            return  getTranslate(0, (j % 2 == 0 ? staggerUp : staggerDown));
                        });

                    var totalInBetweenTicks = d3.selectAll(".nv-x.nv-axis .nv-wrap g g text")[0].length;
                    g.selectAll(".nv-x.nv-axis .nv-axisMaxMin text")
                        .attr("transform", function(d,i) {
                            return getTranslate(0, (i === 0 || totalInBetweenTicks % 2 !== 0) ? staggerDown : staggerUp);
                        });
                }

                if (reduceXTicks)
                    xTicks
                        .filter(function(d,i) {
                            return i % Math.ceil(data[0].values.length / (availableWidth / 100)) !== 0;
                        })
                        .selectAll('text, line')
                        .style('opacity', 0);

                if(rotateLabels)
                    xTicks
                        .selectAll('.tick text')
                        .attr('transform', 'rotate(' + rotateLabels + ' 0,0)')
                        .style('text-anchor', rotateLabels > 0 ? 'start' : 'end');

                g.select('.nv-x.nv-axis').selectAll('g.nv-axisMaxMin text')
                    .style('opacity', 1);
            }

            if (showYAxis) {
                yAxis
                    .scale(y)
                    .ticks( nv.utils.calcTicksY(availableHeight/36, data) )
                    .tickSize( -availableWidth, 0);

                g.select('.nv-y.nv-axis')
                    .call(yAxis);
            }

            //============================================================
            // Event Handling/Dispatching (in chart's scope)
            //------------------------------------------------------------

            legend.dispatch.on('stateChange', function(newState) {
                for (var key in newState)
                    state[key] = newState[key];
                dispatch.stateChange(state);
                chart.update();
            });

            controls.dispatch.on('legendClick', function(d,i) {
                if (!d.disabled) return;
                controlsData = controlsData.map(function(s) {
                    s.disabled = true;
                    return s;
                });
                d.disabled = false;

                switch (d.key) {
                    case 'Grouped':
                        multibar.stacked(false);
                        break;
                    case 'Stacked':
                        multibar.stacked(true);
                        break;
                }

                state.stacked = multibar.stacked();
                dispatch.stateChange(state);

                chart.update();
            });

            dispatch.on('tooltipShow', function(e) {
                if (tooltips) showTooltip(e, that.parentNode)
            });

            // Update chart from a state object passed to event handler
            dispatch.on('changeState', function(e) {

                if (typeof e.disabled !== 'undefined') {
                    data.forEach(function(series,i) {
                        series.disabled = e.disabled[i];
                    });

                    state.disabled = e.disabled;
                }

                if (typeof e.stacked !== 'undefined') {
                    multibar.stacked(e.stacked);
                    state.stacked = e.stacked;
                    stacked = e.stacked;
                }

                chart.update();
            });
// ---
// OTRS
// ---
            // because it could happen that x axis headings occur multiple
            // times (such as Thu 18 for two different months), we
            // need to make sure the custom label which has been added to
            // the data is removed properly for displaying
            var TicksText = container.selectAll('.tick');
            TicksText.each(function() {
                var Content = $(this).text(),
                    NewContent = '';
                if ( Content.match(/__LABEL_START__\d+__LABEL_END__/) ) {
                    NewContent = Content.replace(/__LABEL_START__\d+__LABEL_END__/, '');
                    $(this).find('text').text(NewContent);
                }
            });
// ---
        });

        renderWatch.renderEnd('multibarchart immediate');
        return chart;
    }

    //============================================================
    // Event Handling/Dispatching (out of chart's scope)
    //------------------------------------------------------------

    multibar.dispatch.on('elementMouseover.tooltip', function(e) {
        e.pos = [e.pos[0] +  margin.left, e.pos[1] + margin.top];
        dispatch.tooltipShow(e);
    });

    multibar.dispatch.on('elementMouseout.tooltip', function(e) {
        dispatch.tooltipHide(e);
    });
    dispatch.on('tooltipHide', function() {
        if (tooltips) nv.tooltip.cleanup();
    });

    //============================================================
    // Expose Public Variables
    //------------------------------------------------------------

    // expose chart's sub-components
    chart.dispatch = dispatch;
    chart.multibar = multibar;
    chart.legend = legend;
    chart.xAxis = xAxis;
    chart.yAxis = yAxis;
    chart.state = state;

    chart.options = nv.utils.optionsFunc.bind(chart);

    chart._options = Object.create({}, {
        // simple options, just get/set the necessary values
        width:      {get: function(){return width;}, set: function(_){width=_;}},
        height:     {get: function(){return height;}, set: function(_){height=_;}},
        showLegend: {get: function(){return showLegend;}, set: function(_){showLegend=_;}},
        showControls: {get: function(){return showControls;}, set: function(_){showControls=_;}},
        controlLabels: {get: function(){return controlLabels;}, set: function(_){controlLabels=_;}},
        showXAxis:      {get: function(){return showXAxis;}, set: function(_){showXAxis=_;}},
        showYAxis:    {get: function(){return showYAxis;}, set: function(_){showYAxis=_;}},
        tooltips:    {get: function(){return tooltips;}, set: function(_){tooltips=_;}},
        tooltipContent:    {get: function(){return tooltip;}, set: function(_){tooltip=_;}},
        defaultState:    {get: function(){return defaultState;}, set: function(_){defaultState=_;}},
        noData:    {get: function(){return noData;}, set: function(_){noData=_;}},
        reduceXTicks:    {get: function(){return reduceXTicks;}, set: function(_){reduceXTicks=_;}},
        rotateLabels:    {get: function(){return rotateLabels;}, set: function(_){rotateLabels=_;}},
        staggerLabels:    {get: function(){return staggerLabels;}, set: function(_){staggerLabels=_;}},

        // options that require extra logic in the setter
        margin: {get: function(){return margin;}, set: function(_){
            margin.top    = _.top    !== undefined ? _.top    : margin.top;
            margin.right  = _.right  !== undefined ? _.right  : margin.right;
            margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom;
            margin.left   = _.left   !== undefined ? _.left   : margin.left;
        }},
        duration: {get: function(){return duration;}, set: function(_){
            duration = _;
            multibar.duration(duration);
            xAxis.duration(duration);
            yAxis.duration(duration);
            renderWatch.reset(duration);
        }},
        color:  {get: function(){return color;}, set: function(_){
            color = nv.utils.getColor(_);
            legend.color(color);
        }},
        rightAlignYAxis: {get: function(){return rightAlignYAxis;}, set: function(_){
            rightAlignYAxis = _;
            yAxis.orient( rightAlignYAxis ? 'right' : 'left');
        }}
    });

    nv.utils.inheritOptions(chart, multibar);
    nv.utils.initOptions(chart);

    return chart;
};<|endoftext|>


<commit_before>(function () {
  var _ = function (element, o) {
    var me = this;

    if (typeof element === 'string' && $$(element).length > 1) {
      var object = [];
      $$(element).forEach(function (element) {
        object.push(new _(element, o));
      });
      return object;
    }

    // Setup.
    me.element = $(element);
    me.options = {};

    // Return an empty object if the element does not exist.
    if (!me.element) {
      return {};
    }

    o = o || {};

    configure(me, {
      disableHistory: false,
      classNames: {
        main: 'perfundo',
        link: '__link',
        overlay: '__overlay',
        content: '__content',
        html: '__html',
        figcaption: '__figcaption',
        control: '__control',
        close: '__close',
        prev: '__prev',
        next: '__next',
        untarget: '__untarget',
        active: 'is-active'
      }
    }, o);

    // Add ".is-active" functionality.
    var style = $('style', me.element);
    if (style) {
      var isActiveStyle = style.cloneNode(true);
      isActiveStyle.innerHTML = isActiveStyle.innerHTML.replace(
        /:target/gi,
        '.' + me.options.classNames.active
      );
      me.element.insertBefore(isActiveStyle, style);
    }

    $.bind($$('.' + me.options.classNames.main + me.options.classNames.link, me.element), {
      'click': function (e) {
        var overlay = $(this.getAttribute('href'));
        if (me.options.disableHistory) {
          e.preventDefault();
        }
        me.closeOverlay();
        overlay.classList.add(me.options.classNames.active); // @TODO: use openOverlay prototype
      }
    });

    $.bind($$('.' + me.options.classNames.main + me.options.classNames.close, me.element), {
      'click': function (e) {
        if (me.options.disableHistory) {
          e.preventDefault();
        }
        me.closeOverlay();
      }
    });

    // $.bind($$('.' + me.options.classNames.link, me.element), {
    //   'focus': function (e) {
    //     var trigger = e.target;
    //     var subMenu = trigger.nextElementSibling;
    //     // Close open sub menus that are not part of the triggered menu tree.
    //     var openSubMenus = $$('.' + me.options.classNames.open, me.element);
    //     openSubMenus.forEach(function (openSubMenu) {
    //       if (!isDescendant(openSubMenu, trigger)) {
    //         me.closeSubMenu(openSubMenu);
    //       }
    //     });
    //     // Open the sub menu.
    //     if (me.options.focusopen == 'enabled' && subMenu && subMenu.classList.contains(me.options.classNames.sub)) {
    //       me.openSubMenu(subMenu);
    //     }
    //   },
    //   'blur': function (e) {
    //     // Close all open sub menus if the menu loses focus.
    //     setTimeout(function() {
    //       var trigger = document.activeElement;
    //       if (!isDescendant(me.element, trigger)) {
    //         var openSubMenus = $$('.' + me.options.classNames.open, me.element);
    //         openSubMenus.forEach(function (openSubMenu) {
    //           me.closeSubMenu(openSubMenu);
    //         });
    //       }
    //     }, 1);
    //   }
    // });

    // if (me.options.spaceopen == 'enabled') {
    //   $.bind($$('.' + me.options.classNames.link, me.element), {
    //     'keydown': function (e) {
    //       if (e.keyCode == 32) {
    //         var trigger = e.target;
    //         var subMenu = trigger.nextElementSibling;

    //         if (subMenu && subMenu.classList.contains(me.options.classNames.sub)) {
    //           e.preventDefault();
    //           me.toggleSubMenu(subMenu);
    //         }
    //       }
    //     }
    //   });
    // }

    // if (me.options.clickopen == 'enabled') {
    //   $$('.' + me.options.classNames.sub, me.element).forEach(function (subMenu) {
    //     subMenu.classList.add(me.options.classNames.nohover)
    //   });
    //   $.bind($$('.' + me.options.classNames.link, me.element), {
    //     'click': function (e) {
    //       var subMenu = e.target.nextElementSibling;

    //       if (subMenu && subMenu.classList.contains(me.options.classNames.sub)) {
    //         e.preventDefault();
    //         me.openSubMenu(subMenu);
    //       }
    //     }
    //   });
    // }
  };

  _.prototype = {
    closeOverlay: function () {
      var me = this;
      $$('.' + me.options.classNames.main + me.options.classNames.overlay + '.' + me.options.classNames.active, me.element).forEach(function (overlay) {
        overlay.classList.remove(me.options.classNames.active);
      });
    }
    // openSubMenu: function (subMenu) {
    //   subMenu = $(subMenu, this.element);
    //   // Add open class and aria rule to the sub menu.
    //   subMenu.previousElementSibling.setAttribute('aria-expanded', true);
    //   subMenu.classList.add(this.options.classNames.open);
    // },
    // closeSubMenu: function (subMenu) {
    //   subMenu = $(subMenu, this.element);
    //   // Remove open class and aria rule from not targeted sub menus.
    //   subMenu.previousElementSibling.setAttribute('aria-expanded', false);
    //   subMenu.classList.remove(this.options.classNames.open);
    // },
    // toggleSubMenu: function (subMenu) {
    //   subMenu = $(subMenu, this.element);
    //   if (subMenu.classList.contains(this.options.classNames.open)) {
    //     this.closeSubMenu(subMenu);
    //   }
    //   else {
    //     this.openSubMenu(subMenu);
    //   }
    // }
  };

  // Private functions.
  function configure(instance, properties, o) {
    for (var i in properties) {
      var initial = properties[i];
      var attrValue = instance.element.getAttribute('data-' + i.toLowerCase());

      if (typeof initial === 'number') {
        instance.options[i] = parseInt(attrValue);
      }
      else if (initial === false) {
        instance.options[i] = attrValue !== null;
      }
      else if (initial instanceof Function) {
        instance.options[i] = null;
      }
      else {
        instance.options[i] = attrValue;
      }

      if (!instance.options[i] && instance.options[i] !== 0) {
        instance.options[i] = (i in o) ? o[i] : initial;
      }
    }
  }

  // Helpers.
  var slice = Array.prototype.slice;

  function $(expr, con) {
    return typeof expr === 'string' ? (con || document).querySelector(expr) : expr || null;
  }

  function $$(expr, con) {
    return slice.call((con || document).querySelectorAll(expr));
  }

  // function isDescendant(parent, child) {
  //   var node = child.parentNode;
  //   while (node != null) {
  //     if (node == parent) {
  //       return true;
  //     }
  //     node = node.parentNode;
  //   }
  //   return false;
  // }

  $.bind = function(elements, o) {
    if (elements) {
      elements = elements.length ? elements : [elements];
      elements.forEach(function (element) {
        for (var event in o) {
          var callback = o[event];
          event.split(/\s+/).forEach(function (event) {
            element.addEventListener(event, callback);
          });
        }
      });
    }
  };

  _.$ = $;
  _.$$ = $$;

  // Make sure to export perfundo on self when in a browser.
  if (typeof self !== 'undefined') {
    self.perfundo = _;
  }

  // Expose componentNav as a CJS module.
  if (typeof module === 'object' && module.exports) {
    module.exports = _;
  }

  return _;
}());
<commit_msg>add TODO<commit_after>(function () {
  var _ = function (element, o) {
    var me = this;

    if (typeof element === 'string' && $$(element).length > 1) {
      var object = [];
      $$(element).forEach(function (element) {
        object.push(new _(element, o));
      });
      return object;
    }

    // Setup.
    me.element = $(element);
    me.options = {};

    // Return an empty object if the element does not exist.
    if (!me.element) {
      return {};
    }

    o = o || {};

    // @TODO: add a placeholder for the main class name to the class names?!
    configure(me, {
      disableHistory: false,
      classNames: {
        main: 'perfundo',
        link: '__link',
        overlay: '__overlay',
        content: '__content',
        html: '__html',
        figcaption: '__figcaption',
        control: '__control',
        close: '__close',
        prev: '__prev',
        next: '__next',
        untarget: '__untarget',
        active: 'is-active'
      }
    }, o);

    // Add ".is-active" functionality.
    var style = $('style', me.element);
    if (style) {
      var isActiveStyle = style.cloneNode(true);
      isActiveStyle.innerHTML = isActiveStyle.innerHTML.replace(
        /:target/gi,
        '.' + me.options.classNames.active
      );
      me.element.insertBefore(isActiveStyle, style);
    }

    $.bind($$('.' + me.options.classNames.main + me.options.classNames.link, me.element), {
      'click': function (e) {
        var overlay = $(this.getAttribute('href'));
        if (me.options.disableHistory) {
          e.preventDefault();
        }
        me.closeOverlay();
        overlay.classList.add(me.options.classNames.active); // @TODO: use openOverlay prototype
      }
    });

    $.bind($$('.' + me.options.classNames.main + me.options.classNames.close, me.element), {
      'click': function (e) {
        if (me.options.disableHistory) {
          e.preventDefault();
        }
        me.closeOverlay();
      }
    });

    // $.bind($$('.' + me.options.classNames.link, me.element), {
    //   'focus': function (e) {
    //     var trigger = e.target;
    //     var subMenu = trigger.nextElementSibling;
    //     // Close open sub menus that are not part of the triggered menu tree.
    //     var openSubMenus = $$('.' + me.options.classNames.open, me.element);
    //     openSubMenus.forEach(function (openSubMenu) {
    //       if (!isDescendant(openSubMenu, trigger)) {
    //         me.closeSubMenu(openSubMenu);
    //       }
    //     });
    //     // Open the sub menu.
    //     if (me.options.focusopen == 'enabled' && subMenu && subMenu.classList.contains(me.options.classNames.sub)) {
    //       me.openSubMenu(subMenu);
    //     }
    //   },
    //   'blur': function (e) {
    //     // Close all open sub menus if the menu loses focus.
    //     setTimeout(function() {
    //       var trigger = document.activeElement;
    //       if (!isDescendant(me.element, trigger)) {
    //         var openSubMenus = $$('.' + me.options.classNames.open, me.element);
    //         openSubMenus.forEach(function (openSubMenu) {
    //           me.closeSubMenu(openSubMenu);
    //         });
    //       }
    //     }, 1);
    //   }
    // });

    // if (me.options.spaceopen == 'enabled') {
    //   $.bind($$('.' + me.options.classNames.link, me.element), {
    //     'keydown': function (e) {
    //       if (e.keyCode == 32) {
    //         var trigger = e.target;
    //         var subMenu = trigger.nextElementSibling;

    //         if (subMenu && subMenu.classList.contains(me.options.classNames.sub)) {
    //           e.preventDefault();
    //           me.toggleSubMenu(subMenu);
    //         }
    //       }
    //     }
    //   });
    // }

    // if (me.options.clickopen == 'enabled') {
    //   $$('.' + me.options.classNames.sub, me.element).forEach(function (subMenu) {
    //     subMenu.classList.add(me.options.classNames.nohover)
    //   });
    //   $.bind($$('.' + me.options.classNames.link, me.element), {
    //     'click': function (e) {
    //       var subMenu = e.target.nextElementSibling;

    //       if (subMenu && subMenu.classList.contains(me.options.classNames.sub)) {
    //         e.preventDefault();
    //         me.openSubMenu(subMenu);
    //       }
    //     }
    //   });
    // }
  };

  _.prototype = {
    closeOverlay: function () {
      var me = this;
      $$('.' + me.options.classNames.main + me.options.classNames.overlay + '.' + me.options.classNames.active, me.element).forEach(function (overlay) {
        overlay.classList.remove(me.options.classNames.active);
      });
    }
    // openSubMenu: function (subMenu) {
    //   subMenu = $(subMenu, this.element);
    //   // Add open class and aria rule to the sub menu.
    //   subMenu.previousElementSibling.setAttribute('aria-expanded', true);
    //   subMenu.classList.add(this.options.classNames.open);
    // },
    // closeSubMenu: function (subMenu) {
    //   subMenu = $(subMenu, this.element);
    //   // Remove open class and aria rule from not targeted sub menus.
    //   subMenu.previousElementSibling.setAttribute('aria-expanded', false);
    //   subMenu.classList.remove(this.options.classNames.open);
    // },
    // toggleSubMenu: function (subMenu) {
    //   subMenu = $(subMenu, this.element);
    //   if (subMenu.classList.contains(this.options.classNames.open)) {
    //     this.closeSubMenu(subMenu);
    //   }
    //   else {
    //     this.openSubMenu(subMenu);
    //   }
    // }
  };

  // Private functions.
  function configure(instance, properties, o) {
    for (var i in properties) {
      var initial = properties[i];
      var attrValue = instance.element.getAttribute('data-' + i.toLowerCase());

      if (typeof initial === 'number') {
        instance.options[i] = parseInt(attrValue);
      }
      else if (initial === false) {
        instance.options[i] = attrValue !== null;
      }
      else if (initial instanceof Function) {
        instance.options[i] = null;
      }
      else {
        instance.options[i] = attrValue;
      }

      if (!instance.options[i] && instance.options[i] !== 0) {
        instance.options[i] = (i in o) ? o[i] : initial;
      }
    }
  }

  // Helpers.
  var slice = Array.prototype.slice;

  function $(expr, con) {
    return typeof expr === 'string' ? (con || document).querySelector(expr) : expr || null;
  }

  function $$(expr, con) {
    return slice.call((con || document).querySelectorAll(expr));
  }

  // function isDescendant(parent, child) {
  //   var node = child.parentNode;
  //   while (node != null) {
  //     if (node == parent) {
  //       return true;
  //     }
  //     node = node.parentNode;
  //   }
  //   return false;
  // }

  $.bind = function(elements, o) {
    if (elements) {
      elements = elements.length ? elements : [elements];
      elements.forEach(function (element) {
        for (var event in o) {
          var callback = o[event];
          event.split(/\s+/).forEach(function (event) {
            element.addEventListener(event, callback);
          });
        }
      });
    }
  };

  _.$ = $;
  _.$$ = $$;

  // Make sure to export perfundo on self when in a browser.
  if (typeof self !== 'undefined') {
    self.perfundo = _;
  }

  // Expose componentNav as a CJS module.
  if (typeof module === 'object' && module.exports) {
    module.exports = _;
  }

  return _;
}());
<|endoftext|>


<commit_before>// Syntactic sugars, for write less, do more, no bloats.
var $ = function (selector) {
  return document.querySelector(selector);
}
var $$ = function (selector) {
  return document.querySelectorAll(selector);
}

// Minimalist landing page with semi interactive RepelJS
// https://gist.github.com/debloper/c8fd6c328d471fbec6e9
// =====================================================
// Set the element to select; the only moving part here
// Like literally...
var logo = $("#peek")
  , html = $("html");

// Take the viewport height/width into consideration
// @TODO: should be updated on window resize
var height = document.documentElement.clientHeight
  , width  = document.documentElement.clientWidth;

// Used later to decide Scroll Threshold
var timeDelta = Date.now();

// Here's where the magic happens... look away!
html.addEventListener("mousemove", function (event) {
  logo.style.top = (height/2 - event.clientY)/50 + "px";
  logo.style.left = (width/2 - event.clientX)/50 + "px";
}, false);

// Click on the teaser section, and it goes p0o0f!
// Leaving the directive to setup the stage async.
var teaser = $("#teaser");
teaser.addEventListener("click", function (event) {
  TweenLite.to(teaser, 0.5, {
    opacity: 0,
    delay: 0.1,
    onComplete: setupStage
  });
});

// post-teaser routines to set up the main content
var setupStage = function (args) {

  // One time calls; no strings attached (figuratively)
  $("body").removeChild(teaser);
  $("header").style.display = "block";
  $("footer").style.display = "block";

  // Set the first chapter to the view
  setChapter(1);

  // Set the event listener to process scroll events
  window.addEventListener("wheel", function (event) {
    if (Date.now() - timeDelta > 750) {
      timeDelta = Date.now();
      if(event.deltaY > 0) setChapter("next");
      if(event.deltaY < 0) setChapter("prev");
    }
  }, false);

  // Set the event listener to process navigation events
  var elements = document.querySelectorAll("#masthead div");
  for (var i in elements) {
    if (i == "item") break;
    elements[i].addEventListener("click", toggleSwitch, false);
  }
}

// Shitz get real with the chapter switch routines
var setChapter = function (index) {
  var activeChapter = $(".focus")
    , chapterNumber = "";

  if (Number.isInteger(parseInt(index, 10))) {
    chapterNumber = parseInt(index, 10);
  } else if (typeof index === "string") {
    // Check if any chapter is set to start with
    // Except for the first time, it's always set
    if (activeChapter) {
      chapterNumber = activeChapter.getAttribute("id");
    } else { chapterNumber = 0; }

    if (index === "next") {
      // If asked to go next, increase counter
      chapterNumber++;
    } else if (index === "prev") {
      // If asked to go prev, decrease counter
      chapterNumber--;
    } else return;
  } else return;

  // If the final value of the chapterNumber is within limits:
  if (chapterNumber >= 1 && chapterNumber <= 3) {
    // Unset the current active chapter
    if (activeChapter) activeChapter.classList.remove("focus");
    // Set the target active chapter
    var target = document.getElementById("" + chapterNumber);
    target.classList.add("focus");
    toggleSwitch(chapterNumber)
  }
}

// Pseudo-navigation bar switches, extened to handle chapters
var toggleSwitch = function (cue) {
  var element = document.querySelector("#masthead div.active");
  element.classList.remove("active");

  // Select the target element
  var target;

  // If the queue is a number, assign it as integer
  if (Number.isInteger(parseInt(cue, 10))) {
    target = $("#for" + parseInt(cue, 10));

    // Add active class to the target
    target.classList.add("active");
  } else if (typeof cue === "object") {
    target = cue.target;

    // Add active class to the target
    target.classList.add("active");
    // Set the correct chapter
    setChapter(target.getAttribute("id").split("for")[1]);
  } else return;
}
<commit_msg>Touch-responsive teaser interaction, with new (not-so-good) event-sugar<commit_after>// Syntactic sugars, for write less, do more, no bloats.
var $ = function (selector) {
  return document.querySelector(selector);
}
var $$ = function (selector) {
  return document.querySelectorAll(selector);
}

var on = function (events, element, handler) {
  events = events.split(" ");
  for (var i in events) {
    element.addEventListener(events[i], handler, false);
  }
}

// Minimalist landing page with semi interactive RepelJS
// https://gist.github.com/debloper/c8fd6c328d471fbec6e9
// =====================================================
// Set the element to select; the only moving part here
// Like literally...
var logo = $("#peek")
  , html = $("html");

// Take the viewport height/width into consideration
// @TODO: should be updated on window resize
var height = document.documentElement.clientHeight
  , width  = document.documentElement.clientWidth;

// Used later to decide Scroll Threshold
var timeDelta = Date.now();

// Here's where the magic happens... look away!
on("mousemove touchmove", html, function (event) {
  var action = (event.type === "touchmove") ? event.touches[0] : event;
  logo.style.top = (height/2 - action.clientY)/50 + "px";
  logo.style.left = (width/2 - action.clientX)/50 + "px";
}, false);

// Click on the teaser section, and it goes p0o0f!
// Leaving the directive to setup the stage async.
var teaser = $("#teaser");
on("click touchend", teaser, function (event) {
  TweenLite.to(teaser, 0.5, {
    opacity: 0,
    delay: 0.1,
    onComplete: setupStage
  });
});

// post-teaser routines to set up the main content
var setupStage = function (args) {

  // One time calls; no strings attached (figuratively)
  $("body").removeChild(teaser);
  $("header").style.display = "block";
  $("footer").style.display = "block";

  // Set the first chapter to the view
  setChapter(1);

  // Set the event listener to process scroll events
  window.addEventListener("wheel", function (event) {
    if (Date.now() - timeDelta > 750) {
      timeDelta = Date.now();
      if(event.deltaY > 0) setChapter("next");
      if(event.deltaY < 0) setChapter("prev");
    }
  }, false);

  // Set the event listener to process navigation events
  var elements = document.querySelectorAll("#masthead div");
  for (var i in elements) {
    if (i == "item") break;
    elements[i].addEventListener("click", toggleSwitch, false);
  }
}

// Shitz get real with the chapter switch routines
var setChapter = function (index) {
  var activeChapter = $(".focus")
    , chapterNumber = "";

  if (Number.isInteger(parseInt(index, 10))) {
    chapterNumber = parseInt(index, 10);
  } else if (typeof index === "string") {
    // Check if any chapter is set to start with
    // Except for the first time, it's always set
    if (activeChapter) {
      chapterNumber = activeChapter.getAttribute("id");
    } else { chapterNumber = 0; }

    if (index === "next") {
      // If asked to go next, increase counter
      chapterNumber++;
    } else if (index === "prev") {
      // If asked to go prev, decrease counter
      chapterNumber--;
    } else return;
  } else return;

  // If the final value of the chapterNumber is within limits:
  if (chapterNumber >= 1 && chapterNumber <= 3) {
    // Unset the current active chapter
    if (activeChapter) activeChapter.classList.remove("focus");
    // Set the target active chapter
    var target = document.getElementById("" + chapterNumber);
    target.classList.add("focus");
    toggleSwitch(chapterNumber)
  }
}

// Pseudo-navigation bar switches, extened to handle chapters
var toggleSwitch = function (cue) {
  var element = document.querySelector("#masthead div.active");
  element.classList.remove("active");

  // Select the target element
  var target;

  // If the queue is a number, assign it as integer
  if (Number.isInteger(parseInt(cue, 10))) {
    target = $("#for" + parseInt(cue, 10));

    // Add active class to the target
    target.classList.add("active");
  } else if (typeof cue === "object") {
    target = cue.target;

    // Add active class to the target
    target.classList.add("active");
    // Set the correct chapter
    setChapter(target.getAttribute("id").split("for")[1]);
  } else return;
}
<|endoftext|>


<commit_before>var gulp = require('gulp');
var $ = require('gulp-load-plugins')();
var del = require('del');
var karmaServer = require('karma').Server;
var browserSync = require('browser-sync').create();
var nodemon = require('gulp-nodemon');
var series = require('stream-series');

// VARIABLES ======================================================
var isDist = $.util.env.type === 'dist';
var outputFolder = isDist ? 'dist' : 'build';

var globs = {
  sass: 'src/style/**/*.scss',
  views: 'src/client/**/*.html',
  assets: 'src/assets/**/*.*',
  // karma typescript preprocessor generates a bunch of .ktp.ts which gets picked
  // up by the watch, rinse and repeat
  types: ['src/types/**/*.ts', '!src/**/*.ktp.*'],
  client: ['src/client/**/*.ts', '!src/client/**/*.ktp.*'],
  clientWithDefinitions: ['src/client/**/*.ts', 'src/types/**/*.ts', '!src/**/*.ktp.*'],
  server: ['src/server/**/*.ts', '!src/server/**/*.ktp.*'],
  serverWithDefinitions: ['src/server/**/*.ts', 'src/types/**/*.ts', '!src/**/*.ktp.*'],
  integration: 'src/tests/integration/**/*.js',
  index: 'src/index.html'
};

var destinations = {
  css: outputFolder + "/style",
  client: outputFolder + "/client",
  server: outputFolder + "/server",
  libs: outputFolder + "/vendor",
  assets: outputFolder + "/assets",
  index: outputFolder + "/views"
};

// When adding a 3rd party we want to insert in the html, add it to
// vendoredLibs, order matters
var vendoredLibs = [
  'vendor/angular/angular.js',
  'vendor/ui-router/release/angular-ui-router.js',
];

// Will be filled automatically
var vendoredLibsMin = [];

var injectLibsPaths = {
  dev: [],
  dist: []
};

var injectPaths = {
  dev: [],
  dist: []
};

vendoredLibs.forEach(function(lib) {
    // take the filename
    var splittedPath = lib.split('/');
    var filename = splittedPath[splittedPath.length -1];
    injectLibsPaths.dev.push(destinations.libs + '/' + filename);
    // And get the minified version
    filename = filename.split('.')[0] + '.min.js';
    splittedPath[splittedPath.length - 1] = filename;
    vendoredLibsMin.push(splittedPath.join('/'));
    injectLibsPaths.dist.push(destinations.libs + '/' + filename);
});

['dev', 'dist'].forEach(function (env) {
    injectPaths[env] = injectLibsPaths[env].concat([
        destinations.client + '/*/**/*.module.js',
        destinations.client + '/app.module.js',
        isDist ? destinations.client + '/app.js' : destinations.client + '/**/!(*.module).js',
        destinations.client + '/views.js',
        destinations.css + '/*.css'
    ]);
});

// TASKS ===========================================================
function sass(file) {
    var path = typeof file == 'function' ? globs.sass : file;
    return gulp.src(path)
        .pipe($.sass({style: 'compressed'}).on('error', $.sass.logError))
        .pipe($.autoprefixer())  // defauls to > 1%, last 2 versions, Firefox ESR, Opera 12.1
        .pipe(gulp.dest(destinations.css))
        .pipe(browserSync.reload({stream: true}));
}

gulp.task('ts-lint', function () {
  return gulp.src(globs.client)
    .pipe($.tslint())
    .pipe($.tslint.report('full', {emitError: false}));
});

function computePaths(file) {
    if (typeof file != 'function') {
        this.src = file;
        var parts = file.split('\\');
        this.dest = outputFolder + '\\' + parts.slice(1, parts.length - 1).join('\\');
    }
}

function tsCompileClient(file) {
    var tsProject = $.typescript.createProject({
        removeComments: true,
        module: 'umd'
    });

    var paths = {
        src: globs.clientWithDefinitions,
        dest: destinations.client
    };

    computePaths.call(paths, file);

    var tsResult = gulp.src(paths.src)
        .pipe($.typescript(tsProject));

    return tsResult.js.pipe(isDist ? $.concat('app.js') : $.util.noop())
        .pipe($.ngAnnotate({gulpWarnings: false}))
        .pipe(isDist ? $.uglify() : $.util.noop())
        .pipe($.wrap({ src: './iife.txt'}))
        .pipe(gulp.dest(paths.dest))
        .pipe(browserSync.reload({stream: true}));
}

function tsCompileServer(file) {
    var tsProject = $.typescript.createProject({
        removeComments: true,
        module: 'umd'
    });

    var path = typeof file == 'function' ? globs.serverWithDefinitions : file;
    var tsResult = gulp.src(path)
        .pipe($.typescript(tsProject));

    return tsResult.js.pipe(isDist ? $.concat('app.js') : $.util.noop())
        .pipe(isDist ? $.uglify() : $.util.noop())
        .pipe(gulp.dest(destinations.server))
        .pipe(browserSync.reload({stream: true}));
}

gulp.task('ts-compile', gulp.series(tsCompileClient, tsCompileServer));

function views(file) {
    var srcPath = typeof file == 'function' ? globs.views : file;
    return gulp.src(srcPath)
        .pipe($.minifyHtml({
            empty: true,
            spare: true,
            quotes: true
        }))
        .pipe($.ngHtml2js({ moduleName: 'views' }))
        .pipe($.concat('views.js'))
        .pipe(isDist ? $.uglify() : $.util.noop())
        .pipe(gulp.dest(destinations.client))
        .pipe(browserSync.reload({ stream: true }));
}

gulp.task('clean', function (cb) {
    del(['dist/', 'build/']).then(path => { cb(); });
});

gulp.task('karma-watch', function(cb) {
    new karmaServer({
        configFile: __dirname + '/karma.conf.js'
    }, cb).start()
});

gulp.task('nodemon', function(cb) {
    return nodemon({
		script: 'build/server/app.js',
        watch: 'build/server/**/*.js'
	}).once('start', cb).on('restart', function() {
        setTimeout(function() {
            browserSync.reload({ stream: false });
        }, 1000);
    });
});

gulp.task('browser-sync', function () {
    return browserSync.init({
        open: false,
        proxy: "http://localhost:8081",
        reloadDelay: 1000,
        files: ["build/**/*.*"],
        browser: "google chrome",
        port: 8080,
    });
});

gulp.task('copy-vendor', function () {
  return gulp.src(isDist ? vendoredLibsMin : vendoredLibs)
    .pipe(gulp.dest(destinations.libs));
});

gulp.task('copy-assets', function () {
  return gulp.src(globs.assets)
    .pipe(gulp.dest(destinations.assets));
});

gulp.task('index', function () {
    var target = gulp.src(globs.index);
    var _injectPaths = isDist ? injectPaths.dist : injectPaths.dev;

    var injectOpts = {
        ignorePath: outputFolder,
        addRootSlash: false
    };

    var streams = _injectPaths.map(function (p) { return gulp.src(p); });
    return target
        .pipe($.inject(series(streams), injectOpts))
        .pipe(gulp.dest(destinations.index));
});

gulp.task('tsconfig-files', function () {
    return gulp.src(['src/**/*.ts'])
        .pipe($.tsconfigFiles());
});

gulp.task('watch', function() {
    //gulp.watch(globs.sass, gulp.series('sass'));
    gulp.watch(globs.sass).on('change', sass);
    gulp.watch(globs.client, gulp.series('ts-lint')).on('change', tsCompileClient);
    gulp.watch(globs.server).on('change', tsCompileServer);
    gulp.watch(globs.views).on('change', views);
    gulp.watch(globs.index, gulp.series('index'));
    gulp.watch(globs.assets, gulp.series('copy-assets'));
});

gulp.task('build',
    gulp.series(
        'tsconfig-files',
        //'clean', // not sure why we build, then clean, which deletes the built folder...
        gulp.parallel(sass, 'copy-assets', 'ts-compile', views, 'copy-vendor'),
        'index'
    )
);

gulp.task('default',
    gulp.series('build', gulp.parallel('browser-sync', 'nodemon', 'watch', 'karma-watch'))
);
<commit_msg>didn't actually need to remove clean<commit_after>var gulp = require('gulp');
var $ = require('gulp-load-plugins')();
var del = require('del');
var karmaServer = require('karma').Server;
var browserSync = require('browser-sync').create();
var nodemon = require('gulp-nodemon');
var series = require('stream-series');

// VARIABLES ======================================================
var isDist = $.util.env.type === 'dist';
var outputFolder = isDist ? 'dist' : 'build';

var globs = {
  sass: 'src/style/**/*.scss',
  views: 'src/client/**/*.html',
  assets: 'src/assets/**/*.*',
  // karma typescript preprocessor generates a bunch of .ktp.ts which gets picked
  // up by the watch, rinse and repeat
  types: ['src/types/**/*.ts', '!src/**/*.ktp.*'],
  client: ['src/client/**/*.ts', '!src/client/**/*.ktp.*'],
  clientWithDefinitions: ['src/client/**/*.ts', 'src/types/**/*.ts', '!src/**/*.ktp.*'],
  server: ['src/server/**/*.ts', '!src/server/**/*.ktp.*'],
  serverWithDefinitions: ['src/server/**/*.ts', 'src/types/**/*.ts', '!src/**/*.ktp.*'],
  integration: 'src/tests/integration/**/*.js',
  index: 'src/index.html'
};

var destinations = {
  css: outputFolder + "/style",
  client: outputFolder + "/client",
  server: outputFolder + "/server",
  libs: outputFolder + "/vendor",
  assets: outputFolder + "/assets",
  index: outputFolder + "/views"
};

// When adding a 3rd party we want to insert in the html, add it to
// vendoredLibs, order matters
var vendoredLibs = [
  'vendor/angular/angular.js',
  'vendor/ui-router/release/angular-ui-router.js',
];

// Will be filled automatically
var vendoredLibsMin = [];

var injectLibsPaths = {
  dev: [],
  dist: []
};

var injectPaths = {
  dev: [],
  dist: []
};

vendoredLibs.forEach(function(lib) {
    // take the filename
    var splittedPath = lib.split('/');
    var filename = splittedPath[splittedPath.length -1];
    injectLibsPaths.dev.push(destinations.libs + '/' + filename);
    // And get the minified version
    filename = filename.split('.')[0] + '.min.js';
    splittedPath[splittedPath.length - 1] = filename;
    vendoredLibsMin.push(splittedPath.join('/'));
    injectLibsPaths.dist.push(destinations.libs + '/' + filename);
});

['dev', 'dist'].forEach(function (env) {
    injectPaths[env] = injectLibsPaths[env].concat([
        destinations.client + '/*/**/*.module.js',
        destinations.client + '/app.module.js',
        isDist ? destinations.client + '/app.js' : destinations.client + '/**/!(*.module).js',
        destinations.client + '/views.js',
        destinations.css + '/*.css'
    ]);
});

// TASKS ===========================================================
function sass(file) {
    var path = typeof file == 'function' ? globs.sass : file;
    return gulp.src(path)
        .pipe($.sass({style: 'compressed'}).on('error', $.sass.logError))
        .pipe($.autoprefixer())  // defauls to > 1%, last 2 versions, Firefox ESR, Opera 12.1
        .pipe(gulp.dest(destinations.css))
        .pipe(browserSync.reload({stream: true}));
}

gulp.task('ts-lint', function () {
  return gulp.src(globs.client)
    .pipe($.tslint())
    .pipe($.tslint.report('full', {emitError: false}));
});

function computePaths(file) {
    if (typeof file != 'function') {
        this.src = file;
        var parts = file.split('\\');
        this.dest = outputFolder + '\\' + parts.slice(1, parts.length - 1).join('\\');
    }
}

function tsCompileClient(file) {
    var tsProject = $.typescript.createProject({
        removeComments: true,
        module: 'umd'
    });

    var paths = {
        src: globs.clientWithDefinitions,
        dest: destinations.client
    };

    computePaths.call(paths, file);

    var tsResult = gulp.src(paths.src)
        .pipe($.typescript(tsProject));

    return tsResult.js.pipe(isDist ? $.concat('app.js') : $.util.noop())
        .pipe($.ngAnnotate({gulpWarnings: false}))
        .pipe(isDist ? $.uglify() : $.util.noop())
        .pipe($.wrap({ src: './iife.txt'}))
        .pipe(gulp.dest(paths.dest))
        .pipe(browserSync.reload({stream: true}));
}

function tsCompileServer(file) {
    var tsProject = $.typescript.createProject({
        removeComments: true,
        module: 'umd'
    });

    var path = typeof file == 'function' ? globs.serverWithDefinitions : file;
    var tsResult = gulp.src(path)
        .pipe($.typescript(tsProject));

    return tsResult.js.pipe(isDist ? $.concat('app.js') : $.util.noop())
        .pipe(isDist ? $.uglify() : $.util.noop())
        .pipe(gulp.dest(destinations.server))
        .pipe(browserSync.reload({stream: true}));
}

gulp.task('ts-compile', gulp.series(tsCompileClient, tsCompileServer));

function views(file) {
    var srcPath = typeof file == 'function' ? globs.views : file;
    return gulp.src(srcPath)
        .pipe($.minifyHtml({
            empty: true,
            spare: true,
            quotes: true
        }))
        .pipe($.ngHtml2js({ moduleName: 'views' }))
        .pipe($.concat('views.js'))
        .pipe(isDist ? $.uglify() : $.util.noop())
        .pipe(gulp.dest(destinations.client))
        .pipe(browserSync.reload({ stream: true }));
}

gulp.task('clean', function (cb) {
    del(['dist/', 'build/']).then(path => { cb(); });
});

gulp.task('karma-watch', function(cb) {
    new karmaServer({
        configFile: __dirname + '/karma.conf.js'
    }, cb).start()
});

gulp.task('nodemon', function(cb) {
    return nodemon({
		script: 'build/server/app.js',
        watch: 'build/server/**/*.js'
	}).once('start', cb).on('restart', function() {
        setTimeout(function() {
            browserSync.reload({ stream: false });
        }, 1000);
    });
});

gulp.task('browser-sync', function () {
    return browserSync.init({
        open: false,
        proxy: "http://localhost:8081",
        reloadDelay: 1000,
        files: ["build/**/*.*"],
        browser: "google chrome",
        port: 8080,
    });
});

gulp.task('copy-vendor', function () {
  return gulp.src(isDist ? vendoredLibsMin : vendoredLibs)
    .pipe(gulp.dest(destinations.libs));
});

gulp.task('copy-assets', function () {
  return gulp.src(globs.assets)
    .pipe(gulp.dest(destinations.assets));
});

gulp.task('index', function () {
    var target = gulp.src(globs.index);
    var _injectPaths = isDist ? injectPaths.dist : injectPaths.dev;

    var injectOpts = {
        ignorePath: outputFolder,
        addRootSlash: false
    };

    var streams = _injectPaths.map(function (p) { return gulp.src(p); });
    return target
        .pipe($.inject(series(streams), injectOpts))
        .pipe(gulp.dest(destinations.index));
});

gulp.task('tsconfig-files', function () {
    return gulp.src(['src/**/*.ts'])
        .pipe($.tsconfigFiles());
});

gulp.task('watch', function() {
    //gulp.watch(globs.sass, gulp.series('sass'));
    gulp.watch(globs.sass).on('change', sass);
    gulp.watch(globs.client, gulp.series('ts-lint')).on('change', tsCompileClient);
    gulp.watch(globs.server).on('change', tsCompileServer);
    gulp.watch(globs.views).on('change', views);
    gulp.watch(globs.index, gulp.series('index'));
    gulp.watch(globs.assets, gulp.series('copy-assets'));
});

gulp.task('build',
    gulp.series(
        'tsconfig-files',
        'clean',
        gulp.parallel(sass, 'copy-assets', 'ts-compile', views, 'copy-vendor'),
        'index'
    )
);

gulp.task('default',
    gulp.series('build', gulp.parallel('browser-sync', 'nodemon', 'watch', 'karma-watch'))
);
<|endoftext|>


<commit_before>/*
* svg.rx.js
*/

// assert( SVG.Element.prototype );

/*
* Add '.track' to an 'SVG.Element'.
*
* Update observables when x/y/width/height are being changed (either explicitly
* or via dragging).
*/
(function() {
  var proto = SVG.Element.prototype;

  // 'was_f': the original method function
  // 'n': index in the 'this._observables' array to emit to
  // 'ctx': the 'this' context
  //
  // Returns: the wrapped method function
  //
  var fact = function (was_f, n, ctx) {
    return function (v) {                 // (Number) => ctx or () => Number

      if (v !== undefined) {
        // Making the change before informing observers (we don't want to trust
        // in the implementation details of '.onNext'). Then again, the order
        // probably doesn't even matter. AKa280915
        //
        was_f.call(ctx,v);

        var obs = ctx._observables ? ctx._observables[n] : null;
        if (obs) {
          obs.onNext(v);
        }

        return ctx;

      } else {
        return was_f.call(ctx);
      }

    };
  };

  // tbd. DAMN making JavaScript inheritence right is difficult (take ES6 to help!).
  //      (The problem here is getting the right 'this' context for the factory-made method.) AKa190915
  //
  // Note: 'move', 'cx', 'cy', 'center' get processed indirectly, because they forward to
  //      'x','y','width','height' calls.
  //
  var was_x = proto.x;
  var was_y = proto.y;
  var was_width = proto.width;
  var was_height = proto.height;

  proto.x = function(v) { return fact(was_x, 0, this)(v); };
  proto.y = function(v) { return fact(was_y, 1, this)(v); };
  proto.width = function(v) { return fact(was_width, 2, this)(v); };
  proto.height = function(v) { return fact(was_height, 3, this)(v); };

  SVG.extend( SVG.Element, {

    // Note: In practice the observables are likely 'Rx.BehaviorSubject's.
    //
    track: function (obsX, obsY, obsW, obsH) {    // ([Rx.BehaviorSubject], [Rx.BehaviorSubject], [Rx.BehaviorSubject], [Rx.BehaviorSubject]) => this

      // Note: This overrides earlier trackings. Should we do a '.dispose()' or is it okay just to leave them waiting for
      //      garbage collection? tbd. AKa200915
      //
      this._observables = [obsX, obsY, obsW, obsH];

      return this;
    },

    // Make the element's attribute ("x","y","width" or "height") follow an 'SVG.Observable'.
    //
    follow: function (attr, obs) {    // (String, SVG.Observable) => this
      var el= this;

      obs.subscribe( function (v) {
        el.attr(attr, v);
      } );

      return this;
    }
  });

})();
<commit_msg>small simplification; w/h were not used<commit_after>/*
* svg.rx.js
*/

// assert( SVG.Element.prototype );

/*
* Add '.track' to an 'SVG.Element'.
*
* Update observables when x/y/width/height are being changed (either explicitly
* or via dragging).
*/
(function() {
  var proto = SVG.Element.prototype;

  // 'was_f': the original method function
  // 'n': index in the 'this._observables' array to emit to
  // 'ctx': the 'this' context
  //
  // Returns: the wrapped method function
  //
  var fact = function (was_f, n, ctx) {
    return function (v) {                 // (Number) => ctx or () => Number

      if (v !== undefined) {
        // Making the change before informing observers (we don't want to trust
        // in the implementation details of '.onNext'). Then again, the order
        // probably doesn't even matter. AKa280915
        //
        was_f.call(ctx,v);

        var obs = ctx._observables ? ctx._observables[n] : null;
        if (obs) {
          obs.onNext(v);
        }

        return ctx;

      } else {
        return was_f.call(ctx);
      }

    };
  };

  // tbd. DAMN making JavaScript inheritence right is difficult (take ES6 to help!).
  //      (The problem here is getting the right 'this' context for the factory-made method.) AKa190915
  //
  // Note: 'move', 'cx', 'cy', 'center' get processed indirectly, because they forward to
  //      'x','y','width','height' calls.
  //
  var was_x = proto.x;
  var was_y = proto.y;
  //var was_width = proto.width;
  //var was_height = proto.height;

  proto.x = function(v) { return fact(was_x, 0, this)(v); };
  proto.y = function(v) { return fact(was_y, 1, this)(v); };
  //proto.width = function(v) { return fact(was_width, 2, this)(v); };
  //proto.height = function(v) { return fact(was_height, 3, this)(v); };

  SVG.extend( SVG.Element, {

    // Note: In practice the observables are likely 'Rx.BehaviorSubject's.
    //
    track: function (obsX, obsY /*, obsW, obsH*/) {    // ([Rx.BehaviorSubject], [Rx.BehaviorSubject], [Rx.BehaviorSubject], [Rx.BehaviorSubject]) => this

      // Note: This overrides earlier trackings. Should we do a '.dispose()' or is it okay just to leave them waiting for
      //      garbage collection? tbd. AKa200915
      //
      this._observables = [obsX, obsY /*, obsW, obsH*/];

      return this;
    },

    // Make the element's attribute ("x","y","width" or "height") follow an 'SVG.Observable'.
    //
    follow: function (attr, obs) {    // (String, SVG.Observable) => this
      var el= this;

      obs.subscribe( function (v) {
        el.attr(attr, v);
      } );

      return this;
    }
  });

})();
<|endoftext|>


<commit_before>'use strict';

var Vue = require('vue');
var assert = require('assert');
var test = require('tape');

// Returns a stub instance of CodeMirror.
global.CodeMirror = function() {
  return {
    focus: function() {},
    setOption: function() {},
    setValue: function(val) {}
  };
};

var localStorageExamples;

var exampleListInjector = require('!!vue?inject!../src/components/example-list.vue');
var ExampleList = Vue.extend(exampleListInjector({
  'global/window': {
    localStorage: {
      getItem: function() {
        return '[]';
      },
      setItem: function(name, value) {
        assert.equal(name, 'examples');
        localStorageExamples = JSON.parse(value);
      }
    }
  }
}));

test('adding and updating examples', function(t) {
  var vm = new ExampleList();
  vm.$mount();

  t.equal(vm.getSelected(), undefined);

  var id = vm.addExample();
  t.equal(vm.selectedId, id, 'adding selects the new example');
  t.deepEqual(vm.getSelected(), {text: '', startRule: null, shouldMatch: true});

  vm.setExample(id, 'woooo', 'Start');
  t.deepEqual(vm.getSelected(), {text: 'woooo', startRule: 'Start', shouldMatch: true});

  vm.setExample(id, 'woooo', 'Start', false);
  t.deepEqual(vm.getSelected(), {text: 'woooo', startRule: 'Start', shouldMatch: false});

  var id2 = vm.addExample();
  t.equal(vm.selectedId, id2);
  t.deepEqual(vm.getSelected(), {text: '', startRule: null, shouldMatch: true});

  t.end();
});

test('deleting', function(t) {
  var vm = new ExampleList();
  vm.$mount();

  localStorageExamples = [];

  // First, try adding a single example then deleting it.
  var id = vm.addExample();
  vm.setExample(id, 'first');

  Vue.nextTick(function() {
    vm.deleteExample(id);
    t.equal(vm.selectedId, null);
    t.equal(vm.getSelected(), undefined);

    // Now, add two examples.
    var id1 = vm.addExample();
    var id2 = vm.addExample();

    vm.setExample(id1, "hi i'm id1");
    t.equal(vm.selectedId, id2, 'the second example is selected');

    Vue.nextTick(function() {
      vm.deleteExample(id2);
      t.equal(vm.selectedId, id1, 'after deleting it, the first example is selected');

      // Now add one more example, and delete the first one.
      id2 = vm.addExample();
      vm.setExample(id2, "hi i'm id2");
      Vue.nextTick(function() {
        vm.deleteExample(id1);
        t.equal(vm.selectedId, id2, 'after deleting first example, second is selected');

        Vue.nextTick(function() {
          // localStorage should hold the id2 example.
          t.deepEqual(
              localStorageExamples,
              [{text: "hi i'm id2", startRule: null, shouldMatch: true}]);

          t.end();
        });
      });
    });
  });
});

test('toggleShouldMatch', function(t) {
  var vm = new ExampleList();
  vm.$mount();

  localStorageExamples = [];

  var id = vm.addExample();
  vm.toggleShouldMatch(id);

  var example = vm.getSelected();
  t.equal(example.shouldMatch, false);

  Vue.nextTick(function() {
    t.deepEqual(
        localStorageExamples,
        [{text: '', startRule: null, shouldMatch: false}],
        'new value is saved to localStorage');

    vm.toggleShouldMatch(id);  // Toggle it back.
    t.equal(example.shouldMatch, true);

    Vue.nextTick(function() {
      t.deepEqual(
          localStorageExamples,
          [{text: '', startRule: null, shouldMatch: true}],
          'new value is saved to localStorage');

      t.end();
    });
  });
});
<commit_msg>Add test for example pass/fail status.<commit_after>'use strict';

var Vue = require('vue');
var assert = require('assert');
var ohm = require('ohm-js');
var test = require('tape');

// Dependencies w/ mocks
// ---------------------

// Returns a stub instance of CodeMirror.
global.CodeMirror = function() {
  return {
    focus: function() {},
    setOption: function() {},
    setValue: function(val) {}
  };
};

var ohmEditor = require('../src/ohmEditor');  // Requires CodeMirror()

var localStorageExamples;

var exampleListInjector = require('!!vue?inject!../src/components/example-list.vue');
var ExampleList = Vue.extend(exampleListInjector({
  'global/window': {
    localStorage: {
      getItem: function() {
        return '[]';
      },
      setItem: function(name, value) {
        assert.equal(name, 'examples');
        localStorageExamples = JSON.parse(value);
      }
    }
  }
}));

// Helpers
// -------

function simulateGrammarEdit(source, cb) {
  ohmEditor.emit('change:grammar', source);
  Vue.nextTick(function() {
    ohmEditor.emit('parse:grammar', null, ohm.grammar(source), null);
    Vue.nextTick(cb);
  });
}

// Tests
// -----

test('adding and updating examples', function(t) {
  var vm = new ExampleList();
  vm.$mount();

  t.equal(vm.getSelected(), undefined);

  var id = vm.addExample();
  t.equal(vm.selectedId, id, 'adding selects the new example');
  t.deepEqual(vm.getSelected(), {text: '', startRule: null, shouldMatch: true});

  vm.setExample(id, 'woooo', 'Start');
  t.deepEqual(vm.getSelected(), {text: 'woooo', startRule: 'Start', shouldMatch: true});

  vm.setExample(id, 'woooo', 'Start', false);
  t.deepEqual(vm.getSelected(), {text: 'woooo', startRule: 'Start', shouldMatch: false});

  var id2 = vm.addExample();
  t.equal(vm.selectedId, id2);
  t.deepEqual(vm.getSelected(), {text: '', startRule: null, shouldMatch: true});

  t.end();
});

test('deleting', function(t) {
  var vm = new ExampleList();
  vm.$mount();

  localStorageExamples = [];

  // First, try adding a single example then deleting it.
  var id = vm.addExample();
  vm.setExample(id, 'first');

  Vue.nextTick(function() {
    vm.deleteExample(id);
    t.equal(vm.selectedId, null);
    t.equal(vm.getSelected(), undefined);

    // Now, add two examples.
    var id1 = vm.addExample();
    var id2 = vm.addExample();

    vm.setExample(id1, "hi i'm id1");
    t.equal(vm.selectedId, id2, 'the second example is selected');

    Vue.nextTick(function() {
      vm.deleteExample(id2);
      t.equal(vm.selectedId, id1, 'after deleting it, the first example is selected');

      // Now add one more example, and delete the first one.
      id2 = vm.addExample();
      vm.setExample(id2, "hi i'm id2");
      Vue.nextTick(function() {
        vm.deleteExample(id1);
        t.equal(vm.selectedId, id2, 'after deleting first example, second is selected');

        Vue.nextTick(function() {
          // localStorage should hold the id2 example.
          t.deepEqual(
              localStorageExamples,
              [{text: "hi i'm id2", startRule: null, shouldMatch: true}]);

          t.end();
        });
      });
    });
  });
});

test('toggleShouldMatch', function(t) {
  var vm = new ExampleList();
  vm.$mount();

  localStorageExamples = [];

  var id = vm.addExample();
  vm.toggleShouldMatch(id);

  var example = vm.getSelected();
  t.equal(example.shouldMatch, false);

  Vue.nextTick(function() {
    t.deepEqual(
        localStorageExamples,
        [{text: '', startRule: null, shouldMatch: false}],
        'new value is saved to localStorage');

    vm.toggleShouldMatch(id);  // Toggle it back.
    t.equal(example.shouldMatch, true);

    Vue.nextTick(function() {
      t.deepEqual(
          localStorageExamples,
          [{text: '', startRule: null, shouldMatch: true}],
          'new value is saved to localStorage');

      t.end();
    });
  });
});

test('pass/fail status', function(t) {
  var vm = new ExampleList();
  vm.$mount();

  var id = vm.addExample();
  vm.setExample(id, 'abcdefg');

  Vue.nextTick(function() {
    t.equal(vm.exampleStatus[id], undefined, 'status is undefined without a grammar');

    simulateGrammarEdit('G { start = letter+ }', function() {
      t.equal(vm.exampleStatus[id], 'pass');
      vm.exampleValues[id].shouldMatch = false;

      Vue.nextTick(function() {
        t.equal(vm.exampleStatus[id], 'fail', 'fails now that shouldMatch is false');

        simulateGrammarEdit('G { start = digit+ }', function() {
          t.equal(vm.exampleStatus[id], 'pass', 'passes when example fails matching');

          var id2 = vm.addExample();
          vm.setExample(id2, '123');
          Vue.nextTick(function() {
            t.equal(vm.exampleStatus[id2], 'pass');

            ohmEditor.emit('change:grammar', '');
            Vue.nextTick(function() {
              for (var k in vm.exampleStatus) {
                t.equal(vm.exampleStatus[k], undefined);
              }
              t.end();
            });
          });
        });
      });
    });
  });
});
<|endoftext|>


<commit_before>'use strict';

function tables_add(table){
    const headers = Array.from(table.firstElementChild.firstElementChild.children);

    for(const header in headers){
        if(headers[header].classList.contains('tables-nosort')){
            continue;

        }else if(headers[header].classList.contains('tables-main')){
            tables_column_main = header;
        }

        let type = 0;
        if(headers[header].classList.contains('tables-numbers')){
            type = 1;
        }

        headers[header].innerHTML += '
<input onclick="tables_sort(this,' + header + ',1,' + type + ')" type=button value=↑>'
          + '<input onclick="tables_sort(this,' + header + ',0,' + type + ')" type=button value=↓>
';
    }
}

function tables_format_number(value){
    return Number(value.replace(
      /,/g,
      ''
    ));
}

function tables_init(){
    const tables = document.getElementsByTagName('table');

    for(let i = 0; i < tables.length; i++){
        if(!tables[i].classList.contains('tables-nosort')){
            tables_add(tables[i]);
        }
    }
}

function tables_sort(element, column, direction, type){
    const table = element.closest('table');
    const rows = Array.from(table.firstElementChild.children);

    const column_content = [];
    let sorted_html = '';
    const used_rows = [];

    for(const row in rows){
        if(row === '0'){
            continue;
        }

        column_content.push(rows[row].children[column].innerText);
    }

    let sort_function = 0;
    if(type === 1){
        if(direction === 0){
            sort_function = function(a, b){
                return tables_format_number(b) - tables_format_number(a);
            };

        }else{
            sort_function = function(a, b){
                return tables_format_number(a) - tables_format_number(b);
            };
        }

    }else{
        const collator = new Intl.Collator();

        if(direction === 0){
            sort_function = function(a, b){
                return collator.compare(b, a);
            };

        }else{
            sort_function = collator.compare;
        }
    }
    column_content.sort(sort_function);

    for(const sorted in column_content){
        for(const row in rows){
            if(row === '0'){
                continue;
            }

            const parent = rows[row].children;

            if(parent[column].innerText === column_content[sorted]
              && !used_rows.includes(parent[tables_column_main].innerText)){
                sorted_html += rows[row].outerHTML;
                used_rows.push(parent[tables_column_main].innerText);

                break;
            }
        }
    }

    table.firstElementChild.innerHTML = rows[0].outerHTML + sorted_html;
}

globalThis.tables_column_main = 0;

tables_init();
<commit_msg>replaced arrows with triangles<commit_after>'use strict';

function tables_add(table){
    const headers = Array.from(table.firstElementChild.firstElementChild.children);

    for(const header in headers){
        if(headers[header].classList.contains('tables-nosort')){
            continue;

        }else if(headers[header].classList.contains('tables-main')){
            tables_column_main = header;
        }

        let type = 0;
        if(headers[header].classList.contains('tables-numbers')){
            type = 1;
        }

        headers[header].innerHTML += '
<input onclick="tables_sort(this,' + header + ',1,' + type + ')" type=button value=▲>'
          + '<input onclick="tables_sort(this,' + header + ',0,' + type + ')" type=button value=▼>
';
    }
}

function tables_format_number(value){
    return Number(value.replace(
      /,/g,
      ''
    ));
}

function tables_init(){
    const tables = document.getElementsByTagName('table');

    for(let i = 0; i < tables.length; i++){
        if(!tables[i].classList.contains('tables-nosort')){
            tables_add(tables[i]);
        }
    }
}

function tables_sort(element, column, direction, type){
    const table = element.closest('table');
    const rows = Array.from(table.firstElementChild.children);

    const column_content = [];
    let sorted_html = '';
    const used_rows = [];

    for(const row in rows){
        if(row === '0'){
            continue;
        }

        column_content.push(rows[row].children[column].innerText);
    }

    let sort_function = 0;
    if(type === 1){
        if(direction === 0){
            sort_function = function(a, b){
                return tables_format_number(b) - tables_format_number(a);
            };

        }else{
            sort_function = function(a, b){
                return tables_format_number(a) - tables_format_number(b);
            };
        }

    }else{
        const collator = new Intl.Collator();

        if(direction === 0){
            sort_function = function(a, b){
                return collator.compare(b, a);
            };

        }else{
            sort_function = collator.compare;
        }
    }
    column_content.sort(sort_function);

    for(const sorted in column_content){
        for(const row in rows){
            if(row === '0'){
                continue;
            }

            const parent = rows[row].children;

            if(parent[column].innerText === column_content[sorted]
              && !used_rows.includes(parent[tables_column_main].innerText)){
                sorted_html += rows[row].outerHTML;
                used_rows.push(parent[tables_column_main].innerText);

                break;
            }
        }
    }

    table.firstElementChild.innerHTML = rows[0].outerHTML + sorted_html;
}

globalThis.tables_column_main = 0;

tables_init();
<|endoftext|>


<commit_before>/*!
 * remarkable-regexp
 * Copyright (c) 2014 Alex Kocharin
 * MIT Licensed
 */

/**
 * Module dependencies.
 */

var util  = require('util')
var stuff = require('./utils')

/**
 * Expose `Plugin`
 */

module.exports = Plugin

/**
 * Constructor function
 */

function Plugin(regexp, replacer) {
  // return value should be a callable function
  // with strictly defined options passed by remarkable
  var self = function(remarkable, options) {
    self.options = options
    self.init(remarkable)
  }

  // initialize plugin object
  self.__proto__ = Plugin.prototype

  // clone regexp with all the flags
  var flags = (regexp.global     ? 'g' : '')
            + (regexp.multiline  ? 'm' : '')
            + (regexp.ignoreCase ? 'i' : '')

  self.regexp = RegExp(regexp.source, flags)

  // copy init options
  self.replacer = replacer

  // this plugin can be inserted multiple times,
  // so we're generating unique name for it
  self.id = 'regexp-' + String(Math.random).slice(2)

  return self
}

util.inherits(Plugin, Function)

// function that registers plugin with remarkable
Plugin.prototype.init = function(remarkable) {
  remarkable.inline.ruler.push(this.id, this.parse.bind(this))

  remarkable.renderer.rules[this.id] = this.render.bind(this)
}

Plugin.prototype.parse = function(state, silent) {
  // slowwww... maybe use an advanced regexp engine for this
  var match = this.regexp.exec(state.src.slice(state.pos))
  if (!match) return false

  // valid match found, now we need to advance cursor
  state.pos += match[0].length

  // don't insert any tokens in silent mode
  if (silent) return true

  state.push({
    type  : this.id,
    level : state.level,
    match : match,
  })

  return true
}

Plugin.prototype.render = function(tokens, id, options, env) {
  return this.replacer(tokens[id].match, stuff)
}

<commit_msg>bugfix: regexp should have "^" at the start<commit_after>/*!
 * remarkable-regexp
 * Copyright (c) 2014 Alex Kocharin
 * MIT Licensed
 */

/**
 * Module dependencies.
 */

var util  = require('util')
var stuff = require('./utils')

/**
 * Expose `Plugin`
 */

module.exports = Plugin

/**
 * Constructor function
 */

function Plugin(regexp, replacer) {
  // return value should be a callable function
  // with strictly defined options passed by remarkable
  var self = function(remarkable, options) {
    self.options = options
    self.init(remarkable)
  }

  // initialize plugin object
  self.__proto__ = Plugin.prototype

  // clone regexp with all the flags
  var flags = (regexp.global     ? 'g' : '')
            + (regexp.multiline  ? 'm' : '')
            + (regexp.ignoreCase ? 'i' : '')

  self.regexp = RegExp('^' + regexp.source, flags)

  // copy init options
  self.replacer = replacer

  // this plugin can be inserted multiple times,
  // so we're generating unique name for it
  self.id = 'regexp-' + String(Math.random).slice(2)

  return self
}

util.inherits(Plugin, Function)

// function that registers plugin with remarkable
Plugin.prototype.init = function(remarkable) {
  remarkable.inline.ruler.push(this.id, this.parse.bind(this))

  remarkable.renderer.rules[this.id] = this.render.bind(this)
}

Plugin.prototype.parse = function(state, silent) {
  // slowwww... maybe use an advanced regexp engine for this
  var match = this.regexp.exec(state.src.slice(state.pos))
  if (!match) return false

  // valid match found, now we need to advance cursor
  state.pos += match[0].length

  // don't insert any tokens in silent mode
  if (silent) return true

  state.push({
    type  : this.id,
    level : state.level,
    match : match,
  })

  return true
}

Plugin.prototype.render = function(tokens, id, options, env) {
  return this.replacer(tokens[id].match, stuff)
}

<|endoftext|>