<commit_before>/**
 * jqPlot
 * Pure JavaScript plotting plugin using jQuery
 *
 * Version: @VERSION
 *
 * Copyright (c) 2009-2011 Chris Leonello
 * jqPlot is currently available for use in all personal or commercial projects
 * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL
 * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can
 * choose the license that best suits your project and use it accordingly.
 *
 * Although not required, the author would appreciate an email letting him
 * know of any substantial use of jqPlot.  You can reach the author at:
 * chris at jqplot dot com or see http://www.jqplot.com/info.php .
 *
 * If you are feeling kind and generous, consider supporting the project by
 * making a donation at: http://www.jqplot.com/donate.php .
 *
 * sprintf functions contained in jqplot.sprintf.js by Ash Searle:
 *
 *     version 2007.04.27
 *     author Ash Searle
 *     http://hexmen.com/blog/2007/03/printf-sprintf/
 *     http://hexmen.com/js/sprintf.js
 *     The author (Ash Searle) has placed this code in the public domain:
 *     "This code is unrestricted: you are free to use it however you like."
 *
 */
(function($) {
    // Class: $.jqplot.CanvasGridRenderer
    // The default jqPlot grid renderer, creating a grid on a canvas element.
    // The renderer has no additional options beyond the <Grid> class.
    $.jqplot.CanvasGridRenderer = function(){
        this.shadowRenderer = new $.jqplot.ShadowRenderer();
    };

    // called with context of Grid object
    $.jqplot.CanvasGridRenderer.prototype.init = function(options) {
        this._ctx;
        $.extend(true, this, options);
        // set the shadow renderer options
        var sopts = {lineJoin:'miter', lineCap:'round', fill:false, isarc:false, angle:this.shadowAngle, offset:this.shadowOffset, alpha:this.shadowAlpha, depth:this.shadowDepth, lineWidth:this.shadowWidth, closePath:false, strokeStyle:this.shadowColor};
        this.renderer.shadowRenderer.init(sopts);
    };

    // called with context of Grid.
    $.jqplot.CanvasGridRenderer.prototype.createElement = function(plot) {
        var elem;
        // Memory Leaks patch
        if (this._elem) {
          if ($.jqplot.use_excanvas && window.G_vmlCanvasManager.uninitElement !== undefined) {
            elem = this._elem.get(0);
            window.G_vmlCanvasManager.uninitElement(elem);
            elem = null;
          }

          this._elem.emptyForce();
          this._elem = null;
        }

        elem = plot.canvasManager.getCanvas();

        var w = this._plotDimensions.width;
        var h = this._plotDimensions.height;
        elem.width = w;
        elem.height = h;
        this._elem = $(elem);
        this._elem.addClass('jqplot-grid-canvas');
        this._elem.css({ position: 'absolute', left: 0, top: 0 });

		elem = plot.canvasManager.initCanvas(elem);

        this._top = this._offsets.top;
        this._bottom = h - this._offsets.bottom;
        this._left = this._offsets.left;
        this._right = w - this._offsets.right;
        this._width = this._right - this._left;
        this._height = this._bottom - this._top;
        // avoid memory leak
        elem = null;
        return this._elem;
    };

    $.jqplot.CanvasGridRenderer.prototype.draw = function() {
        this._ctx = this._elem.get(0).getContext("2d");
        var ctx = this._ctx;
        var axes = this._axes;
        // Add the grid onto the grid canvas.  This is the bottom most layer.
        ctx.save();
        ctx.clearRect(0, 0, this._plotDimensions.width, this._plotDimensions.height);
        ctx.fillStyle = this.backgroundColor || this.background;
        ctx.fillRect(this._left, this._top, this._width, this._height);

        if (true) {
            ctx.save();
            ctx.lineJoin = 'miter';
            ctx.lineCap = 'butt';
            ctx.lineWidth = this.gridLineWidth;
            ctx.strokeStyle = this.gridLineColor;
            var b, e, s, m;
            var ax = ['xaxis', 'yaxis', 'x2axis', 'y2axis'];
            for (var i=4; i>0; i--) {
                var name = ax[i-1];
                var axis = axes[name];
                var ticks = axis._ticks;
                var numticks = ticks.length;
                if (axis.show) {
                    for (var j=numticks; j>0; j--) {
                        var t = ticks[j-1];
                        if (t.show) {
                            var pos = Math.round(axis.u2p(t.value)) + 0.5;
                            switch (name) {
                                case 'xaxis':
                                    // draw the grid line if we should
                                    if (t.showGridline && this.drawGridlines) {
                                        drawLine(pos, this._top, pos, this._bottom);
                                    }
                                    // else if first tick and show y baseline
                                    else if (j === 1 && axes.yaxis.show && axes.yaxis.drawBaseline) {
                                        drawLine(pos, this._top, pos, this._bottom);
                                    }
                                    // else if last tick and show y2 baseline
                                    else if (j === numticks && axes.y2axis.show && axes.y2axis.drawBaseline) {
                                        drawLine(pos, this._top, pos, this._bottom);
                                    }

                                    // draw the mark
	                                if (t.showMark && t.mark) {
                                        s = t.markSize;
                                        m = t.mark;
                                        var pos = Math.round(axis.u2p(t.value)) + 0.5;
                                        switch (m) {
                                            case 'outside':
                                                b = this._bottom;
                                                e = this._bottom+s;
                                                break;
                                            case 'inside':
                                                b = this._bottom-s;
                                                e = this._bottom;
                                                break;
                                            case 'cross':
                                                b = this._bottom-s;
                                                e = this._bottom+s;
                                                break;
                                            default:
                                                b = this._bottom;
                                                e = this._bottom+s;
                                                break;
                                        }
                                        // draw the shadow
                                        if (this.shadow) {
                                            this.renderer.shadowRenderer.draw(ctx, [[pos,b],[pos,e]], {lineCap:'butt', lineWidth:this.gridLineWidth, offset:this.gridLineWidth*0.75, depth:2, fill:false, closePath:false});
                                        }
                                        // draw the line
                                        drawLine(pos, b, pos, e);
                                    }
                                    break;
                                case 'yaxis':
                                    // draw the grid line
                                    if (t.showGridline && this.drawGridlines) {
                                        drawLine(this._right, pos, this._left, pos);
                                    }
                                    // else if first tick and show x baseline
                                    else if (j === 1 && axes.xaxis.show && axes.xaxis.drawBaseline) {
                                        drawLine(this._right, pos, this._left, pos);
                                    }
                                    // else if last tick and show x2 baseline
                                    else if (j === numticks && axes.x2axis.show && axes.x2axis.drawBaseline) {
                                        drawLine(this._right, pos, this._left, pos);
                                    }
                                    // draw the mark
                                    if (t.showMark && t.mark) {
                                        s = t.markSize;
                                        m = t.mark;
                                        var pos = Math.round(axis.u2p(t.value)) + 0.5;
                                        switch (m) {
                                            case 'outside':
                                                b = this._left-s;
                                                e = this._left;
                                                break;
                                            case 'inside':
                                                b = this._left;
                                                e = this._left+s;
                                                break;
                                            case 'cross':
                                                b = this._left-s;
                                                e = this._left+s;
                                                break;
                                            default:
                                                b = this._left-s;
                                                e = this._left;
                                                break;
                                                }
                                        // draw the shadow
                                        if (this.shadow) {
                                            this.renderer.shadowRenderer.draw(ctx, [[b, pos], [e, pos]], {lineCap:'butt', lineWidth:this.gridLineWidth*1.5, offset:this.gridLineWidth*0.75, fill:false, closePath:false});
                                        }
                                        drawLine(b, pos, e, pos, {strokeStyle:axis.borderColor});
                                    }
                                    break;
                                case 'x2axis':
                                    // draw the grid line
                                    if (t.showGridline && this.drawGridlines) {
                                        drawLine(pos, this._bottom, pos, this._top);
                                    }
                                    // else if first tick and show y baseline
                                    else if (j === 1 && axes.yaxis.show && axes.yaxis.drawBaseline) {
                                        drawLine(pos, this._bottom, pos, this._top);
                                    }
                                    // else if last tick and show y2 baseline
                                    else if (j === numticks && axes.y2axis.show && axes.y2axis.drawBaseline) {
                                        drawLine(pos, this._bottom, pos, this._top);
                                    }
                                    // draw the mark
                                    if (t.showMark && t.mark) {
                                        s = t.markSize;
                                        m = t.mark;
                                        var pos = Math.round(axis.u2p(t.value)) + 0.5;
                                        switch (m) {
                                            case 'outside':
                                                b = this._top-s;
                                                e = this._top;
                                                break;
                                            case 'inside':
                                                b = this._top;
                                                e = this._top+s;
                                                break;
                                            case 'cross':
                                                b = this._top-s;
                                                e = this._top+s;
                                                break;
                                            default:
                                                b = this._top-s;
                                                e = this._top;
                                                break;
                                                }
                                        // draw the shadow
                                        if (this.shadow) {
                                            this.renderer.shadowRenderer.draw(ctx, [[pos,b],[pos,e]], {lineCap:'butt', lineWidth:this.gridLineWidth, offset:this.gridLineWidth*0.75, depth:2, fill:false, closePath:false});
                                        }
                                        drawLine(pos, b, pos, e);
                                    }
                                    break;
                                case 'y2axis':
                                    // draw the grid line
                                    if (t.showGridline && this.drawGridlines) {
                                        drawLine(this._left, pos, this._right, pos);
                                    }
                                    // else if first tick and show x baseline
                                    else if (j === 1 && axes.xaxis.show && axes.xaxis.drawBaseline) {
                                        drawLine(this._left, pos, this._right, pos);
                                    }
                                    // else if last tick and show x2 baseline
                                    else if (j === numticks && axes.x2axis.show && axes.x2axis.drawBaseline) {
                                        drawLine(this._left, pos, this._right, pos);
                                    }
                                    // draw the mark
                                    if (t.showMark && t.mark) {
                                        s = t.markSize;
                                        m = t.mark;
                                        var pos = Math.round(axis.u2p(t.value)) + 0.5;
                                        switch (m) {
                                            case 'outside':
                                                b = this._right;
                                                e = this._right+s;
                                                break;
                                            case 'inside':
                                                b = this._right-s;
                                                e = this._right;
                                                break;
                                            case 'cross':
                                                b = this._right-s;
                                                e = this._right+s;
                                                break;
                                            default:
                                                b = this._right;
                                                e = this._right+s;
                                                break;
                                                }
                                        // draw the shadow
                                        if (this.shadow) {
                                            this.renderer.shadowRenderer.draw(ctx, [[b, pos], [e, pos]], {lineCap:'butt', lineWidth:this.gridLineWidth*1.5, offset:this.gridLineWidth*0.75, fill:false, closePath:false});
                                        }
                                        drawLine(b, pos, e, pos, {strokeStyle:axis.borderColor});
                                    }
                                    break;
                                default:
                                    break;
                            }
                        }
                    }
                    t = null;
                }
                axis = null;
                ticks = null;
            }
            // Now draw grid lines for additional y axes
            ax = ['y3axis', 'y4axis', 'y5axis', 'y6axis', 'y7axis', 'y8axis', 'y9axis'];
            for (var i=7; i>0; i--) {
                var axis = axes[ax[i-1]];
                var ticks = axis._ticks;
                if (axis.show) {
                    var tn = ticks[axis.numberTicks-1];
                    var t0 = ticks[0];
                    var left = axis.getLeft();
                    var points = [[left, tn.getTop() + tn.getHeight()/2], [left, t0.getTop() + t0.getHeight()/2 + 1.0]];
                    // draw the shadow
                    if (this.shadow) {
                        this.renderer.shadowRenderer.draw(ctx, points, {lineCap:'butt', fill:false, closePath:false});
                    }
                    // draw the line
                    drawLine(points[0][0], points[0][1], points[1][0], points[1][1], {lineCap:'butt', strokeStyle:axis.borderColor, lineWidth:axis.borderWidth});
                    // draw the tick marks
                    for (var j=ticks.length; j>0; j--) {
                        var t = ticks[j-1];
                        s = t.markSize;
                        m = t.mark;
                        var pos = Math.round(axis.u2p(t.value)) + 0.5;
                        if (t.showMark && t.mark) {
                            switch (m) {
                                case 'outside':
                                    b = left;
                                    e = left+s;
                                    break;
                                case 'inside':
                                    b = left-s;
                                    e = left;
                                    break;
                                case 'cross':
                                    b = left-s;
                                    e = left+s;
                                    break;
                                default:
                                    b = left;
                                    e = left+s;
                                    break;
                            }
                            points = [[b,pos], [e,pos]];
                            // draw the shadow
                            if (this.shadow) {
                                this.renderer.shadowRenderer.draw(ctx, points, {lineCap:'butt', lineWidth:this.gridLineWidth*1.5, offset:this.gridLineWidth*0.75, fill:false, closePath:false});
                            }
                            // draw the line
                            drawLine(b, pos, e, pos, {strokeStyle:axis.borderColor});
                        }
                        t = null;
                    }
                    t0 = null;
                }
                axis = null;
                ticks =  null;
            }

            ctx.restore();
        }

        function drawLine(bx, by, ex, ey, opts) {
            ctx.save();
            opts = opts || {};
            if (opts.lineWidth == null || opts.lineWidth != 0){
                $.extend(true, ctx, opts);
                ctx.beginPath();
                ctx.moveTo(bx, by);
                ctx.lineTo(ex, ey);
                ctx.stroke();
                ctx.restore();
            }
        }

        if (this.shadow) {
            var points = [[this._left, this._bottom], [this._right, this._bottom], [this._right, this._top]];
            this.renderer.shadowRenderer.draw(ctx, points);
        }
        // Now draw border around grid.  Use axis border definitions. start at
        // upper left and go clockwise.
        if (this.borderWidth != 0 && this.drawBorder) {
            drawLine (this._left, this._top, this._right, this._top, {lineCap:'round', strokeStyle:axes.x2axis.borderColor, lineWidth:axes.x2axis.borderWidth});
            drawLine (this._right, this._top, this._right, this._bottom, {lineCap:'round', strokeStyle:axes.y2axis.borderColor, lineWidth:axes.y2axis.borderWidth});
            drawLine (this._right, this._bottom, this._left, this._bottom, {lineCap:'round', strokeStyle:axes.xaxis.borderColor, lineWidth:axes.xaxis.borderWidth});
            drawLine (this._left, this._bottom, this._left, this._top, {lineCap:'round', strokeStyle:axes.yaxis.borderColor, lineWidth:axes.yaxis.borderWidth});
        }
        // ctx.lineWidth = this.borderWidth;
        // ctx.strokeStyle = this.borderColor;
        // ctx.strokeRect(this._left, this._top, this._width, this._height);

        ctx.restore();
        ctx =  null;
        axes = null;
    };
})(jQuery); <commit_msg>some refactoring<commit_after>/**
 * jqPlot
 * Pure JavaScript plotting plugin using jQuery
 *
 * Version: @VERSION
 *
 * Copyright (c) 2009-2011 Chris Leonello
 * jqPlot is currently available for use in all personal or commercial projects
 * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL
 * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can
 * choose the license that best suits your project and use it accordingly.
 *
 * Although not required, the author would appreciate an email letting him
 * know of any substantial use of jqPlot.  You can reach the author at:
 * chris at jqplot dot com or see http://www.jqplot.com/info.php .
 *
 * If you are feeling kind and generous, consider supporting the project by
 * making a donation at: http://www.jqplot.com/donate.php .
 *
 * sprintf functions contained in jqplot.sprintf.js by Ash Searle:
 *
 *     version 2007.04.27
 *     author Ash Searle
 *     http://hexmen.com/blog/2007/03/printf-sprintf/
 *     http://hexmen.com/js/sprintf.js
 *     The author (Ash Searle) has placed this code in the public domain:
 *     "This code is unrestricted: you are free to use it however you like."
 *
 */
(function($) {
    // Class: $.jqplot.CanvasGridRenderer
    // The default jqPlot grid renderer, creating a grid on a canvas element.
    // The renderer has no additional options beyond the <Grid> class.
    $.jqplot.CanvasGridRenderer = function(){
        this.shadowRenderer = new $.jqplot.ShadowRenderer();
    };

    // called with context of Grid object
    $.jqplot.CanvasGridRenderer.prototype.init = function(options) {
        this._ctx;
        $.extend(true, this, options);
        // set the shadow renderer options
        var sopts = {lineJoin:'miter', lineCap:'round', fill:false, isarc:false, angle:this.shadowAngle, offset:this.shadowOffset, alpha:this.shadowAlpha, depth:this.shadowDepth, lineWidth:this.shadowWidth, closePath:false, strokeStyle:this.shadowColor};
        this.renderer.shadowRenderer.init(sopts);
    };

    // called with context of Grid.
    $.jqplot.CanvasGridRenderer.prototype.createElement = function(plot) {
        var elem;
        // Memory Leaks patch
        if (this._elem) {
          if ($.jqplot.use_excanvas && window.G_vmlCanvasManager.uninitElement !== undefined) {
            elem = this._elem.get(0);
            window.G_vmlCanvasManager.uninitElement(elem);
            elem = null;
          }

          this._elem.emptyForce();
          this._elem = null;
        }

        elem = plot.canvasManager.getCanvas();

        var w = this._plotDimensions.width;
        var h = this._plotDimensions.height;
        elem.width = w;
        elem.height = h;
        this._elem = $(elem);
        this._elem.addClass('jqplot-grid-canvas');
        this._elem.css({ position: 'absolute', left: 0, top: 0 });

		elem = plot.canvasManager.initCanvas(elem);

        this._top = this._offsets.top;
        this._bottom = h - this._offsets.bottom;
        this._left = this._offsets.left;
        this._right = w - this._offsets.right;
        this._width = this._right - this._left;
        this._height = this._bottom - this._top;
        // avoid memory leak
        elem = null;
        return this._elem;
    };

    $.jqplot.CanvasGridRenderer.prototype.draw = function() {
        this._ctx = this._elem.get(0).getContext("2d");
        var ctx = this._ctx;
        var axes = this._axes;
        // Add the grid onto the grid canvas.  This is the bottom most layer.
        ctx.save();
        ctx.clearRect(0, 0, this._plotDimensions.width, this._plotDimensions.height);
        ctx.fillStyle = this.backgroundColor || this.background;
        ctx.fillRect(this._left, this._top, this._width, this._height);

        ctx.save();
        ctx.lineJoin = 'miter';
        ctx.lineCap = 'butt';
        ctx.lineWidth = this.gridLineWidth;
        ctx.strokeStyle = this.gridLineColor;
        var b, e, s, m;
        var ax = ['xaxis', 'yaxis', 'x2axis', 'y2axis'];
        for (var i=4; i>0; i--) {
            var name = ax[i-1];
            var axis = axes[name];
            var ticks = axis._ticks;
            var numticks = ticks.length;
            if (axis.show) {
                for (var j=numticks; j>0; j--) {
                    var t = ticks[j-1];
                    if (t.show) {
                        var pos = Math.round(axis.u2p(t.value)) + 0.5;
                        switch (name) {
                            case 'xaxis':
                                // draw the grid line if we should
                                if (t.showGridline && this.drawGridlines) {
                                    drawLine(pos, this._top, pos, this._bottom);
                                }
                                // else if first tick and show y baseline
                                else if (j === 1 && axes.yaxis.show && axes.yaxis.drawBaseline) {
                                    drawLine(pos, this._top, pos, this._bottom);
                                }
                                // else if last tick and show y2 baseline
                                else if (j === numticks && axes.y2axis.show && axes.y2axis.drawBaseline) {
                                    drawLine(pos, this._top, pos, this._bottom);
                                }

                                // draw the mark
                                if (t.showMark && t.mark) {
                                    s = t.markSize;
                                    m = t.mark;
                                    var pos = Math.round(axis.u2p(t.value)) + 0.5;
                                    switch (m) {
                                        case 'outside':
                                            b = this._bottom;
                                            e = this._bottom+s;
                                            break;
                                        case 'inside':
                                            b = this._bottom-s;
                                            e = this._bottom;
                                            break;
                                        case 'cross':
                                            b = this._bottom-s;
                                            e = this._bottom+s;
                                            break;
                                        default:
                                            b = this._bottom;
                                            e = this._bottom+s;
                                            break;
                                    }
                                    // draw the shadow
                                    if (this.shadow) {
                                        this.renderer.shadowRenderer.draw(ctx, [[pos,b],[pos,e]], {lineCap:'butt', lineWidth:this.gridLineWidth, offset:this.gridLineWidth*0.75, depth:2, fill:false, closePath:false});
                                    }
                                    // draw the line
                                    drawLine(pos, b, pos, e);
                                }
                                break;
                            case 'yaxis':
                                // draw the grid line
                                if (t.showGridline && this.drawGridlines) {
                                    drawLine(this._right, pos, this._left, pos);
                                }
                                // else if first tick and show x baseline
                                else if (j === 1 && axes.xaxis.show && axes.xaxis.drawBaseline) {
                                    drawLine(this._right, pos, this._left, pos);
                                }
                                // else if last tick and show x2 baseline
                                else if (j === numticks && axes.x2axis.show && axes.x2axis.drawBaseline) {
                                    drawLine(this._right, pos, this._left, pos);
                                }
                                // draw the mark
                                if (t.showMark && t.mark) {
                                    s = t.markSize;
                                    m = t.mark;
                                    var pos = Math.round(axis.u2p(t.value)) + 0.5;
                                    switch (m) {
                                        case 'outside':
                                            b = this._left-s;
                                            e = this._left;
                                            break;
                                        case 'inside':
                                            b = this._left;
                                            e = this._left+s;
                                            break;
                                        case 'cross':
                                            b = this._left-s;
                                            e = this._left+s;
                                            break;
                                        default:
                                            b = this._left-s;
                                            e = this._left;
                                            break;
                                            }
                                    // draw the shadow
                                    if (this.shadow) {
                                        this.renderer.shadowRenderer.draw(ctx, [[b, pos], [e, pos]], {lineCap:'butt', lineWidth:this.gridLineWidth*1.5, offset:this.gridLineWidth*0.75, fill:false, closePath:false});
                                    }
                                    drawLine(b, pos, e, pos, {strokeStyle:axis.borderColor});
                                }
                                break;
                            case 'x2axis':
                                // draw the grid line
                                if (t.showGridline && this.drawGridlines) {
                                    drawLine(pos, this._bottom, pos, this._top);
                                }
                                // else if first tick and show y baseline
                                else if (j === 1 && axes.yaxis.show && axes.yaxis.drawBaseline) {
                                    drawLine(pos, this._bottom, pos, this._top);
                                }
                                // else if last tick and show y2 baseline
                                else if (j === numticks && axes.y2axis.show && axes.y2axis.drawBaseline) {
                                    drawLine(pos, this._bottom, pos, this._top);
                                }
                                // draw the mark
                                if (t.showMark && t.mark) {
                                    s = t.markSize;
                                    m = t.mark;
                                    var pos = Math.round(axis.u2p(t.value)) + 0.5;
                                    switch (m) {
                                        case 'outside':
                                            b = this._top-s;
                                            e = this._top;
                                            break;
                                        case 'inside':
                                            b = this._top;
                                            e = this._top+s;
                                            break;
                                        case 'cross':
                                            b = this._top-s;
                                            e = this._top+s;
                                            break;
                                        default:
                                            b = this._top-s;
                                            e = this._top;
                                            break;
                                            }
                                    // draw the shadow
                                    if (this.shadow) {
                                        this.renderer.shadowRenderer.draw(ctx, [[pos,b],[pos,e]], {lineCap:'butt', lineWidth:this.gridLineWidth, offset:this.gridLineWidth*0.75, depth:2, fill:false, closePath:false});
                                    }
                                    drawLine(pos, b, pos, e);
                                }
                                break;
                            case 'y2axis':
                                // draw the grid line
                                if (t.showGridline && this.drawGridlines) {
                                    drawLine(this._left, pos, this._right, pos);
                                }
                                // else if first tick and show x baseline
                                else if (j === 1 && axes.xaxis.show && axes.xaxis.drawBaseline) {
                                    drawLine(this._left, pos, this._right, pos);
                                }
                                // else if last tick and show x2 baseline
                                else if (j === numticks && axes.x2axis.show && axes.x2axis.drawBaseline) {
                                    drawLine(this._left, pos, this._right, pos);
                                }
                                // draw the mark
                                if (t.showMark && t.mark) {
                                    s = t.markSize;
                                    m = t.mark;
                                    var pos = Math.round(axis.u2p(t.value)) + 0.5;
                                    switch (m) {
                                        case 'outside':
                                            b = this._right;
                                            e = this._right+s;
                                            break;
                                        case 'inside':
                                            b = this._right-s;
                                            e = this._right;
                                            break;
                                        case 'cross':
                                            b = this._right-s;
                                            e = this._right+s;
                                            break;
                                        default:
                                            b = this._right;
                                            e = this._right+s;
                                            break;
                                            }
                                    // draw the shadow
                                    if (this.shadow) {
                                        this.renderer.shadowRenderer.draw(ctx, [[b, pos], [e, pos]], {lineCap:'butt', lineWidth:this.gridLineWidth*1.5, offset:this.gridLineWidth*0.75, fill:false, closePath:false});
                                    }
                                    drawLine(b, pos, e, pos, {strokeStyle:axis.borderColor});
                                }
                                break;
                            default:
                                break;
                        }
                    }
                }
                t = null;
            }
            axis = null;
            ticks = null;
        }
        // Now draw grid lines for additional y axes
        //////
        // TO DO: handle yMidAxis
        //////
        ax = ['y3axis', 'y4axis', 'y5axis', 'y6axis', 'y7axis', 'y8axis', 'y9axis', 'yMidAxis'];
        for (var i=7; i>0; i--) {
            var axis = axes[ax[i-1]];
            var ticks = axis._ticks;
            if (axis.show) {
                var tn = ticks[axis.numberTicks-1];
                var t0 = ticks[0];
                var left = axis.getLeft();
                var points = [[left, tn.getTop() + tn.getHeight()/2], [left, t0.getTop() + t0.getHeight()/2 + 1.0]];
                // draw the shadow
                if (this.shadow) {
                    this.renderer.shadowRenderer.draw(ctx, points, {lineCap:'butt', fill:false, closePath:false});
                }
                // draw the line
                drawLine(points[0][0], points[0][1], points[1][0], points[1][1], {lineCap:'butt', strokeStyle:axis.borderColor, lineWidth:axis.borderWidth});
                // draw the tick marks
                for (var j=ticks.length; j>0; j--) {
                    var t = ticks[j-1];
                    s = t.markSize;
                    m = t.mark;
                    var pos = Math.round(axis.u2p(t.value)) + 0.5;
                    if (t.showMark && t.mark) {
                        switch (m) {
                            case 'outside':
                                b = left;
                                e = left+s;
                                break;
                            case 'inside':
                                b = left-s;
                                e = left;
                                break;
                            case 'cross':
                                b = left-s;
                                e = left+s;
                                break;
                            default:
                                b = left;
                                e = left+s;
                                break;
                        }
                        points = [[b,pos], [e,pos]];
                        // draw the shadow
                        if (this.shadow) {
                            this.renderer.shadowRenderer.draw(ctx, points, {lineCap:'butt', lineWidth:this.gridLineWidth*1.5, offset:this.gridLineWidth*0.75, fill:false, closePath:false});
                        }
                        // draw the line
                        drawLine(b, pos, e, pos, {strokeStyle:axis.borderColor});
                    }
                    t = null;
                }
                t0 = null;
            }
            axis = null;
            ticks =  null;
        }

        ctx.restore();

        function drawLine(bx, by, ex, ey, opts) {
            ctx.save();
            opts = opts || {};
            if (opts.lineWidth == null || opts.lineWidth != 0){
                $.extend(true, ctx, opts);
                ctx.beginPath();
                ctx.moveTo(bx, by);
                ctx.lineTo(ex, ey);
                ctx.stroke();
                ctx.restore();
            }
        }

        if (this.shadow) {
            var points = [[this._left, this._bottom], [this._right, this._bottom], [this._right, this._top]];
            this.renderer.shadowRenderer.draw(ctx, points);
        }
        // Now draw border around grid.  Use axis border definitions. start at
        // upper left and go clockwise.
        if (this.borderWidth != 0 && this.drawBorder) {
            drawLine (this._left, this._top, this._right, this._top, {lineCap:'round', strokeStyle:axes.x2axis.borderColor, lineWidth:axes.x2axis.borderWidth});
            drawLine (this._right, this._top, this._right, this._bottom, {lineCap:'round', strokeStyle:axes.y2axis.borderColor, lineWidth:axes.y2axis.borderWidth});
            drawLine (this._right, this._bottom, this._left, this._bottom, {lineCap:'round', strokeStyle:axes.xaxis.borderColor, lineWidth:axes.xaxis.borderWidth});
            drawLine (this._left, this._bottom, this._left, this._top, {lineCap:'round', strokeStyle:axes.yaxis.borderColor, lineWidth:axes.yaxis.borderWidth});
        }
        // ctx.lineWidth = this.borderWidth;
        // ctx.strokeStyle = this.borderColor;
        // ctx.strokeRect(this._left, this._top, this._width, this._height);

        ctx.restore();
        ctx =  null;
        axes = null;
    };
})(jQuery); <|endoftext|>


<commit_before>'use strict';

(function(factory) {
   if (typeof define === 'function' && define.amd) {
      define(['jquery'], factory);
   } else if (typeof module === 'object' && module.exports) {
      module.exports = function(root, jQuery) {
         if (jQuery === undefined) {
            jQuery = typeof window !== 'undefined' ? require('jquery') : require('jquery')(root);
         }

         factory(jQuery);

         return jQuery;
      };
   } else {
      factory(jQuery);
   }
}(function($) {
   /**
    * The jQuery plugin namespace
    *
    * @external "jQuery.fn"
    * @see {@link http://learn.jquery.com/plugins/|jQuery Plugins}
    */

   /**
    * @typedef SettingsHash
    * @type {Object}
    * @property {?string[]} [strings=null] A set of strings to show.
    * @property {boolean} [isEnabled=true] The state of the animation. If the value is false, the
    * animation is in a pause state.
    * @property {number} [fadeIn=300] The number of milliseconds the element appears by fading to opaque
    * @property {number} [duration=500] The number of milliseconds the element stays visible
    * @property {number} [fadeOut=300] The number of milliseconds the element disappears by fading to transparent
    * @property {string} [selection="random"] The order of selection of the strings. The possible values are:
    * "random", "ascending", "descending"
    * @property {number} [repeat=-1] The number of times to repeat the animation. -1 means unlimited
    * @property {number} [pause=0] The time (milliseconds) between animations. Set to "random" to have
    * a random time
    * @property {number} [fontMinSize=7] The minimum font size of the strings
    * @property {number} [fontMaxSize=28] The maximum font size of the strings
    * @property {string} [fontUnit="px"] The unit to use for the font's size. The value accepted are all those
    * accepted by the browsers, for example: "px", "em", "rem" and so on.
    */

   /**
    * @typedef MethodsHash
    * @type {Object}
    * @property {init} init The method to initialize the plugin
    * @property {destroy} destroy The method to stop the animation and clean all the resources
    * @property {disable} disable The method to disable the animation
    * @property {enable} enable The method to enable the animation
    * @property {toggle} toggle The method to toggle the animation
    */

   /**
    * The namespace used to store the data
    *
    * @type {string}
    */
   var namespace = 'audero-flashing-text';

   /**
    * Calculates the index of the string to show next
    *
    * @param {number} index The index of the currently shown string
    * @param {SettingsHash} options An object of options to customize the plugin
    *
    * @return {number}
    */
   function getNextIndex(index, options) {
      var nextIndex;

      if (options.selection === 'ascending') {
         nextIndex = (index + 1) % options.strings.length;
      } else if (options.selection === 'descending') {
         nextIndex = index === 0 ? options.strings.length : index - 1;
      } else {
         nextIndex = Math.floor(Math.random() * options.strings.length);
      }

      return nextIndex;
   }

   /**
    * Creates the animation for the element provided
    *
    * @param {jQuery} $element The jQuery collection, containing a single element,
    * on which the string will be shown
    * @param {number} index The index of the string to show
    */
   function animateText($element, index) {
      // Save the last used index. In this way, if the user disables the animation,
      // once it run again, the plugin know the last used string
      var options = $.extend(
         $element.data(namespace),
         {
            lastIndex: index
         }
      );
      var $text = $('')
         .text(options.strings[index])
         .css({
            position: 'absolute',
            display: 'none',
            fontSize: Math.random() * options.fontMaxSize + options.fontMinSize + options.fontUnit
         })
         .appendTo($element)
         .fadeIn(options.fadeIn)
         .animate(
            {
               opacity: 1
            },
            options.duration // Simulate delay
         )
         .fadeOut(options.fadeOut, function() {
            var nextIndex = getNextIndex(index, options);

            // Remove the current element
            $(this).remove();

            if (options.repeat > 0) {
               options.repeat--;
            }

            // Generate a random pause
            var time = options.pause === 'random' ? Math.random() * 3000 : options.pause;

            $element.data(namespace, options);

            // If the animation is enabled test if it should run again
            if (options.isEnabled === true) {
               // If the repetition limit is not reached, the animation method will run again
               if (options.repeat !== 0) {
                  setTimeout(function() {
                     animateText($element, nextIndex);
                  }, time);
               } else {
                  destroy($element);
               }
            }
         });

      // Set the position so the element will fit the box's size
      var posX = Math.floor(Math.random() * ($element.width() - $text.outerWidth()));
      var posY = Math.floor(Math.random() * ($element.height() - $text.outerHeight()));

      // Set the position of the text
      $text.css({
         left: posX,
         top: posY
      });
   }

   /**
    * Tests if the element has an animation running
    *
    * @param {jQuery} $element The jQuery collection, containing a single element, to test
    *
    * @return {boolean}
    */
   function isRunning($element) {
      var options = $element.data(namespace);

      return $.type(options) === 'object' && options.isEnabled === true;
   }

   /**
    * Checks if the options have valid values
    *
    * @param {SettingsHash} options An object of options to customize the plugin
    */
   function validateOptions(options) {
      /* jshint -W074 */
      if (options.fadeIn < 0) {
         $.error('The fadeIn property of jQuery.auderoFlashingText can\'t be a negative number');
      }

      if (options.duration < 0) {
         $.error('The duration property of jQuery.auderoFlashingText can\'t be a negative number');
      }

      if (options.fadeOut < 0) {
         $.error('The fadeOut property of jQuery.auderoFlashingText can\'t be a negative number');
      }

      if ($.inArray(options.selection, ['random', 'ascending', 'descending']) < 0) {
         $.error('The selection property of jQuery.auderoFlashingText should have one of these values: random, ascending, descending');
      }

      if (options.repeat <= 0 && options.repeat !== -1) {
         $.error('jQuery.auderoFlashingText should run at least one time');
      }

      if (options.pause !== 'random' && options.pause < 0) {
         $.error('jQuery.auderoFlashingText should run the animation with a pause equal to or greater than zero');
      }

      if (options.speed <= 0) {
         $.error('jQuery.auderoFlashingText should run the animation with a speed greater than zero');
      }

      if (options.fontMinSize <= 0) {
         $.error('The fontMinSize property of jQuery.auderoFlashingText can\'t be a negative number');
      }

      if (options.fontMaxSize <= 0) {
         $.error('The fontMaxSize property of jQuery.auderoFlashingText can\'t be a negative number');
      }
   }

   /**
    * Initializes the plugin
    *
    * @callback init
    *
    * @param {jQuery} $elements The jQuery collection to work with
    * @param {SettingsHash} options An object of options to customize the plugin
    *
    * @return {jQuery}
    */
   function init($elements, options) {
      /* jshint +W074 */
      if ($.type(options) !== 'object') {
         options = {};
      }

      if (!options.strings && $elements.find(':first').length !== $elements.length) {
         $.error('To run jQuery.auderoFlashingText if you don\'t specify the texts to show, each element must have at least a child');
      }

      // By default the order is ascending
      var index = 0;

      if (options.selection === 'descending') {
         index = options.strings.length - 1;
      } else if (options.selection === 'random') {
         index = Math.floor(Math.random() * options.strings.length);
      }

      options = $.extend({}, $.fn.auderoFlashingText.defaults, options);

      // Check if the properties have valid values
      validateOptions(options);
      $elements
         .css('position', 'relative')
         .children()
         .css('visibility', 'hidden');
      var $current;

      $elements.each(function() {
         $current = $(this);

         if (options.strings === null) {
            options.strings = $current.children().map(function() {
               return $(this).text();
            });
         }

         // Clone the options object so elements will have the same values without sharing the same object
         $current.data(namespace, $.extend({}, options));

         if (options.isEnabled === true) {
            animateText($current, index);
         }
      });

      return $elements;
   }

   /**
    * Immediately completes the currently running animation for each element
    * in the jQuery collection and blocks those left
    *
    * @callback disable
    *
    * @param {jQuery} $elements The jQuery collection to work with
    *
    * @return {jQuery}
    */
   function disable($elements) {
      $elements.each(function() {
         var $current = $(this);

         if (isRunning($current)) {
            $current.data(namespace).isEnabled = false;
            $current.stop(true, true);
         }
      });

      return $elements;
   }

   /**
    * Restarts the animation for each element in the jQuery collection
    *
    * @callback enable
    *
    * @param {jQuery} $elements The jQuery collection to work with
    *
    * @return {jQuery}
    */
   function enable($elements) {
      $elements.each(function() {
         var $current = $(this);

         if (!isRunning($current)) {
            $current.data(namespace).isEnabled = true;
            animateText($current, $current.data(namespace).lastIndex);
         }
      });

      return $elements;
   }

   /**
    * Disables the animation for elements that are currently running it and
    * enables the animation again for elements that are not running it
    *
    * @callback toggle
    *
    * @param {jQuery} $elements The jQuery collection to work with
    *
    * @return {jQuery}
    */
   function toggle($elements) {
      $elements.each(function() {
         var $current = $(this);

         if (isRunning($current)) {
            disable($current);
         } else {
            enable($current);
         }
      });

      return $elements;
   }

   /**
    * Stops the animation and clean all the resources
    *
    * @callback destroy
    *
    * @param {jQuery} $elements The jQuery collection to work with
    *
    * @return {jQuery}
    */
   function destroy($elements) {
      $elements.each(function() {
         var $current = $(this);

         if (isRunning($current)) {
            // Removes the floating text
            $current
               .children(':animated')
               .stop(true)
               .fadeOut($current.data(namespace).fadeOut)
               .remove();
         }

         $current
            .css('position', 'inherit')
            .removeData(namespace);
      });

      // Restore the default visibility
      $elements
         .children()
         .css('visibility', 'visible');

      return $elements;
   }

   /**
    * The object containing all the public methods
    *
    * @type {MethodsHash}
    */
   var methods = {
      init: init,
      enable: enable,
      disable: disable,
      toggle: toggle,
      destroy: destroy
   };

   /**
    * Creates a flashing text effect for one or more elements
    *
    * @function external:"jQuery.fn".auderoFlashingText
    *
    * @param {SettingsHash} [method] The options to initialize the plugin or the name of the method to call
    *
    * @return {jQuery}
    */
   $.fn.auderoFlashingText = function(method) {
      var args = Array.prototype.slice.call(arguments);

      if (methods[method]) {
         return methods[method].apply(this, [this].concat(args.splice(0, 1)));
      } else if ($.type(method) === 'object' || !method) {
         return methods.init.apply(this, [this].concat(args));
      } else {
         $.error('Method ' + method + ' does not exist on jQuery.auderoFlashingText');
      }
   };

   /**
    * The default options of the plugin
    *
    * @type {SettingsHash}
    */
   $.fn.auderoFlashingText.defaults = {
      strings: null,
      isEnabled: true,
      fadeIn: 300,
      duration: 500,
      fadeOut: 300,
      selection: 'random',
      repeat: -1,
      pause: 0,
      fontMinSize: 7,
      fontMaxSize: 28,
      fontUnit: 'px'
   };
}));<commit_msg>Updated internal documentation<commit_after>'use strict';

(function(factory) {
   if (typeof define === 'function' && define.amd) {
      define(['jquery'], factory);
   } else if (typeof module === 'object' && module.exports) {
      module.exports = function(root, jQuery) {
         if (jQuery === undefined) {
            jQuery = typeof window !== 'undefined' ? require('jquery') : require('jquery')(root);
         }

         factory(jQuery);

         return jQuery;
      };
   } else {
      factory(jQuery);
   }
}(function($) {
   /**
    * The jQuery plugin namespace
    *
    * @external "jQuery.fn"
    * @see {@link http://learn.jquery.com/plugins/|jQuery Plugins}
    */

   /**
    * @typedef SettingsHash
    * @type {Object}
    * @property {?string[]} [strings=null] A set of strings to show.
    * @property {boolean} [isEnabled=true] The state of the animation. If the value is false, the
    * animation is in a pause state.
    * @property {number} [fadeIn=300] The number of milliseconds the element appears by fading to opaque
    * @property {number} [duration=500] The number of milliseconds the element stays visible
    * @property {number} [fadeOut=300] The number of milliseconds the element disappears by fading to transparent
    * @property {string} [selection="random"] The order of selection of the strings. The possible values are:
    * "random", "ascending", "descending"
    * @property {number} [repeat=-1] The number of times to repeat the animation. -1 means unlimited
    * @property {number} [pause=0] The time (milliseconds) between animations. Set to "random" to have
    * a random time
    * @property {number} [fontMinSize=7] The minimum font size of the strings
    * @property {number} [fontMaxSize=28] The maximum font size of the strings
    * @property {string} [fontUnit="px"] The unit to use for the font's size. The value accepted are all those
    * accepted by the browsers, for example: "px", "em", "rem" and so on.
    */

   /**
    * @typedef MethodsHash
    * @type {Object}
    * @property {init} init The method to initialize the plugin
    * @property {destroy} destroy The method to stop the animation and clean all the resources
    * @property {disable} disable The method to disable the animation
    * @property {enable} enable The method to enable the animation
    * @property {toggle} toggle The method to toggle the animation
    */

   /**
    * The namespace used to store the data
    *
    * @type {string}
    */
   var namespace = 'audero-flashing-text';

   /**
    * Calculates the index of the string to show next
    *
    * @param {number} index The index of the currently shown string
    * @param {SettingsHash} options An object of options to customize the plugin
    *
    * @return {number}
    */
   function getNextIndex(index, options) {
      var nextIndex;

      if (options.selection === 'ascending') {
         nextIndex = (index + 1) % options.strings.length;
      } else if (options.selection === 'descending') {
         nextIndex = index === 0 ? options.strings.length : index - 1;
      } else {
         nextIndex = Math.floor(Math.random() * options.strings.length);
      }

      return nextIndex;
   }

   /**
    * Creates the animation for the element provided
    *
    * @param {jQuery} $element The jQuery collection, containing a single element,
    * on which the string will be shown
    * @param {number} index The index of the string to show
    */
   function animateText($element, index) {
      // Save the last used index. In this way, if the user disables the animation,
      // once it run again, the plugin know the last used string
      var options = $.extend(
         $element.data(namespace),
         {
            lastIndex: index
         }
      );
      var $text = $('')
         .text(options.strings[index])
         .css({
            position: 'absolute',
            display: 'none',
            fontSize: Math.random() * options.fontMaxSize + options.fontMinSize + options.fontUnit
         })
         .appendTo($element)
         .fadeIn(options.fadeIn)
         .animate(
            {
               opacity: 1
            },
            options.duration // Simulate delay
         )
         .fadeOut(options.fadeOut, function() {
            var nextIndex = getNextIndex(index, options);

            // Remove the current element
            $(this).remove();

            if (options.repeat > 0) {
               options.repeat--;
            }

            // Generate a random pause
            var time = options.pause === 'random' ? Math.random() * 3000 : options.pause;

            $element.data(namespace, options);

            // If the animation is enabled test if it should run again
            if (options.isEnabled === true) {
               // If the repetition limit is not reached, the animation method will run again
               if (options.repeat !== 0) {
                  setTimeout(function() {
                     animateText($element, nextIndex);
                  }, time);
               } else {
                  destroy($element);
               }
            }
         });

      // Set the position so the element will fit the box's size
      var posX = Math.floor(Math.random() * ($element.width() - $text.outerWidth()));
      var posY = Math.floor(Math.random() * ($element.height() - $text.outerHeight()));

      // Set the position of the text
      $text.css({
         left: posX,
         top: posY
      });
   }

   /**
    * Tests if the element has an animation running
    *
    * @param {jQuery} $element The jQuery collection, containing a single element, to test
    *
    * @return {boolean}
    */
   function isRunning($element) {
      var options = $element.data(namespace);

      return $.type(options) === 'object' && options.isEnabled === true;
   }

   /**
    * Checks if the options have valid values
    *
    * @param {SettingsHash} options An object of options to customize the plugin
    */
   function validateOptions(options) {
      /* jshint -W074 */
      if (options.fadeIn < 0) {
         $.error('The fadeIn property of jQuery.auderoFlashingText can\'t be a negative number');
      }

      if (options.duration < 0) {
         $.error('The duration property of jQuery.auderoFlashingText can\'t be a negative number');
      }

      if (options.fadeOut < 0) {
         $.error('The fadeOut property of jQuery.auderoFlashingText can\'t be a negative number');
      }

      if ($.inArray(options.selection, ['random', 'ascending', 'descending']) < 0) {
         $.error('The selection property of jQuery.auderoFlashingText should have one of these values: random, ascending, descending');
      }

      if (options.repeat <= 0 && options.repeat !== -1) {
         $.error('jQuery.auderoFlashingText should run at least one time');
      }

      if (options.pause !== 'random' && options.pause < 0) {
         $.error('jQuery.auderoFlashingText should run the animation with a pause equal to or greater than zero');
      }

      if (options.speed <= 0) {
         $.error('jQuery.auderoFlashingText should run the animation with a speed greater than zero');
      }

      if (options.fontMinSize <= 0) {
         $.error('The fontMinSize property of jQuery.auderoFlashingText can\'t be a negative number');
      }

      if (options.fontMaxSize <= 0) {
         $.error('The fontMaxSize property of jQuery.auderoFlashingText can\'t be a negative number');
      }
   }

   /**
    * Initializes the plugin
    *
    * @callback init
    *
    * @param {jQuery} $elements The jQuery collection to work with
    * @param {SettingsHash} options An object of options to customize the plugin
    *
    * @return {jQuery}
    */
   function init($elements, options) {
      /* jshint +W074 */
      if ($.type(options) !== 'object') {
         options = {};
      }

      if (!options.strings && $elements.find(':first').length !== $elements.length) {
         $.error('To run jQuery.auderoFlashingText if you don\'t specify the texts to show, each element must have at least a child');
      }

      // By default the order is ascending
      var index = 0;

      if (options.selection === 'descending') {
         index = options.strings.length - 1;
      } else if (options.selection === 'random') {
         index = Math.floor(Math.random() * options.strings.length);
      }

      options = $.extend({}, $.fn.auderoFlashingText.defaults, options);

      // Check if the properties have valid values
      validateOptions(options);
      $elements
         .css('position', 'relative')
         .children()
         .css('visibility', 'hidden');
      var $current;

      $elements.each(function() {
         $current = $(this);

         if (options.strings === null) {
            options.strings = $current.children().map(function() {
               return $(this).text();
            });
         }

         // Clone the options object so elements will have the same values without sharing the same object
         $current.data(namespace, $.extend({}, options));

         if (options.isEnabled === true) {
            animateText($current, index);
         }
      });

      return $elements;
   }

   /**
    * Immediately completes the currently running animation for each element
    * in the jQuery collection and blocks those left
    *
    * @callback disable
    *
    * @param {jQuery} $elements The jQuery collection to work with
    *
    * @return {jQuery}
    */
   function disable($elements) {
      $elements.each(function() {
         var $current = $(this);

         if (isRunning($current)) {
            $current.data(namespace).isEnabled = false;
            $current.stop(true, true);
         }
      });

      return $elements;
   }

   /**
    * Restarts the animation for each element in the jQuery collection
    *
    * @callback enable
    *
    * @param {jQuery} $elements The jQuery collection to work with
    *
    * @return {jQuery}
    */
   function enable($elements) {
      $elements.each(function() {
         var $current = $(this);

         if (!isRunning($current)) {
            $current.data(namespace).isEnabled = true;
            animateText($current, $current.data(namespace).lastIndex);
         }
      });

      return $elements;
   }

   /**
    * Disables the animation for elements that are currently running it and
    * enables the animation again for elements that are not running it
    *
    * @callback toggle
    *
    * @param {jQuery} $elements The jQuery collection to work with
    *
    * @return {jQuery}
    */
   function toggle($elements) {
      $elements.each(function() {
         var $current = $(this);

         if (isRunning($current)) {
            disable($current);
         } else {
            enable($current);
         }
      });

      return $elements;
   }

   /**
    * Stops the animation and clean all the resources
    *
    * @callback destroy
    *
    * @param {jQuery} $elements The jQuery collection to work with
    *
    * @return {jQuery}
    */
   function destroy($elements) {
      $elements.each(function() {
         var $current = $(this);

         if (isRunning($current)) {
            // Removes the floating text
            $current
               .children(':animated')
               .stop(true)
               .fadeOut($current.data(namespace).fadeOut)
               .remove();
         }

         $current
            .css('position', 'inherit')
            .removeData(namespace);
      });

      // Restore the default visibility
      $elements
         .children()
         .css('visibility', 'visible');

      return $elements;
   }

   /**
    * The object containing all the public methods
    *
    * @type {MethodsHash}
    */
   var methods = {
      init: init,
      enable: enable,
      disable: disable,
      toggle: toggle,
      destroy: destroy
   };

   /**
    * Creates a flashing text effect for one or more elements
    *
    * @function external:"jQuery.fn".auderoFlashingText
    *
    * @param {(SettingsHash|string)} [method] The options to initialize the plugin or the name of the method to call
    *
    * @return {jQuery}
    */
   $.fn.auderoFlashingText = function(method) {
      var args = Array.prototype.slice.call(arguments);

      if (methods[method]) {
         return methods[method].apply(this, [this].concat(args.splice(0, 1)));
      } else if ($.type(method) === 'object' || !method) {
         return methods.init.apply(this, [this].concat(args));
      } else {
         $.error('Method ' + method + ' does not exist on jQuery.auderoFlashingText');
      }
   };

   /**
    * The default options of the plugin
    *
    * @type {SettingsHash}
    */
   $.fn.auderoFlashingText.defaults = {
      strings: null,
      isEnabled: true,
      fadeIn: 300,
      duration: 500,
      fadeOut: 300,
      selection: 'random',
      repeat: -1,
      pause: 0,
      fontMinSize: 7,
      fontMaxSize: 28,
      fontUnit: 'px'
   };
}));<|endoftext|>


<commit_before>import {Hooks} from 'PluginSDK';
import React from 'react';

import FormUtil from '../utils/FormUtil';
import HostUtil from '../utils/HostUtil';
import Icon from './Icon';
import SchemaForm from './SchemaForm';
import SchemaFormUtil from '../utils/SchemaFormUtil';
import SchemaUtil from '../utils/SchemaUtil';
import StringUtil from '../utils/StringUtil';
import VirtualNetworksStore from '../stores/VirtualNetworksStore';

const METHODS_TO_BIND = [
  'handleFormChange',
  'onVirtualNetworksStoreSuccess',
  'validateForm'
];

const DUPLICABLE_FIELDS_TO_WATCH = {
  healthChecks: {
    fields: ['protocol', 'portType'],
    forceUpdate: true
  },
  ports: {
    fields: ['discovery', 'protocol'],
    forceUpdate: true
  }
};

const FIELDS_TO_WATCH = {
  networkType: {
    forceUpdate: true
  },
  image: {
    forceUpdate: false
  }
};

class ServiceForm extends SchemaForm {
  constructor() {
    super(...arguments);

    METHODS_TO_BIND.forEach((method) => {
      this[method] = this[method].bind(this);
    });

    this.store_listeners = Hooks.applyFilter('serviceFormStoreListeners', [
      {
        name: 'virtualNetworks',
        events: ['success'],
        suppressUpdate: true
      }
    ]);
  }

  componentWillMount() {
    this.multipleDefinition = this.getNewDefinition();
    this.props.getTriggerSubmit(this.handleExternalSubmit);
  }

  componentDidMount() {
    super.componentDidMount(...arguments);
    Hooks.doAction('serviceFormMount', this);
    // Update definition and build model
    this.updateDefinitions();
    this.bananas(true, 0);
  }

  onVirtualNetworksStoreSuccess() {
    this.updateDefinitions();
  }

  shouldUpdateDefinition(changes, eventType, fieldName) {
    let propKey = FormUtil.getPropKey(fieldName);

    return Object.keys(changes).some(function (changeKey) {
      let tab = FormUtil.getProp(changeKey);

      return ((tab in DUPLICABLE_FIELDS_TO_WATCH)
        && (DUPLICABLE_FIELDS_TO_WATCH[tab].fields.includes(propKey)
        && DUPLICABLE_FIELDS_TO_WATCH[tab].forceUpdate))
        || ((fieldName in FIELDS_TO_WATCH
        && FIELDS_TO_WATCH[fieldName].forceUpdate));
    });
  }

  handleFormChange(changes, eventObj) {
    let {eventType, fieldName} = eventObj;
    let shouldUpdateDefinition = this.shouldUpdateDefinition(
      changes, eventType, fieldName
    );

    if (eventType === 'change' || shouldUpdateDefinition) {
      this.bananas(shouldUpdateDefinition);
    }

    Hooks.doAction('serviceFormChange', ...arguments);
    // Handle the form change in the way service needs here.
    this.props.onChange(...arguments);
  }

  validateForm() {
    let model = this.triggerTabFormSubmit();

    let validated = true;
    // Apply all validations.
    FormUtil.forEachDefinition(this.multipleDefinition, function (definition) {
      definition.showError = false;

      if (typeof definition.externalValidator !== 'function') {
        return;
      }

      let fieldValidated = definition.externalValidator(model, definition);
      if (!fieldValidated) {
        validated = false;
      }
    });

    this.forceUpdate();

    return {
      isValidated: validated,
      model: SchemaFormUtil.processFormModel(model, this.multipleDefinition),
      definition: this.multipleDefinition
    };
  }

  handleExternalSubmit() {
    return this.validateForm();
  }

  onVirtualNetworksStoreSuccess() {
    this.updateDefinitions();
  }

  getNetworkingDescriptionDefinition(model) {
    return {
      name: 'ports-description',
      render: () => {
        let {networkType} = this.getNetworkingConfiguration(model);
        let hostNetworkingDefinition = null;
        let {ports} = model.networking;
        let serviceAddressNetworkingDefinition = null;

        if (ports == null) {
          return null;
        }

        // Build out the Host ports that get dynamically created
        if ('host' === networkType) {
          let portMapping = ports.map(function (port, index) {
            return `$PORT${index}`;
          });
          portMapping = StringUtil.humanizeArray(portMapping, {
            wrapValueFunction(value, index) {
              return (
                {value}
              );
            }
          });

          hostNetworkingDefinition = (

              Host ports will be dynamically assigned and mapped
              to {portMapping}.

          );
        }

        // Build out the hostname mappings
        let serviceAddress = HostUtil.stringToHostname(model.general.id);
        if (serviceAddress) {
          serviceAddress += '.marathon.l4lb.thisdcos.directory';

          let addresses = ports.filter(function (port) {
            return !!port.lbPort;
          }).map(function (port, index) {
            return (

                  {`${serviceAddress}:${port.lbPort}`}

            );
          });

          serviceAddressNetworkingDefinition = (

                Clients can access your service at these Service Addresses

                {addresses}

          );
        }

        if (hostNetworkingDefinition == null &&
          serviceAddressNetworkingDefinition == null) {
          return null;
        }

        return (

                <Icon
                  color="blue"
                  family="small"
                  id="ring-information"
                  size="mini" />

                {hostNetworkingDefinition}
                {serviceAddressNetworkingDefinition}

        );
      }
    };
  }

  updateDefinitions() {
    let {model} = this.internalStorage_get();
    let {networking, volumes} = this.multipleDefinition;
    let {containerSettings} = model;

    let showDockerVolumes = containerSettings &&
      containerSettings.image != null && containerSettings.image !== '';

    let endIndex = -1;
    let startIndex = -1;

    volumes.definition.forEach(function (item, index) {
      if (item.prop === 'localVolumes') {
        startIndex = index + 1;
      }
      if (item.prop === 'dockerVolumes') {
        endIndex = index + 1;
      }
    });
    if (endIndex > -1) {
      this.internalStorage_set({
        model,
        dockerVolumesDefinition: volumes.definition.slice(startIndex, endIndex)
      });
    }
    // Get new value if it was set
    let {dockerVolumesDefinition} = this.internalStorage_get();

    if (showDockerVolumes && endIndex < 0) {
      volumes.definition.splice(startIndex, 0, ...dockerVolumesDefinition);
    } else if (!showDockerVolumes && endIndex > -1) {
      volumes.definition.splice(startIndex, dockerVolumesDefinition.length);
    }
    if (networking) {
      let {networkType} = this.props.schema.properties.networking.properties;

      let virtualNetworks = VirtualNetworksStore.getOverlays()
        .mapItems(function (overlay) {
          let name = overlay.getName();

          return {html: `Virtual Network: ${name}`, id: name};
        }).getItems();

      let networkDescriptionDefinition =
        this.getNetworkingDescriptionDefinition(model);

      networking.definition.forEach(function (definition) {

        if (definition.name === networkDescriptionDefinition.name) {
          Object.assign(definition, networkDescriptionDefinition);
        }

        if (definition.name === 'networkType') {
          definition.options = [].concat(networkType.options, virtualNetworks);
        }

      });

      let definition = networking.definition;
      let firstCheckboxIndex = -1;

      for (let i = definition.length - 1; i >= 0; i--) {
        let currentDefinition = definition[i];
        if ((FormUtil.isFieldInstanceOfProp('ports', currentDefinition)
          && FormUtil.getPropKey(currentDefinition.name) === 'expose')
          || currentDefinition.name === 'expose-endpoints') {

          firstCheckboxIndex = i;
        }
      }

      let checkboxes = this.getExposeNetworkingCheckboxes();
      let label = {
        name: 'expose-endpoints',
        render() {
          return Expose endpoints on host network;
        }
      };

      if (firstCheckboxIndex !== -1) {
        definition.splice(firstCheckboxIndex);
      }

      if (checkboxes.length) {
        definition.push(label, ...checkboxes);
      }

    }
  }

  handleTabClick(tab) {
    this.bananas(true, 0);
    this.props.onTabChange(tab);
  }

  handleAddRow() {
    super.handleAddRow(...arguments);
    this.bananas(true, 0);
  }

  handleRemoveRow() {
    super.handleRemoveRow(...arguments);
    this.bananas(true, 0);
  }

  bananas(shouldUpdateDefinition = false, delay = 50) {
    if (this.timer) {
      clearTimeout(this.timer);
    }
    // Debounce definition update. Also ensures we build the model with
    // latest form changes.
    this.timer = setTimeout(() => {
      let model = {};
      let formModel = this.triggerTabFormSubmit();

      Object.keys(formModel).forEach(function (key) {
        model[key] = FormUtil.modelToCombinedProps(formModel[key]);
      });

      model.environmentVariables = model.environmentVariables.environmentVariables;
      model.labels = model.labels.labels;
      model.healthChecks = model.healthChecks.healthChecks;
      if (!['host', 'bridge'].includes(model.networking.networkType) &&
        model.networking.ports) {
        // Set expose to true as default
        model.networking.ports.map(function (port) {
          port.expose = port.expose || false;

          return port;
        });
      }
      let {dockerVolumesDefinition = null} = this.internalStorage_get();
      this.internalStorage_set({model, dockerVolumesDefinition});

      if (shouldUpdateDefinition) {
        SchemaFormUtil.mergeModelIntoDefinition(
          model,
          this.multipleDefinition,
          this.getRemoveRowButton
        );

        // Provide opportunity to do action after update, before re-render
        Hooks.doAction('serviceFormUpdate', this);
        this.updateDefinitions();
        this.forceUpdate();
      }
    }, delay);
  }

  getNetworkingConfiguration(model) {
    let networkType = (model.networking.networkType || 'host').toLowerCase();
    let isUserMode = model.networking
      && !['host', 'bridge'].includes(networkType);
    return {networkType, isUserMode};
  }

  getExposeNetworkingCheckboxes() {
    let {model} = this.internalStorage_get();
    let definitionGroup = [];
    let {isUserMode} = this.getNetworkingConfiguration(model);
    let networkingDefinition = this.multipleDefinition.networking.definition;
    // First port definition in networking definition is at index 2
    let portDefinitionIndex = 2;

    // TODO: We need to refactor this as the current impl. is highly error prone
    // -> Use the port name to map fields (it's required to be unique)
    if (model.networking && model.networking.ports && isUserMode) {
      model.networking.ports.forEach(function (port) {
        let portDefinition = networkingDefinition[portDefinitionIndex++];

        if (!Array.isArray(portDefinition)) {
          return;
        }

        let propID = FormUtil.getPropIndex(portDefinition[0].name);

        definitionGroup.push({
          fieldType: 'checkbox',
          name: `ports[${propID}].expose`,
          placeholder: '',
          required: false,
          showError: false,
          writeType: 'input',
          value: port.expose || false,
          valueType: 'boolean',
          label: `${port.name} (${port.lbPort || 0}/${port.protocol})`,
          checked: false
        });

      });
    }

    return definitionGroup;
  }

  getNewDefinition() {
    let {model, schema} = this.props;

    schema = Hooks.applyFilter('serviceFormSchema', schema);

    let definition = SchemaUtil.schemaToMultipleDefinition({
      schema,
      renderSubheader: this.getSubHeader,
      renderLabel: this.getLabel,
      renderRemove: this.getRemoveRowButton,
      renderAdd: this.getAddNewRowButton
    });

    SchemaFormUtil.mergeModelIntoDefinition(
      model,
      definition,
      this.getRemoveRowButton
    );

    // Append definitions
    let {networking} = definition;
    if (networking) {
      networking.definition.push(
        this.getNetworkingDescriptionDefinition(model)
      );
    }

    if (model) {
      let {dockerVolumesDefinition = null} = this.internalStorage_get();
      this.internalStorage_set({model, dockerVolumesDefinition});
    }

    return definition;
  }
}

ServiceForm.defaultProps = {
  className: 'multiple-form row',
  defaultTab: '',
  getTriggerSubmit() {},
  onChange() {},
  onTabChange() {},
  schema: {}
};

ServiceForm.propTypes = {
  className: React.PropTypes.string,
  defaultTab: React.PropTypes.string,
  getTriggerSubmit: React.PropTypes.func,
  onChange: React.PropTypes.func,
  onTabChange: React.PropTypes.func,
  schema: React.PropTypes.object
};

module.exports = ServiceForm;
<commit_msg>Name actually needs to be watched, lbPort not so much<commit_after>import {Hooks} from 'PluginSDK';
import React from 'react';

import FormUtil from '../utils/FormUtil';
import HostUtil from '../utils/HostUtil';
import Icon from './Icon';
import SchemaForm from './SchemaForm';
import SchemaFormUtil from '../utils/SchemaFormUtil';
import SchemaUtil from '../utils/SchemaUtil';
import StringUtil from '../utils/StringUtil';
import VirtualNetworksStore from '../stores/VirtualNetworksStore';

const METHODS_TO_BIND = [
  'handleFormChange',
  'onVirtualNetworksStoreSuccess',
  'validateForm'
];

const DUPLICABLE_FIELDS_TO_WATCH = {
  healthChecks: {
    fields: ['protocol', 'portType'],
    forceUpdate: true
  },
  ports: {
    fields: ['discovery', 'protocol'],
    blurOnly: ['name'],
    forceUpdate: true
  }
};

const FIELDS_TO_WATCH = {
  networkType: {
    forceUpdate: true
  },
  image: {
    forceUpdate: false
  }
};

class ServiceForm extends SchemaForm {
  constructor() {
    super(...arguments);

    METHODS_TO_BIND.forEach((method) => {
      this[method] = this[method].bind(this);
    });

    this.store_listeners = Hooks.applyFilter('serviceFormStoreListeners', [
      {
        name: 'virtualNetworks',
        events: ['success'],
        suppressUpdate: true
      }
    ]);
  }

  componentWillMount() {
    this.multipleDefinition = this.getNewDefinition();
    this.props.getTriggerSubmit(this.handleExternalSubmit);
  }

  componentDidMount() {
    super.componentDidMount(...arguments);
    Hooks.doAction('serviceFormMount', this);
    // Update definition and build model
    this.updateDefinitions();
    this.bananas(true, 0);
  }

  onVirtualNetworksStoreSuccess() {
    this.updateDefinitions();
  }

  shouldUpdateDefinition(changes, eventType, fieldName) {
    let propKey = FormUtil.getPropKey(fieldName);
    let blurChange = Object.values(DUPLICABLE_FIELDS_TO_WATCH).some(function (item) {
      return item.blurOnly && item.blurOnly.includes(propKey);
    });

    return Object.keys(changes).some(function (changeKey) {
      let tab = FormUtil.getProp(changeKey);

      return ((tab in DUPLICABLE_FIELDS_TO_WATCH)
        && (DUPLICABLE_FIELDS_TO_WATCH[tab].fields.includes(propKey)
        && DUPLICABLE_FIELDS_TO_WATCH[tab].forceUpdate))
        || ((fieldName in FIELDS_TO_WATCH
        && FIELDS_TO_WATCH[fieldName].forceUpdate));

    }) || (eventType === 'blur' && blurChange);
  }

  handleFormChange(changes, eventObj) {
    let {eventType, fieldName} = eventObj;
    let shouldUpdateDefinition = this.shouldUpdateDefinition(
      changes, eventType, fieldName
    );

    if (eventType === 'change' || shouldUpdateDefinition) {
      this.bananas(shouldUpdateDefinition);
    }

    Hooks.doAction('serviceFormChange', ...arguments);
    // Handle the form change in the way service needs here.
    this.props.onChange(...arguments);
  }

  validateForm() {
    let model = this.triggerTabFormSubmit();

    let validated = true;
    // Apply all validations.
    FormUtil.forEachDefinition(this.multipleDefinition, function (definition) {
      definition.showError = false;

      if (typeof definition.externalValidator !== 'function') {
        return;
      }

      let fieldValidated = definition.externalValidator(model, definition);
      if (!fieldValidated) {
        validated = false;
      }
    });

    this.forceUpdate();

    return {
      isValidated: validated,
      model: SchemaFormUtil.processFormModel(model, this.multipleDefinition),
      definition: this.multipleDefinition
    };
  }

  handleExternalSubmit() {
    return this.validateForm();
  }

  onVirtualNetworksStoreSuccess() {
    this.updateDefinitions();
  }

  getNetworkingDescriptionDefinition(model) {
    return {
      name: 'ports-description',
      render: () => {
        let {networkType} = this.getNetworkingConfiguration(model);
        let hostNetworkingDefinition = null;
        let {ports} = model.networking;
        let serviceAddressNetworkingDefinition = null;

        if (ports == null) {
          return null;
        }

        // Build out the Host ports that get dynamically created
        if ('host' === networkType) {
          let portMapping = ports.map(function (port, index) {
            return `$PORT${index}`;
          });
          portMapping = StringUtil.humanizeArray(portMapping, {
            wrapValueFunction(value, index) {
              return (
                {value}
              );
            }
          });

          hostNetworkingDefinition = (

              Host ports will be dynamically assigned and mapped
              to {portMapping}.

          );
        }

        // Build out the hostname mappings
        let serviceAddress = HostUtil.stringToHostname(model.general.id);
        if (serviceAddress) {
          serviceAddress += '.marathon.l4lb.thisdcos.directory';

          let addresses = ports.filter(function (port) {
            return !!port.lbPort;
          }).map(function (port, index) {
            return (

                  {`${serviceAddress}:${port.lbPort}`}

            );
          });

          serviceAddressNetworkingDefinition = (

                Clients can access your service at these Service Addresses

                {addresses}

          );
        }

        if (hostNetworkingDefinition == null &&
          serviceAddressNetworkingDefinition == null) {
          return null;
        }

        return (

                <Icon
                  color="blue"
                  family="small"
                  id="ring-information"
                  size="mini" />

                {hostNetworkingDefinition}
                {serviceAddressNetworkingDefinition}

        );
      }
    };
  }

  updateDefinitions() {
    let {model} = this.internalStorage_get();
    let {networking, volumes} = this.multipleDefinition;
    let {containerSettings} = model;

    let showDockerVolumes = containerSettings &&
      containerSettings.image != null && containerSettings.image !== '';

    let endIndex = -1;
    let startIndex = -1;

    volumes.definition.forEach(function (item, index) {
      if (item.prop === 'localVolumes') {
        startIndex = index + 1;
      }
      if (item.prop === 'dockerVolumes') {
        endIndex = index + 1;
      }
    });
    if (endIndex > -1) {
      this.internalStorage_set({
        model,
        dockerVolumesDefinition: volumes.definition.slice(startIndex, endIndex)
      });
    }
    // Get new value if it was set
    let {dockerVolumesDefinition} = this.internalStorage_get();

    if (showDockerVolumes && endIndex < 0) {
      volumes.definition.splice(startIndex, 0, ...dockerVolumesDefinition);
    } else if (!showDockerVolumes && endIndex > -1) {
      volumes.definition.splice(startIndex, dockerVolumesDefinition.length);
    }
    if (networking) {
      let {networkType} = this.props.schema.properties.networking.properties;

      let virtualNetworks = VirtualNetworksStore.getOverlays()
        .mapItems(function (overlay) {
          let name = overlay.getName();

          return {html: `Virtual Network: ${name}`, id: name};
        }).getItems();

      let networkDescriptionDefinition =
        this.getNetworkingDescriptionDefinition(model);

      networking.definition.forEach(function (definition) {

        if (definition.name === networkDescriptionDefinition.name) {
          Object.assign(definition, networkDescriptionDefinition);
        }

        if (definition.name === 'networkType') {
          definition.options = [].concat(networkType.options, virtualNetworks);
        }

      });

      let definition = networking.definition;
      let firstCheckboxIndex = -1;

      for (let i = definition.length - 1; i >= 0; i--) {
        let currentDefinition = definition[i];
        if ((FormUtil.isFieldInstanceOfProp('ports', currentDefinition)
          && FormUtil.getPropKey(currentDefinition.name) === 'expose')
          || currentDefinition.name === 'expose-endpoints') {

          firstCheckboxIndex = i;
        }
      }

      let checkboxes = this.getExposeNetworkingCheckboxes();
      let label = {
        name: 'expose-endpoints',
        render() {
          return Expose endpoints on host network;
        }
      };

      if (firstCheckboxIndex !== -1) {
        definition.splice(firstCheckboxIndex);
      }

      if (checkboxes.length) {
        definition.push(label, ...checkboxes);
      }

    }
  }

  handleTabClick(tab) {
    this.bananas(true, 0);
    this.props.onTabChange(tab);
  }

  handleAddRow() {
    super.handleAddRow(...arguments);
    this.bananas(true, 0);
  }

  handleRemoveRow() {
    super.handleRemoveRow(...arguments);
    this.bananas(true, 0);
  }

  bananas(shouldUpdateDefinition = false, delay = 50) {
    if (this.timer) {
      clearTimeout(this.timer);
    }
    // Debounce definition update. Also ensures we build the model with
    // latest form changes.
    this.timer = setTimeout(() => {
      let model = {};
      let formModel = this.triggerTabFormSubmit();

      Object.keys(formModel).forEach(function (key) {
        model[key] = FormUtil.modelToCombinedProps(formModel[key]);
      });

      model.environmentVariables = model.environmentVariables.environmentVariables;
      model.labels = model.labels.labels;
      model.healthChecks = model.healthChecks.healthChecks;
      if (!['host', 'bridge'].includes(model.networking.networkType) &&
        model.networking.ports) {
        // Set expose to true as default
        model.networking.ports.map(function (port) {
          port.expose = port.expose || false;

          return port;
        });
      }
      let {dockerVolumesDefinition = null} = this.internalStorage_get();
      this.internalStorage_set({model, dockerVolumesDefinition});

      if (shouldUpdateDefinition) {
        SchemaFormUtil.mergeModelIntoDefinition(
          model,
          this.multipleDefinition,
          this.getRemoveRowButton
        );

        // Provide opportunity to do action after update, before re-render
        Hooks.doAction('serviceFormUpdate', this);
        this.updateDefinitions();
        this.forceUpdate();
      }
    }, delay);
  }

  getNetworkingConfiguration(model) {
    let networkType = (model.networking.networkType || 'host').toLowerCase();
    let isUserMode = model.networking
      && !['host', 'bridge'].includes(networkType);
    return {networkType, isUserMode};
  }

  getExposeNetworkingCheckboxes() {
    let {model} = this.internalStorage_get();
    let definitionGroup = [];
    let {isUserMode} = this.getNetworkingConfiguration(model);
    let networkingDefinition = this.multipleDefinition.networking.definition;
    // First port definition in networking definition is at index 2
    let portDefinitionIndex = 2;

    // TODO: We need to refactor this as the current impl. is highly error prone
    // -> Use the port name to map fields (it's required to be unique)
    if (model.networking && model.networking.ports && isUserMode) {
      model.networking.ports.forEach(function (port) {
        let portDefinition = networkingDefinition[portDefinitionIndex++];

        if (!Array.isArray(portDefinition)) {
          return;
        }

        let propID = FormUtil.getPropIndex(portDefinition[0].name);

        definitionGroup.push({
          fieldType: 'checkbox',
          name: `ports[${propID}].expose`,
          placeholder: '',
          required: false,
          showError: false,
          writeType: 'input',
          value: port.expose || false,
          valueType: 'boolean',
          label: `${port.name} (${port.lbPort || 0}/${port.protocol})`,
          checked: false
        });

      });
    }

    return definitionGroup;
  }

  getNewDefinition() {
    let {model, schema} = this.props;

    schema = Hooks.applyFilter('serviceFormSchema', schema);

    let definition = SchemaUtil.schemaToMultipleDefinition({
      schema,
      renderSubheader: this.getSubHeader,
      renderLabel: this.getLabel,
      renderRemove: this.getRemoveRowButton,
      renderAdd: this.getAddNewRowButton
    });

    SchemaFormUtil.mergeModelIntoDefinition(
      model,
      definition,
      this.getRemoveRowButton
    );

    // Append definitions
    let {networking} = definition;
    if (networking) {
      networking.definition.push(
        this.getNetworkingDescriptionDefinition(model)
      );
    }

    if (model) {
      let {dockerVolumesDefinition = null} = this.internalStorage_get();
      this.internalStorage_set({model, dockerVolumesDefinition});
    }

    return definition;
  }
}

ServiceForm.defaultProps = {
  className: 'multiple-form row',
  defaultTab: '',
  getTriggerSubmit() {},
  onChange() {},
  onTabChange() {},
  schema: {}
};

ServiceForm.propTypes = {
  className: React.PropTypes.string,
  defaultTab: React.PropTypes.string,
  getTriggerSubmit: React.PropTypes.func,
  onChange: React.PropTypes.func,
  onTabChange: React.PropTypes.func,
  schema: React.PropTypes.object
};

module.exports = ServiceForm;
<|endoftext|>


<commit_before>import React from 'react'
import _ from 'underscore'
import { connect } from 'react-redux'
import { Link } from 'react-router'
import { instance as user } from '../../lib/user'
import actions from '../../actions/user'

const mapStateToProps = (state) => {
	var user = state.user.login.item
	return { user }
}

const mapDispatchToProps = (/*dispatch*/) => {
	return {
	}
}

class Menu extends React.Component {
	componentWillMount () {
		user.onChange(() => {
			this.forceUpdate()
		})
	}

	onLogout () {
		actions.logout()
		this.context.router.replace('/users/login')
	}

	getMenu () {
		var isActive = (to) => _.startsWith(this.props.location.pathname, to, 1)

		if (user.isLoggedIn) {
			return (

<Link to="forecasts">Forecasts</Link>

							{user.get('name')}

Sign out

			)
		} else {
			return (

<Link to="/users/login">Login</Link>

			)
		}
	}

	render () {
		return (

						<Link to="/" class="navbar-brand">Farmer Analytics</Link>
						<button class="navbar-toggle" type="button" data-toggle="collapse" data-target="#navbar-main">

						</button>

					{this.getMenu()}

		)
	}
}

Menu.propTypes = {
	location: React.PropTypes.shape({
		pathname: React.PropTypes.string.isRequired,
	}).isRequired,
}
Menu.contextTypes = {
	router: React.PropTypes.object.isRequired
}

export default connect(
	mapStateToProps,
	mapDispatchToProps
)(Menu)
<commit_msg>Remove forecasts menu item, since it's not needed yet.<commit_after>import React from 'react'
import _ from 'underscore'
import { connect } from 'react-redux'
import { Link } from 'react-router'
import { instance as user } from '../../lib/user'
import actions from '../../actions/user'

const mapStateToProps = (state) => {
	var user = state.user.login.item
	return { user }
}

const mapDispatchToProps = (/*dispatch*/) => {
	return {
	}
}

class Menu extends React.Component {
	componentWillMount () {
		user.onChange(() => {
			this.forceUpdate()
		})
	}

	onLogout () {
		actions.logout()
		this.context.router.replace('/users/login')
	}

	getMenu () {
		var isActive = (to) => _.startsWith(this.props.location.pathname, to, 1)

		if (user.isLoggedIn) {
			return (

						{/*
<Link to="forecasts">Forecasts</Link>
*/}

							{user.get('name')}

Sign out

			)
		} else {
			return (

<Link to="/users/login">Login</Link>

			)
		}
	}

	render () {
		return (

						<Link to="/" class="navbar-brand">Farmer Analytics</Link>
						<button class="navbar-toggle" type="button" data-toggle="collapse" data-target="#navbar-main">

						</button>

					{this.getMenu()}

		)
	}
}

Menu.propTypes = {
	location: React.PropTypes.shape({
		pathname: React.PropTypes.string.isRequired,
	}).isRequired,
}
Menu.contextTypes = {
	router: React.PropTypes.object.isRequired
}

export default connect(
	mapStateToProps,
	mapDispatchToProps
)(Menu)
<|endoftext|>


<commit_before>var keyMirror = require('react/lib/keyMirror');

module.exports = {
  CHANGE_EVENT: 'change',

  ActionTypes: keyMirror({
    LINT_TEXT: null,
    LINT_GITHUB: null
  }),

  ActionSources: keyMirror({
    SERVER_ACTION: null,
    VIEW_ACTION: null
  }),

  URLs: {
    GITHUB_STATIC_URL: 'https://raw.githubusercontent.com/',
    GITHUB_REGULAR_URL: 'https://github.com/'
  }
};<commit_msg>keyMirror location changed<commit_after>var keyMirror = require('fbjs/lib/keyMirror');

module.exports = {
  CHANGE_EVENT: 'change',

  ActionTypes: keyMirror({
    LINT_TEXT: null,
    LINT_GITHUB: null
  }),

  ActionSources: keyMirror({
    SERVER_ACTION: null,
    VIEW_ACTION: null
  }),

  URLs: {
    GITHUB_STATIC_URL: 'https://raw.githubusercontent.com/',
    GITHUB_REGULAR_URL: 'https://github.com/'
  }
};
<|endoftext|>


<commit_before>var $ = require('jquery');

module.exports = (function() {

    $(window).on('resize', function() {
        window.breakpoints = JSON.parse(JSON.parse(window.getComputedStyle(document.querySelector('body'), ':before').content));
    }).trigger('resize');

}());
<commit_msg>Fix IE issue with mq_breakpoints<commit_after>var $ = require('jquery');

function unquote(str) {
    // Fix issue in Internet Explorer with JSON.parse and quotes
    if (!$('html').hasClass('ie')) {
        return false;
    } else {
        return str.slice(1, str.length - 1);
    }
}

module.exports = (function() {

    $(window).on('resize', function() {
        window.breakpoints = JSON.parse(unquote(window.getComputedStyle(document.querySelector('body'), ':before').content));
    }).trigger('resize');

}());
<|endoftext|>