diff options
Diffstat (limited to 'module/web/static')
| -rw-r--r-- | module/web/static/css/default/style.less | 65 | ||||
| -rw-r--r-- | module/web/static/js/default.js | 15 | ||||
| -rw-r--r-- | module/web/static/js/libs/jquery.flot-1.1.js | 2599 | ||||
| -rw-r--r-- | module/web/static/js/libs/jquery.flot.min.js | 6 | ||||
| -rw-r--r-- | module/web/static/js/libs/lodash-0.7.0.js (renamed from module/web/static/js/libs/lodash-0.5.2.js) | 1893 | ||||
| -rw-r--r-- | module/web/static/js/views/packageView.js | 36 | 
6 files changed, 3614 insertions, 1000 deletions
| diff --git a/module/web/static/css/default/style.less b/module/web/static/css/default/style.less index a22f29999..180c763a2 100644 --- a/module/web/static/css/default/style.less +++ b/module/web/static/css/default/style.less @@ -1,19 +1,48 @@  /*
 -    General
 +    Definitions
   */
  @min-width: 1000px;
  @header-height: 70px;
  @footer-height: 100px;
 -@margin-side: 150px;
 +@margin-side: 100px;
  @dark: #333333;
 +@light: #ffffff;
  @grey: #757575;
  @yellow: #fee247;
  @blue: #3a79aa;
 +@lightblue: lighten(spin(@blue, 5), 10%);
 +@darkblue: darken(spin(@blue, -5), 10%);
  @emph: #FF7637;
 +/*
 +  Mixins
 +*/
 +
 +
 +.gradient (@origin: left, @start: #ffffff, @stop: #000000) {
 +  background-color: @start;
 +  background-image: -webkit-linear-gradient(@origin, @start, @stop);
 +  background-image: -moz-linear-gradient(@origin, @start, @stop);
 +  background-image: -o-linear-gradient(@origin, @start, @stop);
 +  background-image: -ms-linear-gradient(@origin, @start, @stop);
 +  background-image: linear-gradient(@origin, @start, @stop);
 +}
 +
 +.transition (@prop: all, @time: 1s, @ease: linear) {
 +  -webkit-transition: @prop @time @ease;
 +  -moz-transition: @prop @time @ease;
 +  -o-transition: @prop @time @ease;
 +  -ms-transition: @prop @time @ease;
 +  transition: @prop @time @ease;
 +}
 +
 +
 +/*
 +    General
 + */
  * {
      margin: 0;
 @@ -320,4 +349,36 @@ footer h2 {  #dash-nav .dropdown-menu i {
    margin-top: 4px;
    padding-right: 5px;
 +}
 +
 +
 +#dashboard ul {
 +  margin: 0;
 +  list-style: none;
 +}
 +
 +.package-view {
 +  height: 30px;
 +  width: 100%;
 +  color: @light;
 +  .gradient(top, @blue, @darkblue);
 +//  background-color: @blue;
 +  font-weight: bold;
 +  border-radius: 5px;
 +  margin-bottom: 3px;
 +}
 +
 +.package-view:hover {
 +  .gradient(top, @blue, @lightblue);
 +}
 +
 +
 +.package-view a {
 +  color: @light;
 +}
 +
 +.package-graph {
 +  display: inline;
 +  width: 20px;
 +  height: 20px;
  }
\ No newline at end of file diff --git a/module/web/static/js/default.js b/module/web/static/js/default.js index e200f470a..cb2ef38c3 100644 --- a/module/web/static/js/default.js +++ b/module/web/static/js/default.js @@ -7,15 +7,18 @@ require.config({          jquery:"libs/jquery-1.8.0",          jqueryui:"libs/jqueryui", -        flot:"libs/jquery.flot.min", +        flot:"libs/jquery.flot-1.1", +        flotpie: "libs/jquery.flot.pie",          transit:"libs/jquery.transit-0.1.3",          omniwindow: "libs/jquery.omniwindow",          bootstrap: "libs/bootstrap-2.1.1", -        underscore:"libs/lodash-0.5.2", +        underscore:"libs/lodash-0.7.0",          backbone:"libs/backbone-0.9.2", +//        handlebars: "libs/Handlebars-1.0rc1", -        // Require.js Plugins +        // Plugins +//        hbs: "plugins/hbs-2.0.1",          text:"plugins/text-2.0.3",          tpl: "../../templates" @@ -29,11 +32,17 @@ require.config({              exports:"Backbone"  //attaches "Backbone" to the window object          },          "flot" : ["jquery"], +        "flotpie" : ["flot"],          "transit" : ["jquery"],          "omniwindow" : ["jquery"],          "bootstrap" : ["jquery"]      } // end Shim Configuration +    // Handlebar Configuration +//    hbs : { +//        templateExtension : 'hbs', +//        disableI18n : true +//    }  });  define('default', ['jquery', 'backbone', 'routers/defaultRouter', 'views/headerView', 'views/packageTreeView', diff --git a/module/web/static/js/libs/jquery.flot-1.1.js b/module/web/static/js/libs/jquery.flot-1.1.js new file mode 100644 index 000000000..aabc544e9 --- /dev/null +++ b/module/web/static/js/libs/jquery.flot-1.1.js @@ -0,0 +1,2599 @@ +/*! Javascript plotting library for jQuery, v. 0.7. + * + * Released under the MIT license by IOLA, December 2007. + * + */ + +// first an inline dependency, jquery.colorhelpers.js, we inline it here +// for convenience + +/* Plugin for jQuery for working with colors. + *  + * Version 1.1. + *  + * Inspiration from jQuery color animation plugin by John Resig. + * + * Released under the MIT license by Ole Laursen, October 2009. + * + * Examples: + * + *   $.color.parse("#fff").scale('rgb', 0.25).add('a', -0.5).toString() + *   var c = $.color.extract($("#mydiv"), 'background-color'); + *   console.log(c.r, c.g, c.b, c.a); + *   $.color.make(100, 50, 25, 0.4).toString() // returns "rgba(100,50,25,0.4)" + * + * Note that .scale() and .add() return the same modified object + * instead of making a new one. + * + * V. 1.1: Fix error handling so e.g. parsing an empty string does + * produce a color rather than just crashing. + */  +(function(B){B.color={};B.color.make=function(F,E,C,D){var G={};G.r=F||0;G.g=E||0;G.b=C||0;G.a=D!=null?D:1;G.add=function(J,I){for(var H=0;H<J.length;++H){G[J.charAt(H)]+=I}return G.normalize()};G.scale=function(J,I){for(var H=0;H<J.length;++H){G[J.charAt(H)]*=I}return G.normalize()};G.toString=function(){if(G.a>=1){return"rgb("+[G.r,G.g,G.b].join(",")+")"}else{return"rgba("+[G.r,G.g,G.b,G.a].join(",")+")"}};G.normalize=function(){function H(J,K,I){return K<J?J:(K>I?I:K)}G.r=H(0,parseInt(G.r),255);G.g=H(0,parseInt(G.g),255);G.b=H(0,parseInt(G.b),255);G.a=H(0,G.a,1);return G};G.clone=function(){return B.color.make(G.r,G.b,G.g,G.a)};return G.normalize()};B.color.extract=function(D,C){var E;do{E=D.css(C).toLowerCase();if(E!=""&&E!="transparent"){break}D=D.parent()}while(!B.nodeName(D.get(0),"body"));if(E=="rgba(0, 0, 0, 0)"){E="transparent"}return B.color.parse(E)};B.color.parse=function(F){var E,C=B.color.make;if(E=/rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(F)){return C(parseInt(E[1],10),parseInt(E[2],10),parseInt(E[3],10))}if(E=/rgba\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]+(?:\.[0-9]+)?)\s*\)/.exec(F)){return C(parseInt(E[1],10),parseInt(E[2],10),parseInt(E[3],10),parseFloat(E[4]))}if(E=/rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(F)){return C(parseFloat(E[1])*2.55,parseFloat(E[2])*2.55,parseFloat(E[3])*2.55)}if(E=/rgba\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\s*\)/.exec(F)){return C(parseFloat(E[1])*2.55,parseFloat(E[2])*2.55,parseFloat(E[3])*2.55,parseFloat(E[4]))}if(E=/#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(F)){return C(parseInt(E[1],16),parseInt(E[2],16),parseInt(E[3],16))}if(E=/#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(F)){return C(parseInt(E[1]+E[1],16),parseInt(E[2]+E[2],16),parseInt(E[3]+E[3],16))}var D=B.trim(F).toLowerCase();if(D=="transparent"){return C(255,255,255,0)}else{E=A[D]||[0,0,0];return C(E[0],E[1],E[2])}};var A={aqua:[0,255,255],azure:[240,255,255],beige:[245,245,220],black:[0,0,0],blue:[0,0,255],brown:[165,42,42],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgrey:[169,169,169],darkgreen:[0,100,0],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkviolet:[148,0,211],fuchsia:[255,0,255],gold:[255,215,0],green:[0,128,0],indigo:[75,0,130],khaki:[240,230,140],lightblue:[173,216,230],lightcyan:[224,255,255],lightgreen:[144,238,144],lightgrey:[211,211,211],lightpink:[255,182,193],lightyellow:[255,255,224],lime:[0,255,0],magenta:[255,0,255],maroon:[128,0,0],navy:[0,0,128],olive:[128,128,0],orange:[255,165,0],pink:[255,192,203],purple:[128,0,128],violet:[128,0,128],red:[255,0,0],silver:[192,192,192],white:[255,255,255],yellow:[255,255,0]}})(jQuery); + +// the actual Flot code +(function($) { +    function Plot(placeholder, data_, options_, plugins) { +        // data is on the form: +        //   [ series1, series2 ... ] +        // where series is either just the data as [ [x1, y1], [x2, y2], ... ] +        // or { data: [ [x1, y1], [x2, y2], ... ], label: "some label", ... } +         +        var series = [], +            options = { +                // the color theme used for graphs +                colors: ["#edc240", "#afd8f8", "#cb4b4b", "#4da74d", "#9440ed"], +                legend: { +                    show: true, +                    noColumns: 1, // number of colums in legend table +                    labelFormatter: null, // fn: string -> string +                    labelBoxBorderColor: "#ccc", // border color for the little label boxes +                    container: null, // container (as jQuery object) to put legend in, null means default on top of graph +                    position: "ne", // position of default legend container within plot +                    margin: 5, // distance from grid edge to default legend container within plot +                    backgroundColor: null, // null means auto-detect +                    backgroundOpacity: 0.85 // set to 0 to avoid background +                }, +                xaxis: { +                    show: null, // null = auto-detect, true = always, false = never +                    position: "bottom", // or "top" +                    mode: null, // null or "time" +                    color: null, // base color, labels, ticks +                    tickColor: null, // possibly different color of ticks, e.g. "rgba(0,0,0,0.15)" +                    transform: null, // null or f: number -> number to transform axis +                    inverseTransform: null, // if transform is set, this should be the inverse function +                    min: null, // min. value to show, null means set automatically +                    max: null, // max. value to show, null means set automatically +                    autoscaleMargin: null, // margin in % to add if auto-setting min/max +                    ticks: null, // either [1, 3] or [[1, "a"], 3] or (fn: axis info -> ticks) or app. number of ticks for auto-ticks +                    tickFormatter: null, // fn: number -> string +                    labelWidth: null, // size of tick labels in pixels +                    labelHeight: null, +                    reserveSpace: null, // whether to reserve space even if axis isn't shown +                    tickLength: null, // size in pixels of ticks, or "full" for whole line +                    alignTicksWithAxis: null, // axis number or null for no sync +                     +                    // mode specific options +                    tickDecimals: null, // no. of decimals, null means auto +                    tickSize: null, // number or [number, "unit"] +                    minTickSize: null, // number or [number, "unit"] +                    monthNames: null, // list of names of months +                    timeformat: null, // format string to use +                    twelveHourClock: false // 12 or 24 time in time mode +                }, +                yaxis: { +                    autoscaleMargin: 0.02, +                    position: "left" // or "right" +                }, +                xaxes: [], +                yaxes: [], +                series: { +                    points: { +                        show: false, +                        radius: 3, +                        lineWidth: 2, // in pixels +                        fill: true, +                        fillColor: "#ffffff", +                        symbol: "circle" // or callback +                    }, +                    lines: { +                        // we don't put in show: false so we can see +                        // whether lines were actively disabled  +                        lineWidth: 2, // in pixels +                        fill: false, +                        fillColor: null, +                        steps: false +                    }, +                    bars: { +                        show: false, +                        lineWidth: 2, // in pixels +                        barWidth: 1, // in units of the x axis +                        fill: true, +                        fillColor: null, +                        align: "left", // or "center"  +                        horizontal: false +                    }, +                    shadowSize: 3 +                }, +                grid: { +                    show: true, +                    aboveData: false, +                    color: "#545454", // primary color used for outline and labels +                    backgroundColor: null, // null for transparent, else color +                    borderColor: null, // set if different from the grid color +                    tickColor: null, // color for the ticks, e.g. "rgba(0,0,0,0.15)" +                    labelMargin: 5, // in pixels +                    axisMargin: 8, // in pixels +                    borderWidth: 2, // in pixels +                    minBorderMargin: null, // in pixels, null means taken from points radius +                    markings: null, // array of ranges or fn: axes -> array of ranges +                    markingsColor: "#f4f4f4", +                    markingsLineWidth: 2, +                    // interactive stuff +                    clickable: false, +                    hoverable: false, +                    autoHighlight: true, // highlight in case mouse is near +                    mouseActiveRadius: 10 // how far the mouse can be away to activate an item +                }, +                hooks: {} +            }, +        canvas = null,      // the canvas for the plot itself +        overlay = null,     // canvas for interactive stuff on top of plot +        eventHolder = null, // jQuery object that events should be bound to +        ctx = null, octx = null, +        xaxes = [], yaxes = [], +        plotOffset = { left: 0, right: 0, top: 0, bottom: 0}, +        canvasWidth = 0, canvasHeight = 0, +        plotWidth = 0, plotHeight = 0, +        hooks = { +            processOptions: [], +            processRawData: [], +            processDatapoints: [], +            drawSeries: [], +            draw: [], +            bindEvents: [], +            drawOverlay: [], +            shutdown: [] +        }, +        plot = this; + +        // public functions +        plot.setData = setData; +        plot.setupGrid = setupGrid; +        plot.draw = draw; +        plot.getPlaceholder = function() { return placeholder; }; +        plot.getCanvas = function() { return canvas; }; +        plot.getPlotOffset = function() { return plotOffset; }; +        plot.width = function () { return plotWidth; }; +        plot.height = function () { return plotHeight; }; +        plot.offset = function () { +            var o = eventHolder.offset(); +            o.left += plotOffset.left; +            o.top += plotOffset.top; +            return o; +        }; +        plot.getData = function () { return series; }; +        plot.getAxes = function () { +            var res = {}, i; +            $.each(xaxes.concat(yaxes), function (_, axis) { +                if (axis) +                    res[axis.direction + (axis.n != 1 ? axis.n : "") + "axis"] = axis; +            }); +            return res; +        }; +        plot.getXAxes = function () { return xaxes; }; +        plot.getYAxes = function () { return yaxes; }; +        plot.c2p = canvasToAxisCoords; +        plot.p2c = axisToCanvasCoords; +        plot.getOptions = function () { return options; }; +        plot.highlight = highlight; +        plot.unhighlight = unhighlight; +        plot.triggerRedrawOverlay = triggerRedrawOverlay; +        plot.pointOffset = function(point) { +            return { +                left: parseInt(xaxes[axisNumber(point, "x") - 1].p2c(+point.x) + plotOffset.left), +                top: parseInt(yaxes[axisNumber(point, "y") - 1].p2c(+point.y) + plotOffset.top) +            }; +        }; +        plot.shutdown = shutdown; +        plot.resize = function () { +            getCanvasDimensions(); +            resizeCanvas(canvas); +            resizeCanvas(overlay); +        }; + +        // public attributes +        plot.hooks = hooks; +         +        // initialize +        initPlugins(plot); +        parseOptions(options_); +        setupCanvases(); +        setData(data_); +        setupGrid(); +        draw(); +        bindEvents(); + + +        function executeHooks(hook, args) { +            args = [plot].concat(args); +            for (var i = 0; i < hook.length; ++i) +                hook[i].apply(this, args); +        } + +        function initPlugins() { +            for (var i = 0; i < plugins.length; ++i) { +                var p = plugins[i]; +                p.init(plot); +                if (p.options) +                    $.extend(true, options, p.options); +            } +        } +         +        function parseOptions(opts) { +            var i; +             +            $.extend(true, options, opts); +             +            if (options.xaxis.color == null) +                options.xaxis.color = options.grid.color; +            if (options.yaxis.color == null) +                options.yaxis.color = options.grid.color; +             +            if (options.xaxis.tickColor == null) // backwards-compatibility +                options.xaxis.tickColor = options.grid.tickColor; +            if (options.yaxis.tickColor == null) // backwards-compatibility +                options.yaxis.tickColor = options.grid.tickColor; + +            if (options.grid.borderColor == null) +                options.grid.borderColor = options.grid.color; +            if (options.grid.tickColor == null) +                options.grid.tickColor = $.color.parse(options.grid.color).scale('a', 0.22).toString(); +             +            // fill in defaults in axes, copy at least always the +            // first as the rest of the code assumes it'll be there +            for (i = 0; i < Math.max(1, options.xaxes.length); ++i) +                options.xaxes[i] = $.extend(true, {}, options.xaxis, options.xaxes[i]); +            for (i = 0; i < Math.max(1, options.yaxes.length); ++i) +                options.yaxes[i] = $.extend(true, {}, options.yaxis, options.yaxes[i]); + +            // backwards compatibility, to be removed in future +            if (options.xaxis.noTicks && options.xaxis.ticks == null) +                options.xaxis.ticks = options.xaxis.noTicks; +            if (options.yaxis.noTicks && options.yaxis.ticks == null) +                options.yaxis.ticks = options.yaxis.noTicks; +            if (options.x2axis) { +                options.xaxes[1] = $.extend(true, {}, options.xaxis, options.x2axis); +                options.xaxes[1].position = "top"; +            } +            if (options.y2axis) { +                options.yaxes[1] = $.extend(true, {}, options.yaxis, options.y2axis); +                options.yaxes[1].position = "right"; +            } +            if (options.grid.coloredAreas) +                options.grid.markings = options.grid.coloredAreas; +            if (options.grid.coloredAreasColor) +                options.grid.markingsColor = options.grid.coloredAreasColor; +            if (options.lines) +                $.extend(true, options.series.lines, options.lines); +            if (options.points) +                $.extend(true, options.series.points, options.points); +            if (options.bars) +                $.extend(true, options.series.bars, options.bars); +            if (options.shadowSize != null) +                options.series.shadowSize = options.shadowSize; + +            // save options on axes for future reference +            for (i = 0; i < options.xaxes.length; ++i) +                getOrCreateAxis(xaxes, i + 1).options = options.xaxes[i]; +            for (i = 0; i < options.yaxes.length; ++i) +                getOrCreateAxis(yaxes, i + 1).options = options.yaxes[i]; + +            // add hooks from options +            for (var n in hooks) +                if (options.hooks[n] && options.hooks[n].length) +                    hooks[n] = hooks[n].concat(options.hooks[n]); + +            executeHooks(hooks.processOptions, [options]); +        } + +        function setData(d) { +            series = parseData(d); +            fillInSeriesOptions(); +            processData(); +        } +         +        function parseData(d) { +            var res = []; +            for (var i = 0; i < d.length; ++i) { +                var s = $.extend(true, {}, options.series); + +                if (d[i].data != null) { +                    s.data = d[i].data; // move the data instead of deep-copy +                    delete d[i].data; + +                    $.extend(true, s, d[i]); + +                    d[i].data = s.data; +                } +                else +                    s.data = d[i]; +                res.push(s); +            } + +            return res; +        } +         +        function axisNumber(obj, coord) { +            var a = obj[coord + "axis"]; +            if (typeof a == "object") // if we got a real axis, extract number +                a = a.n; +            if (typeof a != "number") +                a = 1; // default to first axis +            return a; +        } + +        function allAxes() { +            // return flat array without annoying null entries +            return $.grep(xaxes.concat(yaxes), function (a) { return a; }); +        } +         +        function canvasToAxisCoords(pos) { +            // return an object with x/y corresponding to all used axes  +            var res = {}, i, axis; +            for (i = 0; i < xaxes.length; ++i) { +                axis = xaxes[i]; +                if (axis && axis.used) +                    res["x" + axis.n] = axis.c2p(pos.left); +            } + +            for (i = 0; i < yaxes.length; ++i) { +                axis = yaxes[i]; +                if (axis && axis.used) +                    res["y" + axis.n] = axis.c2p(pos.top); +            } +             +            if (res.x1 !== undefined) +                res.x = res.x1; +            if (res.y1 !== undefined) +                res.y = res.y1; + +            return res; +        } +         +        function axisToCanvasCoords(pos) { +            // get canvas coords from the first pair of x/y found in pos +            var res = {}, i, axis, key; + +            for (i = 0; i < xaxes.length; ++i) { +                axis = xaxes[i]; +                if (axis && axis.used) { +                    key = "x" + axis.n; +                    if (pos[key] == null && axis.n == 1) +                        key = "x"; + +                    if (pos[key] != null) { +                        res.left = axis.p2c(pos[key]); +                        break; +                    } +                } +            } +             +            for (i = 0; i < yaxes.length; ++i) { +                axis = yaxes[i]; +                if (axis && axis.used) { +                    key = "y" + axis.n; +                    if (pos[key] == null && axis.n == 1) +                        key = "y"; + +                    if (pos[key] != null) { +                        res.top = axis.p2c(pos[key]); +                        break; +                    } +                } +            } +             +            return res; +        } +         +        function getOrCreateAxis(axes, number) { +            if (!axes[number - 1]) +                axes[number - 1] = { +                    n: number, // save the number for future reference +                    direction: axes == xaxes ? "x" : "y", +                    options: $.extend(true, {}, axes == xaxes ? options.xaxis : options.yaxis) +                }; +                 +            return axes[number - 1]; +        } + +        function fillInSeriesOptions() { +            var i; +             +            // collect what we already got of colors +            var neededColors = series.length, +                usedColors = [], +                assignedColors = []; +            for (i = 0; i < series.length; ++i) { +                var sc = series[i].color; +                if (sc != null) { +                    --neededColors; +                    if (typeof sc == "number") +                        assignedColors.push(sc); +                    else +                        usedColors.push($.color.parse(series[i].color)); +                } +            } +             +            // we might need to generate more colors if higher indices +            // are assigned +            for (i = 0; i < assignedColors.length; ++i) { +                neededColors = Math.max(neededColors, assignedColors[i] + 1); +            } + +            // produce colors as needed +            var colors = [], variation = 0; +            i = 0; +            while (colors.length < neededColors) { +                var c; +                if (options.colors.length == i) // check degenerate case +                    c = $.color.make(100, 100, 100); +                else +                    c = $.color.parse(options.colors[i]); + +                // vary color if needed +                var sign = variation % 2 == 1 ? -1 : 1; +                c.scale('rgb', 1 + sign * Math.ceil(variation / 2) * 0.2) + +                // FIXME: if we're getting to close to something else, +                // we should probably skip this one +                colors.push(c); +                 +                ++i; +                if (i >= options.colors.length) { +                    i = 0; +                    ++variation; +                } +            } + +            // fill in the options +            var colori = 0, s; +            for (i = 0; i < series.length; ++i) { +                s = series[i]; +                 +                // assign colors +                if (s.color == null) { +                    s.color = colors[colori].toString(); +                    ++colori; +                } +                else if (typeof s.color == "number") +                    s.color = colors[s.color].toString(); + +                // turn on lines automatically in case nothing is set +                if (s.lines.show == null) { +                    var v, show = true; +                    for (v in s) +                        if (s[v] && s[v].show) { +                            show = false; +                            break; +                        } +                    if (show) +                        s.lines.show = true; +                } + +                // setup axes +                s.xaxis = getOrCreateAxis(xaxes, axisNumber(s, "x")); +                s.yaxis = getOrCreateAxis(yaxes, axisNumber(s, "y")); +            } +        } +         +        function processData() { +            var topSentry = Number.POSITIVE_INFINITY, +                bottomSentry = Number.NEGATIVE_INFINITY, +                fakeInfinity = Number.MAX_VALUE, +                i, j, k, m, length, +                s, points, ps, x, y, axis, val, f, p; + +            function updateAxis(axis, min, max) { +                if (min < axis.datamin && min != -fakeInfinity) +                    axis.datamin = min; +                if (max > axis.datamax && max != fakeInfinity) +                    axis.datamax = max; +            } + +            $.each(allAxes(), function (_, axis) { +                // init axis +                axis.datamin = topSentry; +                axis.datamax = bottomSentry; +                axis.used = false; +            }); +             +            for (i = 0; i < series.length; ++i) { +                s = series[i]; +                s.datapoints = { points: [] }; +                 +                executeHooks(hooks.processRawData, [ s, s.data, s.datapoints ]); +            } +             +            // first pass: clean and copy data +            for (i = 0; i < series.length; ++i) { +                s = series[i]; + +                var data = s.data, format = s.datapoints.format; + +                if (!format) { +                    format = []; +                    // find out how to copy +                    format.push({ x: true, number: true, required: true }); +                    format.push({ y: true, number: true, required: true }); + +                    if (s.bars.show || (s.lines.show && s.lines.fill)) { +                        format.push({ y: true, number: true, required: false, defaultValue: 0 }); +                        if (s.bars.horizontal) { +                            delete format[format.length - 1].y; +                            format[format.length - 1].x = true; +                        } +                    } +                     +                    s.datapoints.format = format; +                } + +                if (s.datapoints.pointsize != null) +                    continue; // already filled in + +                s.datapoints.pointsize = format.length; +                 +                ps = s.datapoints.pointsize; +                points = s.datapoints.points; + +                insertSteps = s.lines.show && s.lines.steps; +                s.xaxis.used = s.yaxis.used = true; +                 +                for (j = k = 0; j < data.length; ++j, k += ps) { +                    p = data[j]; + +                    var nullify = p == null; +                    if (!nullify) { +                        for (m = 0; m < ps; ++m) { +                            val = p[m]; +                            f = format[m]; + +                            if (f) { +                                if (f.number && val != null) { +                                    val = +val; // convert to number +                                    if (isNaN(val)) +                                        val = null; +                                    else if (val == Infinity) +                                        val = fakeInfinity; +                                    else if (val == -Infinity) +                                        val = -fakeInfinity; +                                } + +                                if (val == null) { +                                    if (f.required) +                                        nullify = true; +                                     +                                    if (f.defaultValue != null) +                                        val = f.defaultValue; +                                } +                            } +                             +                            points[k + m] = val; +                        } +                    } +                     +                    if (nullify) { +                        for (m = 0; m < ps; ++m) { +                            val = points[k + m]; +                            if (val != null) { +                                f = format[m]; +                                // extract min/max info +                                if (f.x) +                                    updateAxis(s.xaxis, val, val); +                                if (f.y) +                                    updateAxis(s.yaxis, val, val); +                            } +                            points[k + m] = null; +                        } +                    } +                    else { +                        // a little bit of line specific stuff that +                        // perhaps shouldn't be here, but lacking +                        // better means... +                        if (insertSteps && k > 0 +                            && points[k - ps] != null +                            && points[k - ps] != points[k] +                            && points[k - ps + 1] != points[k + 1]) { +                            // copy the point to make room for a middle point +                            for (m = 0; m < ps; ++m) +                                points[k + ps + m] = points[k + m]; + +                            // middle point has same y +                            points[k + 1] = points[k - ps + 1]; + +                            // we've added a point, better reflect that +                            k += ps; +                        } +                    } +                } +            } + +            // give the hooks a chance to run +            for (i = 0; i < series.length; ++i) { +                s = series[i]; +                 +                executeHooks(hooks.processDatapoints, [ s, s.datapoints]); +            } + +            // second pass: find datamax/datamin for auto-scaling +            for (i = 0; i < series.length; ++i) { +                s = series[i]; +                points = s.datapoints.points, +                ps = s.datapoints.pointsize; + +                var xmin = topSentry, ymin = topSentry, +                    xmax = bottomSentry, ymax = bottomSentry; +                 +                for (j = 0; j < points.length; j += ps) { +                    if (points[j] == null) +                        continue; + +                    for (m = 0; m < ps; ++m) { +                        val = points[j + m]; +                        f = format[m]; +                        if (!f || val == fakeInfinity || val == -fakeInfinity) +                            continue; +                         +                        if (f.x) { +                            if (val < xmin) +                                xmin = val; +                            if (val > xmax) +                                xmax = val; +                        } +                        if (f.y) { +                            if (val < ymin) +                                ymin = val; +                            if (val > ymax) +                                ymax = val; +                        } +                    } +                } +                 +                if (s.bars.show) { +                    // make sure we got room for the bar on the dancing floor +                    var delta = s.bars.align == "left" ? 0 : -s.bars.barWidth/2; +                    if (s.bars.horizontal) { +                        ymin += delta; +                        ymax += delta + s.bars.barWidth; +                    } +                    else { +                        xmin += delta; +                        xmax += delta + s.bars.barWidth; +                    } +                } +                 +                updateAxis(s.xaxis, xmin, xmax); +                updateAxis(s.yaxis, ymin, ymax); +            } + +            $.each(allAxes(), function (_, axis) { +                if (axis.datamin == topSentry) +                    axis.datamin = null; +                if (axis.datamax == bottomSentry) +                    axis.datamax = null; +            }); +        } + +        function makeCanvas(skipPositioning, cls) { +            var c = document.createElement('canvas'); +            c.className = cls; +            c.width = canvasWidth; +            c.height = canvasHeight; +                     +            if (!skipPositioning) +                $(c).css({ position: 'absolute', left: 0, top: 0 }); +                 +            $(c).appendTo(placeholder); +                 +            if (!c.getContext) // excanvas hack +                c = window.G_vmlCanvasManager.initElement(c); + +            // used for resetting in case we get replotted +            c.getContext("2d").save(); +             +            return c; +        } + +        function getCanvasDimensions() { +            canvasWidth = placeholder.width(); +            canvasHeight = placeholder.height(); +             +            if (canvasWidth <= 0 || canvasHeight <= 0) +                throw "Invalid dimensions for plot, width = " + canvasWidth + ", height = " + canvasHeight; +        } + +        function resizeCanvas(c) { +            // resizing should reset the state (excanvas seems to be +            // buggy though) +            if (c.width != canvasWidth) +                c.width = canvasWidth; + +            if (c.height != canvasHeight) +                c.height = canvasHeight; + +            // so try to get back to the initial state (even if it's +            // gone now, this should be safe according to the spec) +            var cctx = c.getContext("2d"); +            cctx.restore(); + +            // and save again +            cctx.save(); +        } +         +        function setupCanvases() { +            var reused, +                existingCanvas = placeholder.children("canvas.base"), +                existingOverlay = placeholder.children("canvas.overlay"); + +            if (existingCanvas.length == 0 || existingOverlay == 0) { +                // init everything +                 +                placeholder.html(""); // make sure placeholder is clear +             +                placeholder.css({ padding: 0 }); // padding messes up the positioning +                 +                if (placeholder.css("position") == 'static') +                    placeholder.css("position", "relative"); // for positioning labels and overlay + +                getCanvasDimensions(); +                 +                canvas = makeCanvas(true, "base"); +                overlay = makeCanvas(false, "overlay"); // overlay canvas for interactive features + +                reused = false; +            } +            else { +                // reuse existing elements + +                canvas = existingCanvas.get(0); +                overlay = existingOverlay.get(0); + +                reused = true; +            } + +            ctx = canvas.getContext("2d"); +            octx = overlay.getContext("2d"); + +            // we include the canvas in the event holder too, because IE 7 +            // sometimes has trouble with the stacking order +            eventHolder = $([overlay, canvas]); + +            if (reused) { +                // run shutdown in the old plot object +                placeholder.data("plot").shutdown(); + +                // reset reused canvases +                plot.resize(); +                 +                // make sure overlay pixels are cleared (canvas is cleared when we redraw) +                octx.clearRect(0, 0, canvasWidth, canvasHeight); +                 +                // then whack any remaining obvious garbage left +                eventHolder.unbind(); +                placeholder.children().not([canvas, overlay]).remove(); +            } + +            // save in case we get replotted +            placeholder.data("plot", plot); +        } + +        function bindEvents() { +            // bind events +            if (options.grid.hoverable) { +                eventHolder.mousemove(onMouseMove); +                eventHolder.mouseleave(onMouseLeave); +            } + +            if (options.grid.clickable) +                eventHolder.click(onClick); + +            executeHooks(hooks.bindEvents, [eventHolder]); +        } + +        function shutdown() { +            if (redrawTimeout) +                clearTimeout(redrawTimeout); +             +            eventHolder.unbind("mousemove", onMouseMove); +            eventHolder.unbind("mouseleave", onMouseLeave); +            eventHolder.unbind("click", onClick); +             +            executeHooks(hooks.shutdown, [eventHolder]); +        } + +        function setTransformationHelpers(axis) { +            // set helper functions on the axis, assumes plot area +            // has been computed already +             +            function identity(x) { return x; } +             +            var s, m, t = axis.options.transform || identity, +                it = axis.options.inverseTransform; +             +            // precompute how much the axis is scaling a point +            // in canvas space +            if (axis.direction == "x") { +                s = axis.scale = plotWidth / Math.abs(t(axis.max) - t(axis.min)); +                m = Math.min(t(axis.max), t(axis.min)); +            } +            else { +                s = axis.scale = plotHeight / Math.abs(t(axis.max) - t(axis.min)); +                s = -s; +                m = Math.max(t(axis.max), t(axis.min)); +            } + +            // data point to canvas coordinate +            if (t == identity) // slight optimization +                axis.p2c = function (p) { return (p - m) * s; }; +            else +                axis.p2c = function (p) { return (t(p) - m) * s; }; +            // canvas coordinate to data point +            if (!it) +                axis.c2p = function (c) { return m + c / s; }; +            else +                axis.c2p = function (c) { return it(m + c / s); }; +        } + +        function measureTickLabels(axis) { +            var opts = axis.options, i, ticks = axis.ticks || [], labels = [], +                l, w = opts.labelWidth, h = opts.labelHeight, dummyDiv; + +            function makeDummyDiv(labels, width) { +                return $('<div style="position:absolute;top:-10000px;' + width + 'font-size:smaller">' + +                         '<div class="' + axis.direction + 'Axis ' + axis.direction + axis.n + 'Axis">' +                         + labels.join("") + '</div></div>') +                    .appendTo(placeholder); +            } +             +            if (axis.direction == "x") { +                // to avoid measuring the widths of the labels (it's slow), we +                // construct fixed-size boxes and put the labels inside +                // them, we don't need the exact figures and the +                // fixed-size box content is easy to center +                if (w == null) +                    w = Math.floor(canvasWidth / (ticks.length > 0 ? ticks.length : 1)); + +                // measure x label heights +                if (h == null) { +                    labels = []; +                    for (i = 0; i < ticks.length; ++i) { +                        l = ticks[i].label; +                        if (l) +                            labels.push('<div class="tickLabel" style="float:left;width:' + w + 'px">' + l + '</div>'); +                    } + +                    if (labels.length > 0) { +                        // stick them all in the same div and measure +                        // collective height +                        labels.push('<div style="clear:left"></div>'); +                        dummyDiv = makeDummyDiv(labels, "width:10000px;"); +                        h = dummyDiv.height(); +                        dummyDiv.remove(); +                    } +                } +            } +            else if (w == null || h == null) { +                // calculate y label dimensions +                for (i = 0; i < ticks.length; ++i) { +                    l = ticks[i].label; +                    if (l) +                        labels.push('<div class="tickLabel">' + l + '</div>'); +                } +                 +                if (labels.length > 0) { +                    dummyDiv = makeDummyDiv(labels, ""); +                    if (w == null) +                        w = dummyDiv.children().width(); +                    if (h == null) +                        h = dummyDiv.find("div.tickLabel").height(); +                    dummyDiv.remove(); +                } +            } + +            if (w == null) +                w = 0; +            if (h == null) +                h = 0; + +            axis.labelWidth = w; +            axis.labelHeight = h; +        } + +        function allocateAxisBoxFirstPhase(axis) { +            // find the bounding box of the axis by looking at label +            // widths/heights and ticks, make room by diminishing the +            // plotOffset + +            var lw = axis.labelWidth, +                lh = axis.labelHeight, +                pos = axis.options.position, +                tickLength = axis.options.tickLength, +                axismargin = options.grid.axisMargin, +                padding = options.grid.labelMargin, +                all = axis.direction == "x" ? xaxes : yaxes, +                index; + +            // determine axis margin +            var samePosition = $.grep(all, function (a) { +                return a && a.options.position == pos && a.reserveSpace; +            }); +            if ($.inArray(axis, samePosition) == samePosition.length - 1) +                axismargin = 0; // outermost + +            // determine tick length - if we're innermost, we can use "full" +            if (tickLength == null) +                tickLength = "full"; + +            var sameDirection = $.grep(all, function (a) { +                return a && a.reserveSpace; +            }); + +            var innermost = $.inArray(axis, sameDirection) == 0; +            if (!innermost && tickLength == "full") +                tickLength = 5; +                 +            if (!isNaN(+tickLength)) +                padding += +tickLength; + +            // compute box +            if (axis.direction == "x") { +                lh += padding; +                 +                if (pos == "bottom") { +                    plotOffset.bottom += lh + axismargin; +                    axis.box = { top: canvasHeight - plotOffset.bottom, height: lh }; +                } +                else { +                    axis.box = { top: plotOffset.top + axismargin, height: lh }; +                    plotOffset.top += lh + axismargin; +                } +            } +            else { +                lw += padding; +                 +                if (pos == "left") { +                    axis.box = { left: plotOffset.left + axismargin, width: lw }; +                    plotOffset.left += lw + axismargin; +                } +                else { +                    plotOffset.right += lw + axismargin; +                    axis.box = { left: canvasWidth - plotOffset.right, width: lw }; +                } +            } + +             // save for future reference +            axis.position = pos; +            axis.tickLength = tickLength; +            axis.box.padding = padding; +            axis.innermost = innermost; +        } + +        function allocateAxisBoxSecondPhase(axis) { +            // set remaining bounding box coordinates +            if (axis.direction == "x") { +                axis.box.left = plotOffset.left; +                axis.box.width = plotWidth; +            } +            else { +                axis.box.top = plotOffset.top; +                axis.box.height = plotHeight; +            } +        } +         +        function setupGrid() { +            var i, axes = allAxes(); + +            // first calculate the plot and axis box dimensions + +            $.each(axes, function (_, axis) { +                axis.show = axis.options.show; +                if (axis.show == null) +                    axis.show = axis.used; // by default an axis is visible if it's got data +                 +                axis.reserveSpace = axis.show || axis.options.reserveSpace; + +                setRange(axis); +            }); + +            allocatedAxes = $.grep(axes, function (axis) { return axis.reserveSpace; }); + +            plotOffset.left = plotOffset.right = plotOffset.top = plotOffset.bottom = 0; +            if (options.grid.show) { +                $.each(allocatedAxes, function (_, axis) { +                    // make the ticks +                    setupTickGeneration(axis); +                    setTicks(axis); +                    snapRangeToTicks(axis, axis.ticks); + +                    // find labelWidth/Height for axis +                    measureTickLabels(axis); +                }); + +                // with all dimensions in house, we can compute the +                // axis boxes, start from the outside (reverse order) +                for (i = allocatedAxes.length - 1; i >= 0; --i) +                    allocateAxisBoxFirstPhase(allocatedAxes[i]); + +                // make sure we've got enough space for things that +                // might stick out +                var minMargin = options.grid.minBorderMargin; +                if (minMargin == null) { +                    minMargin = 0; +                    for (i = 0; i < series.length; ++i) +                        minMargin = Math.max(minMargin, series[i].points.radius + series[i].points.lineWidth/2); +                } +                     +                for (var a in plotOffset) { +                    plotOffset[a] += options.grid.borderWidth; +                    plotOffset[a] = Math.max(minMargin, plotOffset[a]); +                } +            } +             +            plotWidth = canvasWidth - plotOffset.left - plotOffset.right; +            plotHeight = canvasHeight - plotOffset.bottom - plotOffset.top; + +            // now we got the proper plotWidth/Height, we can compute the scaling +            $.each(axes, function (_, axis) { +                setTransformationHelpers(axis); +            }); + +            if (options.grid.show) { +                $.each(allocatedAxes, function (_, axis) { +                    allocateAxisBoxSecondPhase(axis); +                }); + +                insertAxisLabels(); +            } +             +            insertLegend(); +        } +         +        function setRange(axis) { +            var opts = axis.options, +                min = +(opts.min != null ? opts.min : axis.datamin), +                max = +(opts.max != null ? opts.max : axis.datamax), +                delta = max - min; + +            if (delta == 0.0) { +                // degenerate case +                var widen = max == 0 ? 1 : 0.01; + +                if (opts.min == null) +                    min -= widen; +                // always widen max if we couldn't widen min to ensure we +                // don't fall into min == max which doesn't work +                if (opts.max == null || opts.min != null) +                    max += widen; +            } +            else { +                // consider autoscaling +                var margin = opts.autoscaleMargin; +                if (margin != null) { +                    if (opts.min == null) { +                        min -= delta * margin; +                        // make sure we don't go below zero if all values +                        // are positive +                        if (min < 0 && axis.datamin != null && axis.datamin >= 0) +                            min = 0; +                    } +                    if (opts.max == null) { +                        max += delta * margin; +                        if (max > 0 && axis.datamax != null && axis.datamax <= 0) +                            max = 0; +                    } +                } +            } +            axis.min = min; +            axis.max = max; +        } + +        function setupTickGeneration(axis) { +            var opts = axis.options; +                 +            // estimate number of ticks +            var noTicks; +            if (typeof opts.ticks == "number" && opts.ticks > 0) +                noTicks = opts.ticks; +            else +                // heuristic based on the model a*sqrt(x) fitted to +                // some data points that seemed reasonable +                noTicks = 0.3 * Math.sqrt(axis.direction == "x" ? canvasWidth : canvasHeight); + +            var delta = (axis.max - axis.min) / noTicks, +                size, generator, unit, formatter, i, magn, norm; + +            if (opts.mode == "time") { +                // pretty handling of time +                 +                // map of app. size of time units in milliseconds +                var timeUnitSize = { +                    "second": 1000, +                    "minute": 60 * 1000, +                    "hour": 60 * 60 * 1000, +                    "day": 24 * 60 * 60 * 1000, +                    "month": 30 * 24 * 60 * 60 * 1000, +                    "year": 365.2425 * 24 * 60 * 60 * 1000 +                }; + + +                // the allowed tick sizes, after 1 year we use +                // an integer algorithm +                var spec = [ +                    [1, "second"], [2, "second"], [5, "second"], [10, "second"], +                    [30, "second"],  +                    [1, "minute"], [2, "minute"], [5, "minute"], [10, "minute"], +                    [30, "minute"],  +                    [1, "hour"], [2, "hour"], [4, "hour"], +                    [8, "hour"], [12, "hour"], +                    [1, "day"], [2, "day"], [3, "day"], +                    [0.25, "month"], [0.5, "month"], [1, "month"], +                    [2, "month"], [3, "month"], [6, "month"], +                    [1, "year"] +                ]; + +                var minSize = 0; +                if (opts.minTickSize != null) { +                    if (typeof opts.tickSize == "number") +                        minSize = opts.tickSize; +                    else +                        minSize = opts.minTickSize[0] * timeUnitSize[opts.minTickSize[1]]; +                } + +                for (var i = 0; i < spec.length - 1; ++i) +                    if (delta < (spec[i][0] * timeUnitSize[spec[i][1]] +                                 + spec[i + 1][0] * timeUnitSize[spec[i + 1][1]]) / 2 +                       && spec[i][0] * timeUnitSize[spec[i][1]] >= minSize) +                        break; +                size = spec[i][0]; +                unit = spec[i][1]; +                 +                // special-case the possibility of several years +                if (unit == "year") { +                    magn = Math.pow(10, Math.floor(Math.log(delta / timeUnitSize.year) / Math.LN10)); +                    norm = (delta / timeUnitSize.year) / magn; +                    if (norm < 1.5) +                        size = 1; +                    else if (norm < 3) +                        size = 2; +                    else if (norm < 7.5) +                        size = 5; +                    else +                        size = 10; + +                    size *= magn; +                } + +                axis.tickSize = opts.tickSize || [size, unit]; +                 +                generator = function(axis) { +                    var ticks = [], +                        tickSize = axis.tickSize[0], unit = axis.tickSize[1], +                        d = new Date(axis.min); +                     +                    var step = tickSize * timeUnitSize[unit]; + +                    if (unit == "second") +                        d.setUTCSeconds(floorInBase(d.getUTCSeconds(), tickSize)); +                    if (unit == "minute") +                        d.setUTCMinutes(floorInBase(d.getUTCMinutes(), tickSize)); +                    if (unit == "hour") +                        d.setUTCHours(floorInBase(d.getUTCHours(), tickSize)); +                    if (unit == "month") +                        d.setUTCMonth(floorInBase(d.getUTCMonth(), tickSize)); +                    if (unit == "year") +                        d.setUTCFullYear(floorInBase(d.getUTCFullYear(), tickSize)); +                     +                    // reset smaller components +                    d.setUTCMilliseconds(0); +                    if (step >= timeUnitSize.minute) +                        d.setUTCSeconds(0); +                    if (step >= timeUnitSize.hour) +                        d.setUTCMinutes(0); +                    if (step >= timeUnitSize.day) +                        d.setUTCHours(0); +                    if (step >= timeUnitSize.day * 4) +                        d.setUTCDate(1); +                    if (step >= timeUnitSize.year) +                        d.setUTCMonth(0); + + +                    var carry = 0, v = Number.NaN, prev; +                    do { +                        prev = v; +                        v = d.getTime(); +                        ticks.push(v); +                        if (unit == "month") { +                            if (tickSize < 1) { +                                // a bit complicated - we'll divide the month +                                // up but we need to take care of fractions +                                // so we don't end up in the middle of a day +                                d.setUTCDate(1); +                                var start = d.getTime(); +                                d.setUTCMonth(d.getUTCMonth() + 1); +                                var end = d.getTime(); +                                d.setTime(v + carry * timeUnitSize.hour + (end - start) * tickSize); +                                carry = d.getUTCHours(); +                                d.setUTCHours(0); +                            } +                            else +                                d.setUTCMonth(d.getUTCMonth() + tickSize); +                        } +                        else if (unit == "year") { +                            d.setUTCFullYear(d.getUTCFullYear() + tickSize); +                        } +                        else +                            d.setTime(v + step); +                    } while (v < axis.max && v != prev); + +                    return ticks; +                }; + +                formatter = function (v, axis) { +                    var d = new Date(v); + +                    // first check global format +                    if (opts.timeformat != null) +                        return $.plot.formatDate(d, opts.timeformat, opts.monthNames); +                     +                    var t = axis.tickSize[0] * timeUnitSize[axis.tickSize[1]]; +                    var span = axis.max - axis.min; +                    var suffix = (opts.twelveHourClock) ? " %p" : ""; +                     +                    if (t < timeUnitSize.minute) +                        fmt = "%h:%M:%S" + suffix; +                    else if (t < timeUnitSize.day) { +                        if (span < 2 * timeUnitSize.day) +                            fmt = "%h:%M" + suffix; +                        else +                            fmt = "%b %d %h:%M" + suffix; +                    } +                    else if (t < timeUnitSize.month) +                        fmt = "%b %d"; +                    else if (t < timeUnitSize.year) { +                        if (span < timeUnitSize.year) +                            fmt = "%b"; +                        else +                            fmt = "%b %y"; +                    } +                    else +                        fmt = "%y"; +                     +                    return $.plot.formatDate(d, fmt, opts.monthNames); +                }; +            } +            else { +                // pretty rounding of base-10 numbers +                var maxDec = opts.tickDecimals; +                var dec = -Math.floor(Math.log(delta) / Math.LN10); +                if (maxDec != null && dec > maxDec) +                    dec = maxDec; + +                magn = Math.pow(10, -dec); +                norm = delta / magn; // norm is between 1.0 and 10.0 +                 +                if (norm < 1.5) +                    size = 1; +                else if (norm < 3) { +                    size = 2; +                    // special case for 2.5, requires an extra decimal +                    if (norm > 2.25 && (maxDec == null || dec + 1 <= maxDec)) { +                        size = 2.5; +                        ++dec; +                    } +                } +                else if (norm < 7.5) +                    size = 5; +                else +                    size = 10; + +                size *= magn; +                 +                if (opts.minTickSize != null && size < opts.minTickSize) +                    size = opts.minTickSize; + +                axis.tickDecimals = Math.max(0, maxDec != null ? maxDec : dec); +                axis.tickSize = opts.tickSize || size; + +                generator = function (axis) { +                    var ticks = []; + +                    // spew out all possible ticks +                    var start = floorInBase(axis.min, axis.tickSize), +                        i = 0, v = Number.NaN, prev; +                    do { +                        prev = v; +                        v = start + i * axis.tickSize; +                        ticks.push(v); +                        ++i; +                    } while (v < axis.max && v != prev); +                    return ticks; +                }; + +                formatter = function (v, axis) { +                    return v.toFixed(axis.tickDecimals); +                }; +            } + +            if (opts.alignTicksWithAxis != null) { +                var otherAxis = (axis.direction == "x" ? xaxes : yaxes)[opts.alignTicksWithAxis - 1]; +                if (otherAxis && otherAxis.used && otherAxis != axis) { +                    // consider snapping min/max to outermost nice ticks +                    var niceTicks = generator(axis); +                    if (niceTicks.length > 0) { +                        if (opts.min == null) +                            axis.min = Math.min(axis.min, niceTicks[0]); +                        if (opts.max == null && niceTicks.length > 1) +                            axis.max = Math.max(axis.max, niceTicks[niceTicks.length - 1]); +                    } +                     +                    generator = function (axis) { +                        // copy ticks, scaled to this axis +                        var ticks = [], v, i; +                        for (i = 0; i < otherAxis.ticks.length; ++i) { +                            v = (otherAxis.ticks[i].v - otherAxis.min) / (otherAxis.max - otherAxis.min); +                            v = axis.min + v * (axis.max - axis.min); +                            ticks.push(v); +                        } +                        return ticks; +                    }; +                     +                    // we might need an extra decimal since forced +                    // ticks don't necessarily fit naturally +                    if (axis.mode != "time" && opts.tickDecimals == null) { +                        var extraDec = Math.max(0, -Math.floor(Math.log(delta) / Math.LN10) + 1), +                            ts = generator(axis); + +                        // only proceed if the tick interval rounded +                        // with an extra decimal doesn't give us a +                        // zero at end +                        if (!(ts.length > 1 && /\..*0$/.test((ts[1] - ts[0]).toFixed(extraDec)))) +                            axis.tickDecimals = extraDec; +                    } +                } +            } + +            axis.tickGenerator = generator; +            if ($.isFunction(opts.tickFormatter)) +                axis.tickFormatter = function (v, axis) { return "" + opts.tickFormatter(v, axis); }; +            else +                axis.tickFormatter = formatter; +        } +         +        function setTicks(axis) { +            var oticks = axis.options.ticks, ticks = []; +            if (oticks == null || (typeof oticks == "number" && oticks > 0)) +                ticks = axis.tickGenerator(axis); +            else if (oticks) { +                if ($.isFunction(oticks)) +                    // generate the ticks +                    ticks = oticks({ min: axis.min, max: axis.max }); +                else +                    ticks = oticks; +            } + +            // clean up/labelify the supplied ticks, copy them over +            var i, v; +            axis.ticks = []; +            for (i = 0; i < ticks.length; ++i) { +                var label = null; +                var t = ticks[i]; +                if (typeof t == "object") { +                    v = +t[0]; +                    if (t.length > 1) +                        label = t[1]; +                } +                else +                    v = +t; +                if (label == null) +                    label = axis.tickFormatter(v, axis); +                if (!isNaN(v)) +                    axis.ticks.push({ v: v, label: label }); +            } +        } + +        function snapRangeToTicks(axis, ticks) { +            if (axis.options.autoscaleMargin && ticks.length > 0) { +                // snap to ticks +                if (axis.options.min == null) +                    axis.min = Math.min(axis.min, ticks[0].v); +                if (axis.options.max == null && ticks.length > 1) +                    axis.max = Math.max(axis.max, ticks[ticks.length - 1].v); +            } +        } +       +        function draw() { +            ctx.clearRect(0, 0, canvasWidth, canvasHeight); + +            var grid = options.grid; + +            // draw background, if any +            if (grid.show && grid.backgroundColor) +                drawBackground(); +             +            if (grid.show && !grid.aboveData) +                drawGrid(); + +            for (var i = 0; i < series.length; ++i) { +                executeHooks(hooks.drawSeries, [ctx, series[i]]); +                drawSeries(series[i]); +            } + +            executeHooks(hooks.draw, [ctx]); +             +            if (grid.show && grid.aboveData) +                drawGrid(); +        } + +        function extractRange(ranges, coord) { +            var axis, from, to, key, axes = allAxes(); + +            for (i = 0; i < axes.length; ++i) { +                axis = axes[i]; +                if (axis.direction == coord) { +                    key = coord + axis.n + "axis"; +                    if (!ranges[key] && axis.n == 1) +                        key = coord + "axis"; // support x1axis as xaxis +                    if (ranges[key]) { +                        from = ranges[key].from; +                        to = ranges[key].to; +                        break; +                    } +                } +            } + +            // backwards-compat stuff - to be removed in future +            if (!ranges[key]) { +                axis = coord == "x" ? xaxes[0] : yaxes[0]; +                from = ranges[coord + "1"]; +                to = ranges[coord + "2"]; +            } + +            // auto-reverse as an added bonus +            if (from != null && to != null && from > to) { +                var tmp = from; +                from = to; +                to = tmp; +            } +             +            return { from: from, to: to, axis: axis }; +        } +         +        function drawBackground() { +            ctx.save(); +            ctx.translate(plotOffset.left, plotOffset.top); + +            ctx.fillStyle = getColorOrGradient(options.grid.backgroundColor, plotHeight, 0, "rgba(255, 255, 255, 0)"); +            ctx.fillRect(0, 0, plotWidth, plotHeight); +            ctx.restore(); +        } + +        function drawGrid() { +            var i; +             +            ctx.save(); +            ctx.translate(plotOffset.left, plotOffset.top); + +            // draw markings +            var markings = options.grid.markings; +            if (markings) { +                if ($.isFunction(markings)) { +                    var axes = plot.getAxes(); +                    // xmin etc. is backwards compatibility, to be +                    // removed in the future +                    axes.xmin = axes.xaxis.min; +                    axes.xmax = axes.xaxis.max; +                    axes.ymin = axes.yaxis.min; +                    axes.ymax = axes.yaxis.max; +                     +                    markings = markings(axes); +                } + +                for (i = 0; i < markings.length; ++i) { +                    var m = markings[i], +                        xrange = extractRange(m, "x"), +                        yrange = extractRange(m, "y"); + +                    // fill in missing +                    if (xrange.from == null) +                        xrange.from = xrange.axis.min; +                    if (xrange.to == null) +                        xrange.to = xrange.axis.max; +                    if (yrange.from == null) +                        yrange.from = yrange.axis.min; +                    if (yrange.to == null) +                        yrange.to = yrange.axis.max; + +                    // clip +                    if (xrange.to < xrange.axis.min || xrange.from > xrange.axis.max || +                        yrange.to < yrange.axis.min || yrange.from > yrange.axis.max) +                        continue; + +                    xrange.from = Math.max(xrange.from, xrange.axis.min); +                    xrange.to = Math.min(xrange.to, xrange.axis.max); +                    yrange.from = Math.max(yrange.from, yrange.axis.min); +                    yrange.to = Math.min(yrange.to, yrange.axis.max); + +                    if (xrange.from == xrange.to && yrange.from == yrange.to) +                        continue; + +                    // then draw +                    xrange.from = xrange.axis.p2c(xrange.from); +                    xrange.to = xrange.axis.p2c(xrange.to); +                    yrange.from = yrange.axis.p2c(yrange.from); +                    yrange.to = yrange.axis.p2c(yrange.to); +                     +                    if (xrange.from == xrange.to || yrange.from == yrange.to) { +                        // draw line +                        ctx.beginPath(); +                        ctx.strokeStyle = m.color || options.grid.markingsColor; +                        ctx.lineWidth = m.lineWidth || options.grid.markingsLineWidth; +                        ctx.moveTo(xrange.from, yrange.from); +                        ctx.lineTo(xrange.to, yrange.to); +                        ctx.stroke(); +                    } +                    else { +                        // fill area +                        ctx.fillStyle = m.color || options.grid.markingsColor; +                        ctx.fillRect(xrange.from, yrange.to, +                                     xrange.to - xrange.from, +                                     yrange.from - yrange.to); +                    } +                } +            } +             +            // draw the ticks +            var axes = allAxes(), bw = options.grid.borderWidth; + +            for (var j = 0; j < axes.length; ++j) { +                var axis = axes[j], box = axis.box, +                    t = axis.tickLength, x, y, xoff, yoff; +                if (!axis.show || axis.ticks.length == 0) +                    continue +                 +                ctx.strokeStyle = axis.options.tickColor || $.color.parse(axis.options.color).scale('a', 0.22).toString(); +                ctx.lineWidth = 1; + +                // find the edges +                if (axis.direction == "x") { +                    x = 0; +                    if (t == "full") +                        y = (axis.position == "top" ? 0 : plotHeight); +                    else +                        y = box.top - plotOffset.top + (axis.position == "top" ? box.height : 0); +                } +                else { +                    y = 0; +                    if (t == "full") +                        x = (axis.position == "left" ? 0 : plotWidth); +                    else +                        x = box.left - plotOffset.left + (axis.position == "left" ? box.width : 0); +                } +                 +                // draw tick bar +                if (!axis.innermost) { +                    ctx.beginPath(); +                    xoff = yoff = 0; +                    if (axis.direction == "x") +                        xoff = plotWidth; +                    else +                        yoff = plotHeight; +                     +                    if (ctx.lineWidth == 1) { +                        x = Math.floor(x) + 0.5; +                        y = Math.floor(y) + 0.5; +                    } + +                    ctx.moveTo(x, y); +                    ctx.lineTo(x + xoff, y + yoff); +                    ctx.stroke(); +                } + +                // draw ticks +                ctx.beginPath(); +                for (i = 0; i < axis.ticks.length; ++i) { +                    var v = axis.ticks[i].v; +                     +                    xoff = yoff = 0; + +                    if (v < axis.min || v > axis.max +                        // skip those lying on the axes if we got a border +                        || (t == "full" && bw > 0 +                            && (v == axis.min || v == axis.max))) +                        continue; + +                    if (axis.direction == "x") { +                        x = axis.p2c(v); +                        yoff = t == "full" ? -plotHeight : t; +                         +                        if (axis.position == "top") +                            yoff = -yoff; +                    } +                    else { +                        y = axis.p2c(v); +                        xoff = t == "full" ? -plotWidth : t; +                         +                        if (axis.position == "left") +                            xoff = -xoff; +                    } + +                    if (ctx.lineWidth == 1) { +                        if (axis.direction == "x") +                            x = Math.floor(x) + 0.5; +                        else +                            y = Math.floor(y) + 0.5; +                    } + +                    ctx.moveTo(x, y); +                    ctx.lineTo(x + xoff, y + yoff); +                } +                 +                ctx.stroke(); +            } +             +             +            // draw border +            if (bw) { +                ctx.lineWidth = bw; +                ctx.strokeStyle = options.grid.borderColor; +                ctx.strokeRect(-bw/2, -bw/2, plotWidth + bw, plotHeight + bw); +            } + +            ctx.restore(); +        } + +        function insertAxisLabels() { +            placeholder.find(".tickLabels").remove(); +             +            var html = ['<div class="tickLabels" style="font-size:smaller">']; + +            var axes = allAxes(); +            for (var j = 0; j < axes.length; ++j) { +                var axis = axes[j], box = axis.box; +                if (!axis.show) +                    continue; +                //debug: html.push('<div style="position:absolute;opacity:0.10;background-color:red;left:' + box.left + 'px;top:' + box.top + 'px;width:' + box.width +  'px;height:' + box.height + 'px"></div>') +                html.push('<div class="' + axis.direction + 'Axis ' + axis.direction + axis.n + 'Axis" style="color:' + axis.options.color + '">'); +                for (var i = 0; i < axis.ticks.length; ++i) { +                    var tick = axis.ticks[i]; +                    if (!tick.label || tick.v < axis.min || tick.v > axis.max) +                        continue; + +                    var pos = {}, align; +                     +                    if (axis.direction == "x") { +                        align = "center"; +                        pos.left = Math.round(plotOffset.left + axis.p2c(tick.v) - axis.labelWidth/2); +                        if (axis.position == "bottom") +                            pos.top = box.top + box.padding; +                        else +                            pos.bottom = canvasHeight - (box.top + box.height - box.padding); +                    } +                    else { +                        pos.top = Math.round(plotOffset.top + axis.p2c(tick.v) - axis.labelHeight/2); +                        if (axis.position == "left") { +                            pos.right = canvasWidth - (box.left + box.width - box.padding) +                            align = "right"; +                        } +                        else { +                            pos.left = box.left + box.padding; +                            align = "left"; +                        } +                    } + +                    pos.width = axis.labelWidth; + +                    var style = ["position:absolute", "text-align:" + align ]; +                    for (var a in pos) +                        style.push(a + ":" + pos[a] + "px") +                     +                    html.push('<div class="tickLabel" style="' + style.join(';') + '">' + tick.label + '</div>'); +                } +                html.push('</div>'); +            } + +            html.push('</div>'); + +            placeholder.append(html.join("")); +        } + +        function drawSeries(series) { +            if (series.lines.show) +                drawSeriesLines(series); +            if (series.bars.show) +                drawSeriesBars(series); +            if (series.points.show) +                drawSeriesPoints(series); +        } +         +        function drawSeriesLines(series) { +            function plotLine(datapoints, xoffset, yoffset, axisx, axisy) { +                var points = datapoints.points, +                    ps = datapoints.pointsize, +                    prevx = null, prevy = null; +                 +                ctx.beginPath(); +                for (var i = ps; i < points.length; i += ps) { +                    var x1 = points[i - ps], y1 = points[i - ps + 1], +                        x2 = points[i], y2 = points[i + 1]; +                     +                    if (x1 == null || x2 == null) +                        continue; + +                    // clip with ymin +                    if (y1 <= y2 && y1 < axisy.min) { +                        if (y2 < axisy.min) +                            continue;   // line segment is outside +                        // compute new intersection point +                        x1 = (axisy.min - y1) / (y2 - y1) * (x2 - x1) + x1; +                        y1 = axisy.min; +                    } +                    else if (y2 <= y1 && y2 < axisy.min) { +                        if (y1 < axisy.min) +                            continue; +                        x2 = (axisy.min - y1) / (y2 - y1) * (x2 - x1) + x1; +                        y2 = axisy.min; +                    } + +                    // clip with ymax +                    if (y1 >= y2 && y1 > axisy.max) { +                        if (y2 > axisy.max) +                            continue; +                        x1 = (axisy.max - y1) / (y2 - y1) * (x2 - x1) + x1; +                        y1 = axisy.max; +                    } +                    else if (y2 >= y1 && y2 > axisy.max) { +                        if (y1 > axisy.max) +                            continue; +                        x2 = (axisy.max - y1) / (y2 - y1) * (x2 - x1) + x1; +                        y2 = axisy.max; +                    } + +                    // clip with xmin +                    if (x1 <= x2 && x1 < axisx.min) { +                        if (x2 < axisx.min) +                            continue; +                        y1 = (axisx.min - x1) / (x2 - x1) * (y2 - y1) + y1; +                        x1 = axisx.min; +                    } +                    else if (x2 <= x1 && x2 < axisx.min) { +                        if (x1 < axisx.min) +                            continue; +                        y2 = (axisx.min - x1) / (x2 - x1) * (y2 - y1) + y1; +                        x2 = axisx.min; +                    } + +                    // clip with xmax +                    if (x1 >= x2 && x1 > axisx.max) { +                        if (x2 > axisx.max) +                            continue; +                        y1 = (axisx.max - x1) / (x2 - x1) * (y2 - y1) + y1; +                        x1 = axisx.max; +                    } +                    else if (x2 >= x1 && x2 > axisx.max) { +                        if (x1 > axisx.max) +                            continue; +                        y2 = (axisx.max - x1) / (x2 - x1) * (y2 - y1) + y1; +                        x2 = axisx.max; +                    } + +                    if (x1 != prevx || y1 != prevy) +                        ctx.moveTo(axisx.p2c(x1) + xoffset, axisy.p2c(y1) + yoffset); +                     +                    prevx = x2; +                    prevy = y2; +                    ctx.lineTo(axisx.p2c(x2) + xoffset, axisy.p2c(y2) + yoffset); +                } +                ctx.stroke(); +            } + +            function plotLineArea(datapoints, axisx, axisy) { +                var points = datapoints.points, +                    ps = datapoints.pointsize, +                    bottom = Math.min(Math.max(0, axisy.min), axisy.max), +                    i = 0, top, areaOpen = false, +                    ypos = 1, segmentStart = 0, segmentEnd = 0; + +                // we process each segment in two turns, first forward +                // direction to sketch out top, then once we hit the +                // end we go backwards to sketch the bottom +                while (true) { +                    if (ps > 0 && i > points.length + ps) +                        break; + +                    i += ps; // ps is negative if going backwards + +                    var x1 = points[i - ps], +                        y1 = points[i - ps + ypos], +                        x2 = points[i], y2 = points[i + ypos]; + +                    if (areaOpen) { +                        if (ps > 0 && x1 != null && x2 == null) { +                            // at turning point +                            segmentEnd = i; +                            ps = -ps; +                            ypos = 2; +                            continue; +                        } + +                        if (ps < 0 && i == segmentStart + ps) { +                            // done with the reverse sweep +                            ctx.fill(); +                            areaOpen = false; +                            ps = -ps; +                            ypos = 1; +                            i = segmentStart = segmentEnd + ps; +                            continue; +                        } +                    } + +                    if (x1 == null || x2 == null) +                        continue; + +                    // clip x values +                     +                    // clip with xmin +                    if (x1 <= x2 && x1 < axisx.min) { +                        if (x2 < axisx.min) +                            continue; +                        y1 = (axisx.min - x1) / (x2 - x1) * (y2 - y1) + y1; +                        x1 = axisx.min; +                    } +                    else if (x2 <= x1 && x2 < axisx.min) { +                        if (x1 < axisx.min) +                            continue; +                        y2 = (axisx.min - x1) / (x2 - x1) * (y2 - y1) + y1; +                        x2 = axisx.min; +                    } + +                    // clip with xmax +                    if (x1 >= x2 && x1 > axisx.max) { +                        if (x2 > axisx.max) +                            continue; +                        y1 = (axisx.max - x1) / (x2 - x1) * (y2 - y1) + y1; +                        x1 = axisx.max; +                    } +                    else if (x2 >= x1 && x2 > axisx.max) { +                        if (x1 > axisx.max) +                            continue; +                        y2 = (axisx.max - x1) / (x2 - x1) * (y2 - y1) + y1; +                        x2 = axisx.max; +                    } + +                    if (!areaOpen) { +                        // open area +                        ctx.beginPath(); +                        ctx.moveTo(axisx.p2c(x1), axisy.p2c(bottom)); +                        areaOpen = true; +                    } +                     +                    // now first check the case where both is outside +                    if (y1 >= axisy.max && y2 >= axisy.max) { +                        ctx.lineTo(axisx.p2c(x1), axisy.p2c(axisy.max)); +                        ctx.lineTo(axisx.p2c(x2), axisy.p2c(axisy.max)); +                        continue; +                    } +                    else if (y1 <= axisy.min && y2 <= axisy.min) { +                        ctx.lineTo(axisx.p2c(x1), axisy.p2c(axisy.min)); +                        ctx.lineTo(axisx.p2c(x2), axisy.p2c(axisy.min)); +                        continue; +                    } +                     +                    // else it's a bit more complicated, there might +                    // be a flat maxed out rectangle first, then a +                    // triangular cutout or reverse; to find these +                    // keep track of the current x values +                    var x1old = x1, x2old = x2; + +                    // clip the y values, without shortcutting, we +                    // go through all cases in turn +                     +                    // clip with ymin +                    if (y1 <= y2 && y1 < axisy.min && y2 >= axisy.min) { +                        x1 = (axisy.min - y1) / (y2 - y1) * (x2 - x1) + x1; +                        y1 = axisy.min; +                    } +                    else if (y2 <= y1 && y2 < axisy.min && y1 >= axisy.min) { +                        x2 = (axisy.min - y1) / (y2 - y1) * (x2 - x1) + x1; +                        y2 = axisy.min; +                    } + +                    // clip with ymax +                    if (y1 >= y2 && y1 > axisy.max && y2 <= axisy.max) { +                        x1 = (axisy.max - y1) / (y2 - y1) * (x2 - x1) + x1; +                        y1 = axisy.max; +                    } +                    else if (y2 >= y1 && y2 > axisy.max && y1 <= axisy.max) { +                        x2 = (axisy.max - y1) / (y2 - y1) * (x2 - x1) + x1; +                        y2 = axisy.max; +                    } + +                    // if the x value was changed we got a rectangle +                    // to fill +                    if (x1 != x1old) { +                        ctx.lineTo(axisx.p2c(x1old), axisy.p2c(y1)); +                        // it goes to (x1, y1), but we fill that below +                    } +                     +                    // fill triangular section, this sometimes result +                    // in redundant points if (x1, y1) hasn't changed +                    // from previous line to, but we just ignore that +                    ctx.lineTo(axisx.p2c(x1), axisy.p2c(y1)); +                    ctx.lineTo(axisx.p2c(x2), axisy.p2c(y2)); + +                    // fill the other rectangle if it's there +                    if (x2 != x2old) { +                        ctx.lineTo(axisx.p2c(x2), axisy.p2c(y2)); +                        ctx.lineTo(axisx.p2c(x2old), axisy.p2c(y2)); +                    } +                } +            } + +            ctx.save(); +            ctx.translate(plotOffset.left, plotOffset.top); +            ctx.lineJoin = "round"; + +            var lw = series.lines.lineWidth, +                sw = series.shadowSize; +            // FIXME: consider another form of shadow when filling is turned on +            if (lw > 0 && sw > 0) { +                // draw shadow as a thick and thin line with transparency +                ctx.lineWidth = sw; +                ctx.strokeStyle = "rgba(0,0,0,0.1)"; +                // position shadow at angle from the mid of line +                var angle = Math.PI/18; +                plotLine(series.datapoints, Math.sin(angle) * (lw/2 + sw/2), Math.cos(angle) * (lw/2 + sw/2), series.xaxis, series.yaxis); +                ctx.lineWidth = sw/2; +                plotLine(series.datapoints, Math.sin(angle) * (lw/2 + sw/4), Math.cos(angle) * (lw/2 + sw/4), series.xaxis, series.yaxis); +            } + +            ctx.lineWidth = lw; +            ctx.strokeStyle = series.color; +            var fillStyle = getFillStyle(series.lines, series.color, 0, plotHeight); +            if (fillStyle) { +                ctx.fillStyle = fillStyle; +                plotLineArea(series.datapoints, series.xaxis, series.yaxis); +            } + +            if (lw > 0) +                plotLine(series.datapoints, 0, 0, series.xaxis, series.yaxis); +            ctx.restore(); +        } + +        function drawSeriesPoints(series) { +            function plotPoints(datapoints, radius, fillStyle, offset, shadow, axisx, axisy, symbol) { +                var points = datapoints.points, ps = datapoints.pointsize; + +                for (var i = 0; i < points.length; i += ps) { +                    var x = points[i], y = points[i + 1]; +                    if (x == null || x < axisx.min || x > axisx.max || y < axisy.min || y > axisy.max) +                        continue; +                     +                    ctx.beginPath(); +                    x = axisx.p2c(x); +                    y = axisy.p2c(y) + offset; +                    if (symbol == "circle") +                        ctx.arc(x, y, radius, 0, shadow ? Math.PI : Math.PI * 2, false); +                    else +                        symbol(ctx, x, y, radius, shadow); +                    ctx.closePath(); +                     +                    if (fillStyle) { +                        ctx.fillStyle = fillStyle; +                        ctx.fill(); +                    } +                    ctx.stroke(); +                } +            } +             +            ctx.save(); +            ctx.translate(plotOffset.left, plotOffset.top); + +            var lw = series.points.lineWidth, +                sw = series.shadowSize, +                radius = series.points.radius, +                symbol = series.points.symbol; +            if (lw > 0 && sw > 0) { +                // draw shadow in two steps +                var w = sw / 2; +                ctx.lineWidth = w; +                ctx.strokeStyle = "rgba(0,0,0,0.1)"; +                plotPoints(series.datapoints, radius, null, w + w/2, true, +                           series.xaxis, series.yaxis, symbol); + +                ctx.strokeStyle = "rgba(0,0,0,0.2)"; +                plotPoints(series.datapoints, radius, null, w/2, true, +                           series.xaxis, series.yaxis, symbol); +            } + +            ctx.lineWidth = lw; +            ctx.strokeStyle = series.color; +            plotPoints(series.datapoints, radius, +                       getFillStyle(series.points, series.color), 0, false, +                       series.xaxis, series.yaxis, symbol); +            ctx.restore(); +        } + +        function drawBar(x, y, b, barLeft, barRight, offset, fillStyleCallback, axisx, axisy, c, horizontal, lineWidth) { +            var left, right, bottom, top, +                drawLeft, drawRight, drawTop, drawBottom, +                tmp; + +            // in horizontal mode, we start the bar from the left +            // instead of from the bottom so it appears to be +            // horizontal rather than vertical +            if (horizontal) { +                drawBottom = drawRight = drawTop = true; +                drawLeft = false; +                left = b; +                right = x; +                top = y + barLeft; +                bottom = y + barRight; + +                // account for negative bars +                if (right < left) { +                    tmp = right; +                    right = left; +                    left = tmp; +                    drawLeft = true; +                    drawRight = false; +                } +            } +            else { +                drawLeft = drawRight = drawTop = true; +                drawBottom = false; +                left = x + barLeft; +                right = x + barRight; +                bottom = b; +                top = y; + +                // account for negative bars +                if (top < bottom) { +                    tmp = top; +                    top = bottom; +                    bottom = tmp; +                    drawBottom = true; +                    drawTop = false; +                } +            } +            +            // clip +            if (right < axisx.min || left > axisx.max || +                top < axisy.min || bottom > axisy.max) +                return; +             +            if (left < axisx.min) { +                left = axisx.min; +                drawLeft = false; +            } + +            if (right > axisx.max) { +                right = axisx.max; +                drawRight = false; +            } + +            if (bottom < axisy.min) { +                bottom = axisy.min; +                drawBottom = false; +            } +             +            if (top > axisy.max) { +                top = axisy.max; +                drawTop = false; +            } + +            left = axisx.p2c(left); +            bottom = axisy.p2c(bottom); +            right = axisx.p2c(right); +            top = axisy.p2c(top); +             +            // fill the bar +            if (fillStyleCallback) { +                c.beginPath(); +                c.moveTo(left, bottom); +                c.lineTo(left, top); +                c.lineTo(right, top); +                c.lineTo(right, bottom); +                c.fillStyle = fillStyleCallback(bottom, top); +                c.fill(); +            } + +            // draw outline +            if (lineWidth > 0 && (drawLeft || drawRight || drawTop || drawBottom)) { +                c.beginPath(); + +                // FIXME: inline moveTo is buggy with excanvas +                c.moveTo(left, bottom + offset); +                if (drawLeft) +                    c.lineTo(left, top + offset); +                else +                    c.moveTo(left, top + offset); +                if (drawTop) +                    c.lineTo(right, top + offset); +                else +                    c.moveTo(right, top + offset); +                if (drawRight) +                    c.lineTo(right, bottom + offset); +                else +                    c.moveTo(right, bottom + offset); +                if (drawBottom) +                    c.lineTo(left, bottom + offset); +                else +                    c.moveTo(left, bottom + offset); +                c.stroke(); +            } +        } +         +        function drawSeriesBars(series) { +            function plotBars(datapoints, barLeft, barRight, offset, fillStyleCallback, axisx, axisy) { +                var points = datapoints.points, ps = datapoints.pointsize; +                 +                for (var i = 0; i < points.length; i += ps) { +                    if (points[i] == null) +                        continue; +                    drawBar(points[i], points[i + 1], points[i + 2], barLeft, barRight, offset, fillStyleCallback, axisx, axisy, ctx, series.bars.horizontal, series.bars.lineWidth); +                } +            } + +            ctx.save(); +            ctx.translate(plotOffset.left, plotOffset.top); + +            // FIXME: figure out a way to add shadows (for instance along the right edge) +            ctx.lineWidth = series.bars.lineWidth; +            ctx.strokeStyle = series.color; +            var barLeft = series.bars.align == "left" ? 0 : -series.bars.barWidth/2; +            var fillStyleCallback = series.bars.fill ? function (bottom, top) { return getFillStyle(series.bars, series.color, bottom, top); } : null; +            plotBars(series.datapoints, barLeft, barLeft + series.bars.barWidth, 0, fillStyleCallback, series.xaxis, series.yaxis); +            ctx.restore(); +        } + +        function getFillStyle(filloptions, seriesColor, bottom, top) { +            var fill = filloptions.fill; +            if (!fill) +                return null; + +            if (filloptions.fillColor) +                return getColorOrGradient(filloptions.fillColor, bottom, top, seriesColor); +             +            var c = $.color.parse(seriesColor); +            c.a = typeof fill == "number" ? fill : 0.4; +            c.normalize(); +            return c.toString(); +        } +         +        function insertLegend() { +            placeholder.find(".legend").remove(); + +            if (!options.legend.show) +                return; +             +            var fragments = [], rowStarted = false, +                lf = options.legend.labelFormatter, s, label; +            for (var i = 0; i < series.length; ++i) { +                s = series[i]; +                label = s.label; +                if (!label) +                    continue; +                 +                if (i % options.legend.noColumns == 0) { +                    if (rowStarted) +                        fragments.push('</tr>'); +                    fragments.push('<tr>'); +                    rowStarted = true; +                } + +                if (lf) +                    label = lf(label, s); +                 +                fragments.push( +                    '<td class="legendColorBox"><div style="border:1px solid ' + options.legend.labelBoxBorderColor + ';padding:1px"><div style="width:4px;height:0;border:5px solid ' + s.color + ';overflow:hidden"></div></div></td>' + +                    '<td class="legendLabel">' + label + '</td>'); +            } +            if (rowStarted) +                fragments.push('</tr>'); +             +            if (fragments.length == 0) +                return; + +            var table = '<table style="font-size:smaller;color:' + options.grid.color + '">' + fragments.join("") + '</table>'; +            if (options.legend.container != null) +                $(options.legend.container).html(table); +            else { +                var pos = "", +                    p = options.legend.position, +                    m = options.legend.margin; +                if (m[0] == null) +                    m = [m, m]; +                if (p.charAt(0) == "n") +                    pos += 'top:' + (m[1] + plotOffset.top) + 'px;'; +                else if (p.charAt(0) == "s") +                    pos += 'bottom:' + (m[1] + plotOffset.bottom) + 'px;'; +                if (p.charAt(1) == "e") +                    pos += 'right:' + (m[0] + plotOffset.right) + 'px;'; +                else if (p.charAt(1) == "w") +                    pos += 'left:' + (m[0] + plotOffset.left) + 'px;'; +                var legend = $('<div class="legend">' + table.replace('style="', 'style="position:absolute;' + pos +';') + '</div>').appendTo(placeholder); +                if (options.legend.backgroundOpacity != 0.0) { +                    // put in the transparent background +                    // separately to avoid blended labels and +                    // label boxes +                    var c = options.legend.backgroundColor; +                    if (c == null) { +                        c = options.grid.backgroundColor; +                        if (c && typeof c == "string") +                            c = $.color.parse(c); +                        else +                            c = $.color.extract(legend, 'background-color'); +                        c.a = 1; +                        c = c.toString(); +                    } +                    var div = legend.children(); +                    $('<div style="position:absolute;width:' + div.width() + 'px;height:' + div.height() + 'px;' + pos +'background-color:' + c + ';"> </div>').prependTo(legend).css('opacity', options.legend.backgroundOpacity); +                } +            } +        } + + +        // interactive features +         +        var highlights = [], +            redrawTimeout = null; +         +        // returns the data item the mouse is over, or null if none is found +        function findNearbyItem(mouseX, mouseY, seriesFilter) { +            var maxDistance = options.grid.mouseActiveRadius, +                smallestDistance = maxDistance * maxDistance + 1, +                item = null, foundPoint = false, i, j; + +            for (i = series.length - 1; i >= 0; --i) { +                if (!seriesFilter(series[i])) +                    continue; +                 +                var s = series[i], +                    axisx = s.xaxis, +                    axisy = s.yaxis, +                    points = s.datapoints.points, +                    ps = s.datapoints.pointsize, +                    mx = axisx.c2p(mouseX), // precompute some stuff to make the loop faster +                    my = axisy.c2p(mouseY), +                    maxx = maxDistance / axisx.scale, +                    maxy = maxDistance / axisy.scale; + +                // with inverse transforms, we can't use the maxx/maxy +                // optimization, sadly +                if (axisx.options.inverseTransform) +                    maxx = Number.MAX_VALUE; +                if (axisy.options.inverseTransform) +                    maxy = Number.MAX_VALUE; +                 +                if (s.lines.show || s.points.show) { +                    for (j = 0; j < points.length; j += ps) { +                        var x = points[j], y = points[j + 1]; +                        if (x == null) +                            continue; +                         +                        // For points and lines, the cursor must be within a +                        // certain distance to the data point +                        if (x - mx > maxx || x - mx < -maxx || +                            y - my > maxy || y - my < -maxy) +                            continue; + +                        // We have to calculate distances in pixels, not in +                        // data units, because the scales of the axes may be different +                        var dx = Math.abs(axisx.p2c(x) - mouseX), +                            dy = Math.abs(axisy.p2c(y) - mouseY), +                            dist = dx * dx + dy * dy; // we save the sqrt + +                        // use <= to ensure last point takes precedence +                        // (last generally means on top of) +                        if (dist < smallestDistance) { +                            smallestDistance = dist; +                            item = [i, j / ps]; +                        } +                    } +                } +                     +                if (s.bars.show && !item) { // no other point can be nearby +                    var barLeft = s.bars.align == "left" ? 0 : -s.bars.barWidth/2, +                        barRight = barLeft + s.bars.barWidth; +                     +                    for (j = 0; j < points.length; j += ps) { +                        var x = points[j], y = points[j + 1], b = points[j + 2]; +                        if (x == null) +                            continue; +   +                        // for a bar graph, the cursor must be inside the bar +                        if (series[i].bars.horizontal ?  +                            (mx <= Math.max(b, x) && mx >= Math.min(b, x) &&  +                             my >= y + barLeft && my <= y + barRight) : +                            (mx >= x + barLeft && mx <= x + barRight && +                             my >= Math.min(b, y) && my <= Math.max(b, y))) +                                item = [i, j / ps]; +                    } +                } +            } + +            if (item) { +                i = item[0]; +                j = item[1]; +                ps = series[i].datapoints.pointsize; +                 +                return { datapoint: series[i].datapoints.points.slice(j * ps, (j + 1) * ps), +                         dataIndex: j, +                         series: series[i], +                         seriesIndex: i }; +            } +             +            return null; +        } + +        function onMouseMove(e) { +            if (options.grid.hoverable) +                triggerClickHoverEvent("plothover", e, +                                       function (s) { return s["hoverable"] != false; }); +        } + +        function onMouseLeave(e) { +            if (options.grid.hoverable) +                triggerClickHoverEvent("plothover", e, +                                       function (s) { return false; }); +        } + +        function onClick(e) { +            triggerClickHoverEvent("plotclick", e, +                                   function (s) { return s["clickable"] != false; }); +        } + +        // trigger click or hover event (they send the same parameters +        // so we share their code) +        function triggerClickHoverEvent(eventname, event, seriesFilter) { +            var offset = eventHolder.offset(), +                canvasX = event.pageX - offset.left - plotOffset.left, +                canvasY = event.pageY - offset.top - plotOffset.top, +            pos = canvasToAxisCoords({ left: canvasX, top: canvasY }); + +            pos.pageX = event.pageX; +            pos.pageY = event.pageY; + +            var item = findNearbyItem(canvasX, canvasY, seriesFilter); + +            if (item) { +                // fill in mouse pos for any listeners out there +                item.pageX = parseInt(item.series.xaxis.p2c(item.datapoint[0]) + offset.left + plotOffset.left); +                item.pageY = parseInt(item.series.yaxis.p2c(item.datapoint[1]) + offset.top + plotOffset.top); +            } + +            if (options.grid.autoHighlight) { +                // clear auto-highlights +                for (var i = 0; i < highlights.length; ++i) { +                    var h = highlights[i]; +                    if (h.auto == eventname && +                        !(item && h.series == item.series && +                          h.point[0] == item.datapoint[0] && +                          h.point[1] == item.datapoint[1])) +                        unhighlight(h.series, h.point); +                } +                 +                if (item) +                    highlight(item.series, item.datapoint, eventname); +            } +             +            placeholder.trigger(eventname, [ pos, item ]); +        } + +        function triggerRedrawOverlay() { +            if (!redrawTimeout) +                redrawTimeout = setTimeout(drawOverlay, 30); +        } + +        function drawOverlay() { +            redrawTimeout = null; + +            // draw highlights +            octx.save(); +            octx.clearRect(0, 0, canvasWidth, canvasHeight); +            octx.translate(plotOffset.left, plotOffset.top); +             +            var i, hi; +            for (i = 0; i < highlights.length; ++i) { +                hi = highlights[i]; + +                if (hi.series.bars.show) +                    drawBarHighlight(hi.series, hi.point); +                else +                    drawPointHighlight(hi.series, hi.point); +            } +            octx.restore(); +             +            executeHooks(hooks.drawOverlay, [octx]); +        } +         +        function highlight(s, point, auto) { +            if (typeof s == "number") +                s = series[s]; + +            if (typeof point == "number") { +                var ps = s.datapoints.pointsize; +                point = s.datapoints.points.slice(ps * point, ps * (point + 1)); +            } + +            var i = indexOfHighlight(s, point); +            if (i == -1) { +                highlights.push({ series: s, point: point, auto: auto }); + +                triggerRedrawOverlay(); +            } +            else if (!auto) +                highlights[i].auto = false; +        } +             +        function unhighlight(s, point) { +            if (s == null && point == null) { +                highlights = []; +                triggerRedrawOverlay(); +            } +             +            if (typeof s == "number") +                s = series[s]; + +            if (typeof point == "number") +                point = s.data[point]; + +            var i = indexOfHighlight(s, point); +            if (i != -1) { +                highlights.splice(i, 1); + +                triggerRedrawOverlay(); +            } +        } +         +        function indexOfHighlight(s, p) { +            for (var i = 0; i < highlights.length; ++i) { +                var h = highlights[i]; +                if (h.series == s && h.point[0] == p[0] +                    && h.point[1] == p[1]) +                    return i; +            } +            return -1; +        } +         +        function drawPointHighlight(series, point) { +            var x = point[0], y = point[1], +                axisx = series.xaxis, axisy = series.yaxis; +             +            if (x < axisx.min || x > axisx.max || y < axisy.min || y > axisy.max) +                return; +             +            var pointRadius = series.points.radius + series.points.lineWidth / 2; +            octx.lineWidth = pointRadius; +            octx.strokeStyle = $.color.parse(series.color).scale('a', 0.5).toString(); +            var radius = 1.5 * pointRadius, +                x = axisx.p2c(x), +                y = axisy.p2c(y); +             +            octx.beginPath(); +            if (series.points.symbol == "circle") +                octx.arc(x, y, radius, 0, 2 * Math.PI, false); +            else +                series.points.symbol(octx, x, y, radius, false); +            octx.closePath(); +            octx.stroke(); +        } + +        function drawBarHighlight(series, point) { +            octx.lineWidth = series.bars.lineWidth; +            octx.strokeStyle = $.color.parse(series.color).scale('a', 0.5).toString(); +            var fillStyle = $.color.parse(series.color).scale('a', 0.5).toString(); +            var barLeft = series.bars.align == "left" ? 0 : -series.bars.barWidth/2; +            drawBar(point[0], point[1], point[2] || 0, barLeft, barLeft + series.bars.barWidth, +                    0, function () { return fillStyle; }, series.xaxis, series.yaxis, octx, series.bars.horizontal, series.bars.lineWidth); +        } + +        function getColorOrGradient(spec, bottom, top, defaultColor) { +            if (typeof spec == "string") +                return spec; +            else { +                // assume this is a gradient spec; IE currently only +                // supports a simple vertical gradient properly, so that's +                // what we support too +                var gradient = ctx.createLinearGradient(0, top, 0, bottom); +                 +                for (var i = 0, l = spec.colors.length; i < l; ++i) { +                    var c = spec.colors[i]; +                    if (typeof c != "string") { +                        var co = $.color.parse(defaultColor); +                        if (c.brightness != null) +                            co = co.scale('rgb', c.brightness) +                        if (c.opacity != null) +                            co.a *= c.opacity; +                        c = co.toString(); +                    } +                    gradient.addColorStop(i / (l - 1), c); +                } +                 +                return gradient; +            } +        } +    } + +    $.plot = function(placeholder, data, options) { +        //var t0 = new Date(); +        var plot = new Plot($(placeholder), data, options, $.plot.plugins); +        //(window.console ? console.log : alert)("time used (msecs): " + ((new Date()).getTime() - t0.getTime())); +        return plot; +    }; + +    $.plot.version = "0.7"; +     +    $.plot.plugins = []; + +    // returns a string with the date d formatted according to fmt +    $.plot.formatDate = function(d, fmt, monthNames) { +        var leftPad = function(n) { +            n = "" + n; +            return n.length == 1 ? "0" + n : n; +        }; +         +        var r = []; +        var escape = false, padNext = false; +        var hours = d.getUTCHours(); +        var isAM = hours < 12; +        if (monthNames == null) +            monthNames = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]; + +        if (fmt.search(/%p|%P/) != -1) { +            if (hours > 12) { +                hours = hours - 12; +            } else if (hours == 0) { +                hours = 12; +            } +        } +        for (var i = 0; i < fmt.length; ++i) { +            var c = fmt.charAt(i); +             +            if (escape) { +                switch (c) { +                case 'h': c = "" + hours; break; +                case 'H': c = leftPad(hours); break; +                case 'M': c = leftPad(d.getUTCMinutes()); break; +                case 'S': c = leftPad(d.getUTCSeconds()); break; +                case 'd': c = "" + d.getUTCDate(); break; +                case 'm': c = "" + (d.getUTCMonth() + 1); break; +                case 'y': c = "" + d.getUTCFullYear(); break; +                case 'b': c = "" + monthNames[d.getUTCMonth()]; break; +                case 'p': c = (isAM) ? ("" + "am") : ("" + "pm"); break; +                case 'P': c = (isAM) ? ("" + "AM") : ("" + "PM"); break; +                case '0': c = ""; padNext = true; break; +                } +                if (c && padNext) { +                    c = leftPad(c); +                    padNext = false; +                } +                r.push(c); +                if (!padNext) +                    escape = false; +            } +            else { +                if (c == "%") +                    escape = true; +                else +                    r.push(c); +            } +        } +        return r.join(""); +    }; +     +    // round to nearby lower multiple of base +    function floorInBase(n, base) { +        return base * Math.floor(n / base); +    } +     +})(jQuery); diff --git a/module/web/static/js/libs/jquery.flot.min.js b/module/web/static/js/libs/jquery.flot.min.js deleted file mode 100644 index 4467fc5d8..000000000 --- a/module/web/static/js/libs/jquery.flot.min.js +++ /dev/null @@ -1,6 +0,0 @@ -/* Javascript plotting library for jQuery, v. 0.7. - * - * Released under the MIT license by IOLA, December 2007. - * - */ -(function(b){b.color={};b.color.make=function(d,e,g,f){var c={};c.r=d||0;c.g=e||0;c.b=g||0;c.a=f!=null?f:1;c.add=function(h,j){for(var k=0;k<h.length;++k){c[h.charAt(k)]+=j}return c.normalize()};c.scale=function(h,j){for(var k=0;k<h.length;++k){c[h.charAt(k)]*=j}return c.normalize()};c.toString=function(){if(c.a>=1){return"rgb("+[c.r,c.g,c.b].join(",")+")"}else{return"rgba("+[c.r,c.g,c.b,c.a].join(",")+")"}};c.normalize=function(){function h(k,j,l){return j<k?k:(j>l?l:j)}c.r=h(0,parseInt(c.r),255);c.g=h(0,parseInt(c.g),255);c.b=h(0,parseInt(c.b),255);c.a=h(0,c.a,1);return c};c.clone=function(){return b.color.make(c.r,c.b,c.g,c.a)};return c.normalize()};b.color.extract=function(d,e){var c;do{c=d.css(e).toLowerCase();if(c!=""&&c!="transparent"){break}d=d.parent()}while(!b.nodeName(d.get(0),"body"));if(c=="rgba(0, 0, 0, 0)"){c="transparent"}return b.color.parse(c)};b.color.parse=function(c){var d,f=b.color.make;if(d=/rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(c)){return f(parseInt(d[1],10),parseInt(d[2],10),parseInt(d[3],10))}if(d=/rgba\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]+(?:\.[0-9]+)?)\s*\)/.exec(c)){return f(parseInt(d[1],10),parseInt(d[2],10),parseInt(d[3],10),parseFloat(d[4]))}if(d=/rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(c)){return f(parseFloat(d[1])*2.55,parseFloat(d[2])*2.55,parseFloat(d[3])*2.55)}if(d=/rgba\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\s*\)/.exec(c)){return f(parseFloat(d[1])*2.55,parseFloat(d[2])*2.55,parseFloat(d[3])*2.55,parseFloat(d[4]))}if(d=/#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(c)){return f(parseInt(d[1],16),parseInt(d[2],16),parseInt(d[3],16))}if(d=/#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(c)){return f(parseInt(d[1]+d[1],16),parseInt(d[2]+d[2],16),parseInt(d[3]+d[3],16))}var e=b.trim(c).toLowerCase();if(e=="transparent"){return f(255,255,255,0)}else{d=a[e]||[0,0,0];return f(d[0],d[1],d[2])}};var a={aqua:[0,255,255],azure:[240,255,255],beige:[245,245,220],black:[0,0,0],blue:[0,0,255],brown:[165,42,42],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgrey:[169,169,169],darkgreen:[0,100,0],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkviolet:[148,0,211],fuchsia:[255,0,255],gold:[255,215,0],green:[0,128,0],indigo:[75,0,130],khaki:[240,230,140],lightblue:[173,216,230],lightcyan:[224,255,255],lightgreen:[144,238,144],lightgrey:[211,211,211],lightpink:[255,182,193],lightyellow:[255,255,224],lime:[0,255,0],magenta:[255,0,255],maroon:[128,0,0],navy:[0,0,128],olive:[128,128,0],orange:[255,165,0],pink:[255,192,203],purple:[128,0,128],violet:[128,0,128],red:[255,0,0],silver:[192,192,192],white:[255,255,255],yellow:[255,255,0]}})(jQuery);(function(c){function b(av,ai,J,af){var Q=[],O={colors:["#edc240","#afd8f8","#cb4b4b","#4da74d","#9440ed"],legend:{show:true,noColumns:1,labelFormatter:null,labelBoxBorderColor:"#ccc",container:null,position:"ne",margin:5,backgroundColor:null,backgroundOpacity:0.85},xaxis:{show:null,position:"bottom",mode:null,color:null,tickColor:null,transform:null,inverseTransform:null,min:null,max:null,autoscaleMargin:null,ticks:null,tickFormatter:null,labelWidth:null,labelHeight:null,reserveSpace:null,tickLength:null,alignTicksWithAxis:null,tickDecimals:null,tickSize:null,minTickSize:null,monthNames:null,timeformat:null,twelveHourClock:false},yaxis:{autoscaleMargin:0.02,position:"left"},xaxes:[],yaxes:[],series:{points:{show:false,radius:3,lineWidth:2,fill:true,fillColor:"#ffffff",symbol:"circle"},lines:{lineWidth:2,fill:false,fillColor:null,steps:false},bars:{show:false,lineWidth:2,barWidth:1,fill:true,fillColor:null,align:"left",horizontal:false},shadowSize:3},grid:{show:true,aboveData:false,color:"#545454",backgroundColor:null,borderColor:null,tickColor:null,labelMargin:5,axisMargin:8,borderWidth:2,minBorderMargin:null,markings:null,markingsColor:"#f4f4f4",markingsLineWidth:2,clickable:false,hoverable:false,autoHighlight:true,mouseActiveRadius:10},hooks:{}},az=null,ad=null,y=null,H=null,A=null,p=[],aw=[],q={left:0,right:0,top:0,bottom:0},G=0,I=0,h=0,w=0,ak={processOptions:[],processRawData:[],processDatapoints:[],drawSeries:[],draw:[],bindEvents:[],drawOverlay:[],shutdown:[]},aq=this;aq.setData=aj;aq.setupGrid=t;aq.draw=W;aq.getPlaceholder=function(){return av};aq.getCanvas=function(){return az};aq.getPlotOffset=function(){return q};aq.width=function(){return h};aq.height=function(){return w};aq.offset=function(){var aB=y.offset();aB.left+=q.left;aB.top+=q.top;return aB};aq.getData=function(){return Q};aq.getAxes=function(){var aC={},aB;c.each(p.concat(aw),function(aD,aE){if(aE){aC[aE.direction+(aE.n!=1?aE.n:"")+"axis"]=aE}});return aC};aq.getXAxes=function(){return p};aq.getYAxes=function(){return aw};aq.c2p=C;aq.p2c=ar;aq.getOptions=function(){return O};aq.highlight=x;aq.unhighlight=T;aq.triggerRedrawOverlay=f;aq.pointOffset=function(aB){return{left:parseInt(p[aA(aB,"x")-1].p2c(+aB.x)+q.left),top:parseInt(aw[aA(aB,"y")-1].p2c(+aB.y)+q.top)}};aq.shutdown=ag;aq.resize=function(){B();g(az);g(ad)};aq.hooks=ak;F(aq);Z(J);X();aj(ai);t();W();ah();function an(aD,aB){aB=[aq].concat(aB);for(var aC=0;aC<aD.length;++aC){aD[aC].apply(this,aB)}}function F(){for(var aB=0;aB<af.length;++aB){var aC=af[aB];aC.init(aq);if(aC.options){c.extend(true,O,aC.options)}}}function Z(aC){var aB;c.extend(true,O,aC);if(O.xaxis.color==null){O.xaxis.color=O.grid.color}if(O.yaxis.color==null){O.yaxis.color=O.grid.color}if(O.xaxis.tickColor==null){O.xaxis.tickColor=O.grid.tickColor}if(O.yaxis.tickColor==null){O.yaxis.tickColor=O.grid.tickColor}if(O.grid.borderColor==null){O.grid.borderColor=O.grid.color}if(O.grid.tickColor==null){O.grid.tickColor=c.color.parse(O.grid.color).scale("a",0.22).toString()}for(aB=0;aB<Math.max(1,O.xaxes.length);++aB){O.xaxes[aB]=c.extend(true,{},O.xaxis,O.xaxes[aB])}for(aB=0;aB<Math.max(1,O.yaxes.length);++aB){O.yaxes[aB]=c.extend(true,{},O.yaxis,O.yaxes[aB])}if(O.xaxis.noTicks&&O.xaxis.ticks==null){O.xaxis.ticks=O.xaxis.noTicks}if(O.yaxis.noTicks&&O.yaxis.ticks==null){O.yaxis.ticks=O.yaxis.noTicks}if(O.x2axis){O.xaxes[1]=c.extend(true,{},O.xaxis,O.x2axis);O.xaxes[1].position="top"}if(O.y2axis){O.yaxes[1]=c.extend(true,{},O.yaxis,O.y2axis);O.yaxes[1].position="right"}if(O.grid.coloredAreas){O.grid.markings=O.grid.coloredAreas}if(O.grid.coloredAreasColor){O.grid.markingsColor=O.grid.coloredAreasColor}if(O.lines){c.extend(true,O.series.lines,O.lines)}if(O.points){c.extend(true,O.series.points,O.points)}if(O.bars){c.extend(true,O.series.bars,O.bars)}if(O.shadowSize!=null){O.series.shadowSize=O.shadowSize}for(aB=0;aB<O.xaxes.length;++aB){V(p,aB+1).options=O.xaxes[aB]}for(aB=0;aB<O.yaxes.length;++aB){V(aw,aB+1).options=O.yaxes[aB]}for(var aD in ak){if(O.hooks[aD]&&O.hooks[aD].length){ak[aD]=ak[aD].concat(O.hooks[aD])}}an(ak.processOptions,[O])}function aj(aB){Q=Y(aB);ax();z()}function Y(aE){var aC=[];for(var aB=0;aB<aE.length;++aB){var aD=c.extend(true,{},O.series);if(aE[aB].data!=null){aD.data=aE[aB].data;delete aE[aB].data;c.extend(true,aD,aE[aB]);aE[aB].data=aD.data}else{aD.data=aE[aB]}aC.push(aD)}return aC}function aA(aC,aD){var aB=aC[aD+"axis"];if(typeof aB=="object"){aB=aB.n}if(typeof aB!="number"){aB=1}return aB}function m(){return c.grep(p.concat(aw),function(aB){return aB})}function C(aE){var aC={},aB,aD;for(aB=0;aB<p.length;++aB){aD=p[aB];if(aD&&aD.used){aC["x"+aD.n]=aD.c2p(aE.left)}}for(aB=0;aB<aw.length;++aB){aD=aw[aB];if(aD&&aD.used){aC["y"+aD.n]=aD.c2p(aE.top)}}if(aC.x1!==undefined){aC.x=aC.x1}if(aC.y1!==undefined){aC.y=aC.y1}return aC}function ar(aF){var aD={},aC,aE,aB;for(aC=0;aC<p.length;++aC){aE=p[aC];if(aE&&aE.used){aB="x"+aE.n;if(aF[aB]==null&&aE.n==1){aB="x"}if(aF[aB]!=null){aD.left=aE.p2c(aF[aB]);break}}}for(aC=0;aC<aw.length;++aC){aE=aw[aC];if(aE&&aE.used){aB="y"+aE.n;if(aF[aB]==null&&aE.n==1){aB="y"}if(aF[aB]!=null){aD.top=aE.p2c(aF[aB]);break}}}return aD}function V(aC,aB){if(!aC[aB-1]){aC[aB-1]={n:aB,direction:aC==p?"x":"y",options:c.extend(true,{},aC==p?O.xaxis:O.yaxis)}}return aC[aB-1]}function ax(){var aG;var aM=Q.length,aB=[],aE=[];for(aG=0;aG<Q.length;++aG){var aJ=Q[aG].color;if(aJ!=null){--aM;if(typeof aJ=="number"){aE.push(aJ)}else{aB.push(c.color.parse(Q[aG].color))}}}for(aG=0;aG<aE.length;++aG){aM=Math.max(aM,aE[aG]+1)}var aC=[],aF=0;aG=0;while(aC.length<aM){var aI;if(O.colors.length==aG){aI=c.color.make(100,100,100)}else{aI=c.color.parse(O.colors[aG])}var aD=aF%2==1?-1:1;aI.scale("rgb",1+aD*Math.ceil(aF/2)*0.2);aC.push(aI);++aG;if(aG>=O.colors.length){aG=0;++aF}}var aH=0,aN;for(aG=0;aG<Q.length;++aG){aN=Q[aG];if(aN.color==null){aN.color=aC[aH].toString();++aH}else{if(typeof aN.color=="number"){aN.color=aC[aN.color].toString()}}if(aN.lines.show==null){var aL,aK=true;for(aL in aN){if(aN[aL]&&aN[aL].show){aK=false;break}}if(aK){aN.lines.show=true}}aN.xaxis=V(p,aA(aN,"x"));aN.yaxis=V(aw,aA(aN,"y"))}}function z(){var aO=Number.POSITIVE_INFINITY,aI=Number.NEGATIVE_INFINITY,aB=Number.MAX_VALUE,aU,aS,aR,aN,aD,aJ,aT,aP,aH,aG,aC,a0,aX,aL;function aF(a3,a2,a1){if(a2<a3.datamin&&a2!=-aB){a3.datamin=a2}if(a1>a3.datamax&&a1!=aB){a3.datamax=a1}}c.each(m(),function(a1,a2){a2.datamin=aO;a2.datamax=aI;a2.used=false});for(aU=0;aU<Q.length;++aU){aJ=Q[aU];aJ.datapoints={points:[]};an(ak.processRawData,[aJ,aJ.data,aJ.datapoints])}for(aU=0;aU<Q.length;++aU){aJ=Q[aU];var aZ=aJ.data,aW=aJ.datapoints.format;if(!aW){aW=[];aW.push({x:true,number:true,required:true});aW.push({y:true,number:true,required:true});if(aJ.bars.show||(aJ.lines.show&&aJ.lines.fill)){aW.push({y:true,number:true,required:false,defaultValue:0});if(aJ.bars.horizontal){delete aW[aW.length-1].y;aW[aW.length-1].x=true}}aJ.datapoints.format=aW}if(aJ.datapoints.pointsize!=null){continue}aJ.datapoints.pointsize=aW.length;aP=aJ.datapoints.pointsize;aT=aJ.datapoints.points;insertSteps=aJ.lines.show&&aJ.lines.steps;aJ.xaxis.used=aJ.yaxis.used=true;for(aS=aR=0;aS<aZ.length;++aS,aR+=aP){aL=aZ[aS];var aE=aL==null;if(!aE){for(aN=0;aN<aP;++aN){a0=aL[aN];aX=aW[aN];if(aX){if(aX.number&&a0!=null){a0=+a0;if(isNaN(a0)){a0=null}else{if(a0==Infinity){a0=aB}else{if(a0==-Infinity){a0=-aB}}}}if(a0==null){if(aX.required){aE=true}if(aX.defaultValue!=null){a0=aX.defaultValue}}}aT[aR+aN]=a0}}if(aE){for(aN=0;aN<aP;++aN){a0=aT[aR+aN];if(a0!=null){aX=aW[aN];if(aX.x){aF(aJ.xaxis,a0,a0)}if(aX.y){aF(aJ.yaxis,a0,a0)}}aT[aR+aN]=null}}else{if(insertSteps&&aR>0&&aT[aR-aP]!=null&&aT[aR-aP]!=aT[aR]&&aT[aR-aP+1]!=aT[aR+1]){for(aN=0;aN<aP;++aN){aT[aR+aP+aN]=aT[aR+aN]}aT[aR+1]=aT[aR-aP+1];aR+=aP}}}}for(aU=0;aU<Q.length;++aU){aJ=Q[aU];an(ak.processDatapoints,[aJ,aJ.datapoints])}for(aU=0;aU<Q.length;++aU){aJ=Q[aU];aT=aJ.datapoints.points,aP=aJ.datapoints.pointsize;var aK=aO,aQ=aO,aM=aI,aV=aI;for(aS=0;aS<aT.length;aS+=aP){if(aT[aS]==null){continue}for(aN=0;aN<aP;++aN){a0=aT[aS+aN];aX=aW[aN];if(!aX||a0==aB||a0==-aB){continue}if(aX.x){if(a0<aK){aK=a0}if(a0>aM){aM=a0}}if(aX.y){if(a0<aQ){aQ=a0}if(a0>aV){aV=a0}}}}if(aJ.bars.show){var aY=aJ.bars.align=="left"?0:-aJ.bars.barWidth/2;if(aJ.bars.horizontal){aQ+=aY;aV+=aY+aJ.bars.barWidth}else{aK+=aY;aM+=aY+aJ.bars.barWidth}}aF(aJ.xaxis,aK,aM);aF(aJ.yaxis,aQ,aV)}c.each(m(),function(a1,a2){if(a2.datamin==aO){a2.datamin=null}if(a2.datamax==aI){a2.datamax=null}})}function j(aB,aC){var aD=document.createElement("canvas");aD.className=aC;aD.width=G;aD.height=I;if(!aB){c(aD).css({position:"absolute",left:0,top:0})}c(aD).appendTo(av);if(!aD.getContext){aD=window.G_vmlCanvasManager.initElement(aD)}aD.getContext("2d").save();return aD}function B(){G=av.width();I=av.height();if(G<=0||I<=0){throw"Invalid dimensions for plot, width = "+G+", height = "+I}}function g(aC){if(aC.width!=G){aC.width=G}if(aC.height!=I){aC.height=I}var aB=aC.getContext("2d");aB.restore();aB.save()}function X(){var aC,aB=av.children("canvas.base"),aD=av.children("canvas.overlay");if(aB.length==0||aD==0){av.html("");av.css({padding:0});if(av.css("position")=="static"){av.css("position","relative")}B();az=j(true,"base");ad=j(false,"overlay");aC=false}else{az=aB.get(0);ad=aD.get(0);aC=true}H=az.getContext("2d");A=ad.getContext("2d");y=c([ad,az]);if(aC){av.data("plot").shutdown();aq.resize();A.clearRect(0,0,G,I);y.unbind();av.children().not([az,ad]).remove()}av.data("plot",aq)}function ah(){if(O.grid.hoverable){y.mousemove(aa);y.mouseleave(l)}if(O.grid.clickable){y.click(R)}an(ak.bindEvents,[y])}function ag(){if(M){clearTimeout(M)}y.unbind("mousemove",aa);y.unbind("mouseleave",l);y.unbind("click",R);an(ak.shutdown,[y])}function r(aG){function aC(aH){return aH}var aF,aB,aD=aG.options.transform||aC,aE=aG.options.inverseTransform;if(aG.direction=="x"){aF=aG.scale=h/Math.abs(aD(aG.max)-aD(aG.min));aB=Math.min(aD(aG.max),aD(aG.min))}else{aF=aG.scale=w/Math.abs(aD(aG.max)-aD(aG.min));aF=-aF;aB=Math.max(aD(aG.max),aD(aG.min))}if(aD==aC){aG.p2c=function(aH){return(aH-aB)*aF}}else{aG.p2c=function(aH){return(aD(aH)-aB)*aF}}if(!aE){aG.c2p=function(aH){return aB+aH/aF}}else{aG.c2p=function(aH){return aE(aB+aH/aF)}}}function L(aD){var aB=aD.options,aF,aJ=aD.ticks||[],aI=[],aE,aK=aB.labelWidth,aG=aB.labelHeight,aC;function aH(aM,aL){return c('<div style="position:absolute;top:-10000px;'+aL+'font-size:smaller"><div class="'+aD.direction+"Axis "+aD.direction+aD.n+'Axis">'+aM.join("")+"</div></div>").appendTo(av)}if(aD.direction=="x"){if(aK==null){aK=Math.floor(G/(aJ.length>0?aJ.length:1))}if(aG==null){aI=[];for(aF=0;aF<aJ.length;++aF){aE=aJ[aF].label;if(aE){aI.push('<div class="tickLabel" style="float:left;width:'+aK+'px">'+aE+"</div>")}}if(aI.length>0){aI.push('<div style="clear:left"></div>');aC=aH(aI,"width:10000px;");aG=aC.height();aC.remove()}}}else{if(aK==null||aG==null){for(aF=0;aF<aJ.length;++aF){aE=aJ[aF].label;if(aE){aI.push('<div class="tickLabel">'+aE+"</div>")}}if(aI.length>0){aC=aH(aI,"");if(aK==null){aK=aC.children().width()}if(aG==null){aG=aC.find("div.tickLabel").height()}aC.remove()}}}if(aK==null){aK=0}if(aG==null){aG=0}aD.labelWidth=aK;aD.labelHeight=aG}function au(aD){var aC=aD.labelWidth,aL=aD.labelHeight,aH=aD.options.position,aF=aD.options.tickLength,aG=O.grid.axisMargin,aJ=O.grid.labelMargin,aK=aD.direction=="x"?p:aw,aE;var aB=c.grep(aK,function(aN){return aN&&aN.options.position==aH&&aN.reserveSpace});if(c.inArray(aD,aB)==aB.length-1){aG=0}if(aF==null){aF="full"}var aI=c.grep(aK,function(aN){return aN&&aN.reserveSpace});var aM=c.inArray(aD,aI)==0;if(!aM&&aF=="full"){aF=5}if(!isNaN(+aF)){aJ+=+aF}if(aD.direction=="x"){aL+=aJ;if(aH=="bottom"){q.bottom+=aL+aG;aD.box={top:I-q.bottom,height:aL}}else{aD.box={top:q.top+aG,height:aL};q.top+=aL+aG}}else{aC+=aJ;if(aH=="left"){aD.box={left:q.left+aG,width:aC};q.left+=aC+aG}else{q.right+=aC+aG;aD.box={left:G-q.right,width:aC}}}aD.position=aH;aD.tickLength=aF;aD.box.padding=aJ;aD.innermost=aM}function U(aB){if(aB.direction=="x"){aB.box.left=q.left;aB.box.width=h}else{aB.box.top=q.top;aB.box.height=w}}function t(){var aC,aE=m();c.each(aE,function(aF,aG){aG.show=aG.options.show;if(aG.show==null){aG.show=aG.used}aG.reserveSpace=aG.show||aG.options.reserveSpace;n(aG)});allocatedAxes=c.grep(aE,function(aF){return aF.reserveSpace});q.left=q.right=q.top=q.bottom=0;if(O.grid.show){c.each(allocatedAxes,function(aF,aG){S(aG);P(aG);ap(aG,aG.ticks);L(aG)});for(aC=allocatedAxes.length-1;aC>=0;--aC){au(allocatedAxes[aC])}var aD=O.grid.minBorderMargin;if(aD==null){aD=0;for(aC=0;aC<Q.length;++aC){aD=Math.max(aD,Q[aC].points.radius+Q[aC].points.lineWidth/2)}}for(var aB in q){q[aB]+=O.grid.borderWidth;q[aB]=Math.max(aD,q[aB])}}h=G-q.left-q.right;w=I-q.bottom-q.top;c.each(aE,function(aF,aG){r(aG)});if(O.grid.show){c.each(allocatedAxes,function(aF,aG){U(aG)});k()}o()}function n(aE){var aF=aE.options,aD=+(aF.min!=null?aF.min:aE.datamin),aB=+(aF.max!=null?aF.max:aE.datamax),aH=aB-aD;if(aH==0){var aC=aB==0?1:0.01;if(aF.min==null){aD-=aC}if(aF.max==null||aF.min!=null){aB+=aC}}else{var aG=aF.autoscaleMargin;if(aG!=null){if(aF.min==null){aD-=aH*aG;if(aD<0&&aE.datamin!=null&&aE.datamin>=0){aD=0}}if(aF.max==null){aB+=aH*aG;if(aB>0&&aE.datamax!=null&&aE.datamax<=0){aB=0}}}}aE.min=aD;aE.max=aB}function S(aG){var aM=aG.options;var aH;if(typeof aM.ticks=="number"&&aM.ticks>0){aH=aM.ticks}else{aH=0.3*Math.sqrt(aG.direction=="x"?G:I)}var aT=(aG.max-aG.min)/aH,aO,aB,aN,aR,aS,aQ,aI;if(aM.mode=="time"){var aJ={second:1000,minute:60*1000,hour:60*60*1000,day:24*60*60*1000,month:30*24*60*60*1000,year:365.2425*24*60*60*1000};var aK=[[1,"second"],[2,"second"],[5,"second"],[10,"second"],[30,"second"],[1,"minute"],[2,"minute"],[5,"minute"],[10,"minute"],[30,"minute"],[1,"hour"],[2,"hour"],[4,"hour"],[8,"hour"],[12,"hour"],[1,"day"],[2,"day"],[3,"day"],[0.25,"month"],[0.5,"month"],[1,"month"],[2,"month"],[3,"month"],[6,"month"],[1,"year"]];var aC=0;if(aM.minTickSize!=null){if(typeof aM.tickSize=="number"){aC=aM.tickSize}else{aC=aM.minTickSize[0]*aJ[aM.minTickSize[1]]}}for(var aS=0;aS<aK.length-1;++aS){if(aT<(aK[aS][0]*aJ[aK[aS][1]]+aK[aS+1][0]*aJ[aK[aS+1][1]])/2&&aK[aS][0]*aJ[aK[aS][1]]>=aC){break}}aO=aK[aS][0];aN=aK[aS][1];if(aN=="year"){aQ=Math.pow(10,Math.floor(Math.log(aT/aJ.year)/Math.LN10));aI=(aT/aJ.year)/aQ;if(aI<1.5){aO=1}else{if(aI<3){aO=2}else{if(aI<7.5){aO=5}else{aO=10}}}aO*=aQ}aG.tickSize=aM.tickSize||[aO,aN];aB=function(aX){var a2=[],a0=aX.tickSize[0],a3=aX.tickSize[1],a1=new Date(aX.min);var aW=a0*aJ[a3];if(a3=="second"){a1.setUTCSeconds(a(a1.getUTCSeconds(),a0))}if(a3=="minute"){a1.setUTCMinutes(a(a1.getUTCMinutes(),a0))}if(a3=="hour"){a1.setUTCHours(a(a1.getUTCHours(),a0))}if(a3=="month"){a1.setUTCMonth(a(a1.getUTCMonth(),a0))}if(a3=="year"){a1.setUTCFullYear(a(a1.getUTCFullYear(),a0))}a1.setUTCMilliseconds(0);if(aW>=aJ.minute){a1.setUTCSeconds(0)}if(aW>=aJ.hour){a1.setUTCMinutes(0)}if(aW>=aJ.day){a1.setUTCHours(0)}if(aW>=aJ.day*4){a1.setUTCDate(1)}if(aW>=aJ.year){a1.setUTCMonth(0)}var a5=0,a4=Number.NaN,aY;do{aY=a4;a4=a1.getTime();a2.push(a4);if(a3=="month"){if(a0<1){a1.setUTCDate(1);var aV=a1.getTime();a1.setUTCMonth(a1.getUTCMonth()+1);var aZ=a1.getTime();a1.setTime(a4+a5*aJ.hour+(aZ-aV)*a0);a5=a1.getUTCHours();a1.setUTCHours(0)}else{a1.setUTCMonth(a1.getUTCMonth()+a0)}}else{if(a3=="year"){a1.setUTCFullYear(a1.getUTCFullYear()+a0)}else{a1.setTime(a4+aW)}}}while(a4<aX.max&&a4!=aY);return a2};aR=function(aV,aY){var a0=new Date(aV);if(aM.timeformat!=null){return c.plot.formatDate(a0,aM.timeformat,aM.monthNames)}var aW=aY.tickSize[0]*aJ[aY.tickSize[1]];var aX=aY.max-aY.min;var aZ=(aM.twelveHourClock)?" %p":"";if(aW<aJ.minute){fmt="%h:%M:%S"+aZ}else{if(aW<aJ.day){if(aX<2*aJ.day){fmt="%h:%M"+aZ}else{fmt="%b %d %h:%M"+aZ}}else{if(aW<aJ.month){fmt="%b %d"}else{if(aW<aJ.year){if(aX<aJ.year){fmt="%b"}else{fmt="%b %y"}}else{fmt="%y"}}}}return c.plot.formatDate(a0,fmt,aM.monthNames)}}else{var aU=aM.tickDecimals;var aP=-Math.floor(Math.log(aT)/Math.LN10);if(aU!=null&&aP>aU){aP=aU}aQ=Math.pow(10,-aP);aI=aT/aQ;if(aI<1.5){aO=1}else{if(aI<3){aO=2;if(aI>2.25&&(aU==null||aP+1<=aU)){aO=2.5;++aP}}else{if(aI<7.5){aO=5}else{aO=10}}}aO*=aQ;if(aM.minTickSize!=null&&aO<aM.minTickSize){aO=aM.minTickSize}aG.tickDecimals=Math.max(0,aU!=null?aU:aP);aG.tickSize=aM.tickSize||aO;aB=function(aX){var aZ=[];var a0=a(aX.min,aX.tickSize),aW=0,aV=Number.NaN,aY;do{aY=aV;aV=a0+aW*aX.tickSize;aZ.push(aV);++aW}while(aV<aX.max&&aV!=aY);return aZ};aR=function(aV,aW){return aV.toFixed(aW.tickDecimals)}}if(aM.alignTicksWithAxis!=null){var aF=(aG.direction=="x"?p:aw)[aM.alignTicksWithAxis-1];if(aF&&aF.used&&aF!=aG){var aL=aB(aG);if(aL.length>0){if(aM.min==null){aG.min=Math.min(aG.min,aL[0])}if(aM.max==null&&aL.length>1){aG.max=Math.max(aG.max,aL[aL.length-1])}}aB=function(aX){var aY=[],aV,aW;for(aW=0;aW<aF.ticks.length;++aW){aV=(aF.ticks[aW].v-aF.min)/(aF.max-aF.min);aV=aX.min+aV*(aX.max-aX.min);aY.push(aV)}return aY};if(aG.mode!="time"&&aM.tickDecimals==null){var aE=Math.max(0,-Math.floor(Math.log(aT)/Math.LN10)+1),aD=aB(aG);if(!(aD.length>1&&/\..*0$/.test((aD[1]-aD[0]).toFixed(aE)))){aG.tickDecimals=aE}}}}aG.tickGenerator=aB;if(c.isFunction(aM.tickFormatter)){aG.tickFormatter=function(aV,aW){return""+aM.tickFormatter(aV,aW)}}else{aG.tickFormatter=aR}}function P(aF){var aH=aF.options.ticks,aG=[];if(aH==null||(typeof aH=="number"&&aH>0)){aG=aF.tickGenerator(aF)}else{if(aH){if(c.isFunction(aH)){aG=aH({min:aF.min,max:aF.max})}else{aG=aH}}}var aE,aB;aF.ticks=[];for(aE=0;aE<aG.length;++aE){var aC=null;var aD=aG[aE];if(typeof aD=="object"){aB=+aD[0];if(aD.length>1){aC=aD[1]}}else{aB=+aD}if(aC==null){aC=aF.tickFormatter(aB,aF)}if(!isNaN(aB)){aF.ticks.push({v:aB,label:aC})}}}function ap(aB,aC){if(aB.options.autoscaleMargin&&aC.length>0){if(aB.options.min==null){aB.min=Math.min(aB.min,aC[0].v)}if(aB.options.max==null&&aC.length>1){aB.max=Math.max(aB.max,aC[aC.length-1].v)}}}function W(){H.clearRect(0,0,G,I);var aC=O.grid;if(aC.show&&aC.backgroundColor){N()}if(aC.show&&!aC.aboveData){ac()}for(var aB=0;aB<Q.length;++aB){an(ak.drawSeries,[H,Q[aB]]);d(Q[aB])}an(ak.draw,[H]);if(aC.show&&aC.aboveData){ac()}}function D(aB,aI){var aE,aH,aG,aD,aF=m();for(i=0;i<aF.length;++i){aE=aF[i];if(aE.direction==aI){aD=aI+aE.n+"axis";if(!aB[aD]&&aE.n==1){aD=aI+"axis"}if(aB[aD]){aH=aB[aD].from;aG=aB[aD].to;break}}}if(!aB[aD]){aE=aI=="x"?p[0]:aw[0];aH=aB[aI+"1"];aG=aB[aI+"2"]}if(aH!=null&&aG!=null&&aH>aG){var aC=aH;aH=aG;aG=aC}return{from:aH,to:aG,axis:aE}}function N(){H.save();H.translate(q.left,q.top);H.fillStyle=am(O.grid.backgroundColor,w,0,"rgba(255, 255, 255, 0)");H.fillRect(0,0,h,w);H.restore()}function ac(){var aF;H.save();H.translate(q.left,q.top);var aH=O.grid.markings;if(aH){if(c.isFunction(aH)){var aK=aq.getAxes();aK.xmin=aK.xaxis.min;aK.xmax=aK.xaxis.max;aK.ymin=aK.yaxis.min;aK.ymax=aK.yaxis.max;aH=aH(aK)}for(aF=0;aF<aH.length;++aF){var aD=aH[aF],aC=D(aD,"x"),aI=D(aD,"y");if(aC.from==null){aC.from=aC.axis.min}if(aC.to==null){aC.to=aC.axis.max}if(aI.from==null){aI.from=aI.axis.min}if(aI.to==null){aI.to=aI.axis.max}if(aC.to<aC.axis.min||aC.from>aC.axis.max||aI.to<aI.axis.min||aI.from>aI.axis.max){continue}aC.from=Math.max(aC.from,aC.axis.min);aC.to=Math.min(aC.to,aC.axis.max);aI.from=Math.max(aI.from,aI.axis.min);aI.to=Math.min(aI.to,aI.axis.max);if(aC.from==aC.to&&aI.from==aI.to){continue}aC.from=aC.axis.p2c(aC.from);aC.to=aC.axis.p2c(aC.to);aI.from=aI.axis.p2c(aI.from);aI.to=aI.axis.p2c(aI.to);if(aC.from==aC.to||aI.from==aI.to){H.beginPath();H.strokeStyle=aD.color||O.grid.markingsColor;H.lineWidth=aD.lineWidth||O.grid.markingsLineWidth;H.moveTo(aC.from,aI.from);H.lineTo(aC.to,aI.to);H.stroke()}else{H.fillStyle=aD.color||O.grid.markingsColor;H.fillRect(aC.from,aI.to,aC.to-aC.from,aI.from-aI.to)}}}var aK=m(),aM=O.grid.borderWidth;for(var aE=0;aE<aK.length;++aE){var aB=aK[aE],aG=aB.box,aQ=aB.tickLength,aN,aL,aP,aJ;if(!aB.show||aB.ticks.length==0){continue}H.strokeStyle=aB.options.tickColor||c.color.parse(aB.options.color).scale("a",0.22).toString();H.lineWidth=1;if(aB.direction=="x"){aN=0;if(aQ=="full"){aL=(aB.position=="top"?0:w)}else{aL=aG.top-q.top+(aB.position=="top"?aG.height:0)}}else{aL=0;if(aQ=="full"){aN=(aB.position=="left"?0:h)}else{aN=aG.left-q.left+(aB.position=="left"?aG.width:0)}}if(!aB.innermost){H.beginPath();aP=aJ=0;if(aB.direction=="x"){aP=h}else{aJ=w}if(H.lineWidth==1){aN=Math.floor(aN)+0.5;aL=Math.floor(aL)+0.5}H.moveTo(aN,aL);H.lineTo(aN+aP,aL+aJ);H.stroke()}H.beginPath();for(aF=0;aF<aB.ticks.length;++aF){var aO=aB.ticks[aF].v;aP=aJ=0;if(aO<aB.min||aO>aB.max||(aQ=="full"&&aM>0&&(aO==aB.min||aO==aB.max))){continue}if(aB.direction=="x"){aN=aB.p2c(aO);aJ=aQ=="full"?-w:aQ;if(aB.position=="top"){aJ=-aJ}}else{aL=aB.p2c(aO);aP=aQ=="full"?-h:aQ;if(aB.position=="left"){aP=-aP}}if(H.lineWidth==1){if(aB.direction=="x"){aN=Math.floor(aN)+0.5}else{aL=Math.floor(aL)+0.5}}H.moveTo(aN,aL);H.lineTo(aN+aP,aL+aJ)}H.stroke()}if(aM){H.lineWidth=aM;H.strokeStyle=O.grid.borderColor;H.strokeRect(-aM/2,-aM/2,h+aM,w+aM)}H.restore()}function k(){av.find(".tickLabels").remove();var aG=['<div class="tickLabels" style="font-size:smaller">'];var aJ=m();for(var aD=0;aD<aJ.length;++aD){var aC=aJ[aD],aF=aC.box;if(!aC.show){continue}aG.push('<div class="'+aC.direction+"Axis "+aC.direction+aC.n+'Axis" style="color:'+aC.options.color+'">');for(var aE=0;aE<aC.ticks.length;++aE){var aH=aC.ticks[aE];if(!aH.label||aH.v<aC.min||aH.v>aC.max){continue}var aK={},aI;if(aC.direction=="x"){aI="center";aK.left=Math.round(q.left+aC.p2c(aH.v)-aC.labelWidth/2);if(aC.position=="bottom"){aK.top=aF.top+aF.padding}else{aK.bottom=I-(aF.top+aF.height-aF.padding)}}else{aK.top=Math.round(q.top+aC.p2c(aH.v)-aC.labelHeight/2);if(aC.position=="left"){aK.right=G-(aF.left+aF.width-aF.padding);aI="right"}else{aK.left=aF.left+aF.padding;aI="left"}}aK.width=aC.labelWidth;var aB=["position:absolute","text-align:"+aI];for(var aL in aK){aB.push(aL+":"+aK[aL]+"px")}aG.push('<div class="tickLabel" style="'+aB.join(";")+'">'+aH.label+"</div>")}aG.push("</div>")}aG.push("</div>");av.append(aG.join(""))}function d(aB){if(aB.lines.show){at(aB)}if(aB.bars.show){e(aB)}if(aB.points.show){ao(aB)}}function at(aE){function aD(aP,aQ,aI,aU,aT){var aV=aP.points,aJ=aP.pointsize,aN=null,aM=null;H.beginPath();for(var aO=aJ;aO<aV.length;aO+=aJ){var aL=aV[aO-aJ],aS=aV[aO-aJ+1],aK=aV[aO],aR=aV[aO+1];if(aL==null||aK==null){continue}if(aS<=aR&&aS<aT.min){if(aR<aT.min){continue}aL=(aT.min-aS)/(aR-aS)*(aK-aL)+aL;aS=aT.min}else{if(aR<=aS&&aR<aT.min){if(aS<aT.min){continue}aK=(aT.min-aS)/(aR-aS)*(aK-aL)+aL;aR=aT.min}}if(aS>=aR&&aS>aT.max){if(aR>aT.max){continue}aL=(aT.max-aS)/(aR-aS)*(aK-aL)+aL;aS=aT.max}else{if(aR>=aS&&aR>aT.max){if(aS>aT.max){continue}aK=(aT.max-aS)/(aR-aS)*(aK-aL)+aL;aR=aT.max}}if(aL<=aK&&aL<aU.min){if(aK<aU.min){continue}aS=(aU.min-aL)/(aK-aL)*(aR-aS)+aS;aL=aU.min}else{if(aK<=aL&&aK<aU.min){if(aL<aU.min){continue}aR=(aU.min-aL)/(aK-aL)*(aR-aS)+aS;aK=aU.min}}if(aL>=aK&&aL>aU.max){if(aK>aU.max){continue}aS=(aU.max-aL)/(aK-aL)*(aR-aS)+aS;aL=aU.max}else{if(aK>=aL&&aK>aU.max){if(aL>aU.max){continue}aR=(aU.max-aL)/(aK-aL)*(aR-aS)+aS;aK=aU.max}}if(aL!=aN||aS!=aM){H.moveTo(aU.p2c(aL)+aQ,aT.p2c(aS)+aI)}aN=aK;aM=aR;H.lineTo(aU.p2c(aK)+aQ,aT.p2c(aR)+aI)}H.stroke()}function aF(aI,aQ,aP){var aW=aI.points,aV=aI.pointsize,aN=Math.min(Math.max(0,aP.min),aP.max),aX=0,aU,aT=false,aM=1,aL=0,aR=0;while(true){if(aV>0&&aX>aW.length+aV){break}aX+=aV;var aZ=aW[aX-aV],aK=aW[aX-aV+aM],aY=aW[aX],aJ=aW[aX+aM];if(aT){if(aV>0&&aZ!=null&&aY==null){aR=aX;aV=-aV;aM=2;continue}if(aV<0&&aX==aL+aV){H.fill();aT=false;aV=-aV;aM=1;aX=aL=aR+aV;continue}}if(aZ==null||aY==null){continue}if(aZ<=aY&&aZ<aQ.min){if(aY<aQ.min){continue}aK=(aQ.min-aZ)/(aY-aZ)*(aJ-aK)+aK;aZ=aQ.min}else{if(aY<=aZ&&aY<aQ.min){if(aZ<aQ.min){continue}aJ=(aQ.min-aZ)/(aY-aZ)*(aJ-aK)+aK;aY=aQ.min}}if(aZ>=aY&&aZ>aQ.max){if(aY>aQ.max){continue}aK=(aQ.max-aZ)/(aY-aZ)*(aJ-aK)+aK;aZ=aQ.max}else{if(aY>=aZ&&aY>aQ.max){if(aZ>aQ.max){continue}aJ=(aQ.max-aZ)/(aY-aZ)*(aJ-aK)+aK;aY=aQ.max}}if(!aT){H.beginPath();H.moveTo(aQ.p2c(aZ),aP.p2c(aN));aT=true}if(aK>=aP.max&&aJ>=aP.max){H.lineTo(aQ.p2c(aZ),aP.p2c(aP.max));H.lineTo(aQ.p2c(aY),aP.p2c(aP.max));continue}else{if(aK<=aP.min&&aJ<=aP.min){H.lineTo(aQ.p2c(aZ),aP.p2c(aP.min));H.lineTo(aQ.p2c(aY),aP.p2c(aP.min));continue}}var aO=aZ,aS=aY;if(aK<=aJ&&aK<aP.min&&aJ>=aP.min){aZ=(aP.min-aK)/(aJ-aK)*(aY-aZ)+aZ;aK=aP.min}else{if(aJ<=aK&&aJ<aP.min&&aK>=aP.min){aY=(aP.min-aK)/(aJ-aK)*(aY-aZ)+aZ;aJ=aP.min}}if(aK>=aJ&&aK>aP.max&&aJ<=aP.max){aZ=(aP.max-aK)/(aJ-aK)*(aY-aZ)+aZ;aK=aP.max}else{if(aJ>=aK&&aJ>aP.max&&aK<=aP.max){aY=(aP.max-aK)/(aJ-aK)*(aY-aZ)+aZ;aJ=aP.max}}if(aZ!=aO){H.lineTo(aQ.p2c(aO),aP.p2c(aK))}H.lineTo(aQ.p2c(aZ),aP.p2c(aK));H.lineTo(aQ.p2c(aY),aP.p2c(aJ));if(aY!=aS){H.lineTo(aQ.p2c(aY),aP.p2c(aJ));H.lineTo(aQ.p2c(aS),aP.p2c(aJ))}}}H.save();H.translate(q.left,q.top);H.lineJoin="round";var aG=aE.lines.lineWidth,aB=aE.shadowSize;if(aG>0&&aB>0){H.lineWidth=aB;H.strokeStyle="rgba(0,0,0,0.1)";var aH=Math.PI/18;aD(aE.datapoints,Math.sin(aH)*(aG/2+aB/2),Math.cos(aH)*(aG/2+aB/2),aE.xaxis,aE.yaxis);H.lineWidth=aB/2;aD(aE.datapoints,Math.sin(aH)*(aG/2+aB/4),Math.cos(aH)*(aG/2+aB/4),aE.xaxis,aE.yaxis)}H.lineWidth=aG;H.strokeStyle=aE.color;var aC=ae(aE.lines,aE.color,0,w);if(aC){H.fillStyle=aC;aF(aE.datapoints,aE.xaxis,aE.yaxis)}if(aG>0){aD(aE.datapoints,0,0,aE.xaxis,aE.yaxis)}H.restore()}function ao(aE){function aH(aN,aM,aU,aK,aS,aT,aQ,aJ){var aR=aN.points,aI=aN.pointsize;for(var aL=0;aL<aR.length;aL+=aI){var aP=aR[aL],aO=aR[aL+1];if(aP==null||aP<aT.min||aP>aT.max||aO<aQ.min||aO>aQ.max){continue}H.beginPath();aP=aT.p2c(aP);aO=aQ.p2c(aO)+aK;if(aJ=="circle"){H.arc(aP,aO,aM,0,aS?Math.PI:Math.PI*2,false)}else{aJ(H,aP,aO,aM,aS)}H.closePath();if(aU){H.fillStyle=aU;H.fill()}H.stroke()}}H.save();H.translate(q.left,q.top);var aG=aE.points.lineWidth,aC=aE.shadowSize,aB=aE.points.radius,aF=aE.points.symbol;if(aG>0&&aC>0){var aD=aC/2;H.lineWidth=aD;H.strokeStyle="rgba(0,0,0,0.1)";aH(aE.datapoints,aB,null,aD+aD/2,true,aE.xaxis,aE.yaxis,aF);H.strokeStyle="rgba(0,0,0,0.2)";aH(aE.datapoints,aB,null,aD/2,true,aE.xaxis,aE.yaxis,aF)}H.lineWidth=aG;H.strokeStyle=aE.color;aH(aE.datapoints,aB,ae(aE.points,aE.color),0,false,aE.xaxis,aE.yaxis,aF);H.restore()}function E(aN,aM,aV,aI,aQ,aF,aD,aL,aK,aU,aR,aC){var aE,aT,aJ,aP,aG,aB,aO,aH,aS;if(aR){aH=aB=aO=true;aG=false;aE=aV;aT=aN;aP=aM+aI;aJ=aM+aQ;if(aT<aE){aS=aT;aT=aE;aE=aS;aG=true;aB=false}}else{aG=aB=aO=true;aH=false;aE=aN+aI;aT=aN+aQ;aJ=aV;aP=aM;if(aP<aJ){aS=aP;aP=aJ;aJ=aS;aH=true;aO=false}}if(aT<aL.min||aE>aL.max||aP<aK.min||aJ>aK.max){return}if(aE<aL.min){aE=aL.min;aG=false}if(aT>aL.max){aT=aL.max;aB=false}if(aJ<aK.min){aJ=aK.min;aH=false}if(aP>aK.max){aP=aK.max;aO=false}aE=aL.p2c(aE);aJ=aK.p2c(aJ);aT=aL.p2c(aT);aP=aK.p2c(aP);if(aD){aU.beginPath();aU.moveTo(aE,aJ);aU.lineTo(aE,aP);aU.lineTo(aT,aP);aU.lineTo(aT,aJ);aU.fillStyle=aD(aJ,aP);aU.fill()}if(aC>0&&(aG||aB||aO||aH)){aU.beginPath();aU.moveTo(aE,aJ+aF);if(aG){aU.lineTo(aE,aP+aF)}else{aU.moveTo(aE,aP+aF)}if(aO){aU.lineTo(aT,aP+aF)}else{aU.moveTo(aT,aP+aF)}if(aB){aU.lineTo(aT,aJ+aF)}else{aU.moveTo(aT,aJ+aF)}if(aH){aU.lineTo(aE,aJ+aF)}else{aU.moveTo(aE,aJ+aF)}aU.stroke()}}function e(aD){function aC(aJ,aI,aL,aG,aK,aN,aM){var aO=aJ.points,aF=aJ.pointsize;for(var aH=0;aH<aO.length;aH+=aF){if(aO[aH]==null){continue}E(aO[aH],aO[aH+1],aO[aH+2],aI,aL,aG,aK,aN,aM,H,aD.bars.horizontal,aD.bars.lineWidth)}}H.save();H.translate(q.left,q.top);H.lineWidth=aD.bars.lineWidth;H.strokeStyle=aD.color;var aB=aD.bars.align=="left"?0:-aD.bars.barWidth/2;var aE=aD.bars.fill?function(aF,aG){return ae(aD.bars,aD.color,aF,aG)}:null;aC(aD.datapoints,aB,aB+aD.bars.barWidth,0,aE,aD.xaxis,aD.yaxis);H.restore()}function ae(aD,aB,aC,aF){var aE=aD.fill;if(!aE){return null}if(aD.fillColor){return am(aD.fillColor,aC,aF,aB)}var aG=c.color.parse(aB);aG.a=typeof aE=="number"?aE:0.4;aG.normalize();return aG.toString()}function o(){av.find(".legend").remove();if(!O.legend.show){return}var aH=[],aF=false,aN=O.legend.labelFormatter,aM,aJ;for(var aE=0;aE<Q.length;++aE){aM=Q[aE];aJ=aM.label;if(!aJ){continue}if(aE%O.legend.noColumns==0){if(aF){aH.push("</tr>")}aH.push("<tr>");aF=true}if(aN){aJ=aN(aJ,aM)}aH.push('<td class="legendColorBox"><div style="border:1px solid '+O.legend.labelBoxBorderColor+';padding:1px"><div style="width:4px;height:0;border:5px solid '+aM.color+';overflow:hidden"></div></div></td><td class="legendLabel">'+aJ+"</td>")}if(aF){aH.push("</tr>")}if(aH.length==0){return}var aL='<table style="font-size:smaller;color:'+O.grid.color+'">'+aH.join("")+"</table>";if(O.legend.container!=null){c(O.legend.container).html(aL)}else{var aI="",aC=O.legend.position,aD=O.legend.margin;if(aD[0]==null){aD=[aD,aD]}if(aC.charAt(0)=="n"){aI+="top:"+(aD[1]+q.top)+"px;"}else{if(aC.charAt(0)=="s"){aI+="bottom:"+(aD[1]+q.bottom)+"px;"}}if(aC.charAt(1)=="e"){aI+="right:"+(aD[0]+q.right)+"px;"}else{if(aC.charAt(1)=="w"){aI+="left:"+(aD[0]+q.left)+"px;"}}var aK=c('<div class="legend">'+aL.replace('style="','style="position:absolute;'+aI+";")+"</div>").appendTo(av);if(O.legend.backgroundOpacity!=0){var aG=O.legend.backgroundColor;if(aG==null){aG=O.grid.backgroundColor;if(aG&&typeof aG=="string"){aG=c.color.parse(aG)}else{aG=c.color.extract(aK,"background-color")}aG.a=1;aG=aG.toString()}var aB=aK.children();c('<div style="position:absolute;width:'+aB.width()+"px;height:"+aB.height()+"px;"+aI+"background-color:"+aG+';"> </div>').prependTo(aK).css("opacity",O.legend.backgroundOpacity)}}}var ab=[],M=null;function K(aI,aG,aD){var aO=O.grid.mouseActiveRadius,a0=aO*aO+1,aY=null,aR=false,aW,aU;for(aW=Q.length-1;aW>=0;--aW){if(!aD(Q[aW])){continue}var aP=Q[aW],aH=aP.xaxis,aF=aP.yaxis,aV=aP.datapoints.points,aT=aP.datapoints.pointsize,aQ=aH.c2p(aI),aN=aF.c2p(aG),aC=aO/aH.scale,aB=aO/aF.scale;if(aH.options.inverseTransform){aC=Number.MAX_VALUE}if(aF.options.inverseTransform){aB=Number.MAX_VALUE}if(aP.lines.show||aP.points.show){for(aU=0;aU<aV.length;aU+=aT){var aK=aV[aU],aJ=aV[aU+1];if(aK==null){continue}if(aK-aQ>aC||aK-aQ<-aC||aJ-aN>aB||aJ-aN<-aB){continue}var aM=Math.abs(aH.p2c(aK)-aI),aL=Math.abs(aF.p2c(aJ)-aG),aS=aM*aM+aL*aL;if(aS<a0){a0=aS;aY=[aW,aU/aT]}}}if(aP.bars.show&&!aY){var aE=aP.bars.align=="left"?0:-aP.bars.barWidth/2,aX=aE+aP.bars.barWidth;for(aU=0;aU<aV.length;aU+=aT){var aK=aV[aU],aJ=aV[aU+1],aZ=aV[aU+2];if(aK==null){continue}if(Q[aW].bars.horizontal?(aQ<=Math.max(aZ,aK)&&aQ>=Math.min(aZ,aK)&&aN>=aJ+aE&&aN<=aJ+aX):(aQ>=aK+aE&&aQ<=aK+aX&&aN>=Math.min(aZ,aJ)&&aN<=Math.max(aZ,aJ))){aY=[aW,aU/aT]}}}}if(aY){aW=aY[0];aU=aY[1];aT=Q[aW].datapoints.pointsize;return{datapoint:Q[aW].datapoints.points.slice(aU*aT,(aU+1)*aT),dataIndex:aU,series:Q[aW],seriesIndex:aW}}return null}function aa(aB){if(O.grid.hoverable){u("plothover",aB,function(aC){return aC.hoverable!=false})}}function l(aB){if(O.grid.hoverable){u("plothover",aB,function(aC){return false})}}function R(aB){u("plotclick",aB,function(aC){return aC.clickable!=false})}function u(aC,aB,aD){var aE=y.offset(),aH=aB.pageX-aE.left-q.left,aF=aB.pageY-aE.top-q.top,aJ=C({left:aH,top:aF});aJ.pageX=aB.pageX;aJ.pageY=aB.pageY;var aK=K(aH,aF,aD);if(aK){aK.pageX=parseInt(aK.series.xaxis.p2c(aK.datapoint[0])+aE.left+q.left);aK.pageY=parseInt(aK.series.yaxis.p2c(aK.datapoint[1])+aE.top+q.top)}if(O.grid.autoHighlight){for(var aG=0;aG<ab.length;++aG){var aI=ab[aG];if(aI.auto==aC&&!(aK&&aI.series==aK.series&&aI.point[0]==aK.datapoint[0]&&aI.point[1]==aK.datapoint[1])){T(aI.series,aI.point)}}if(aK){x(aK.series,aK.datapoint,aC)}}av.trigger(aC,[aJ,aK])}function f(){if(!M){M=setTimeout(s,30)}}function s(){M=null;A.save();A.clearRect(0,0,G,I);A.translate(q.left,q.top);var aC,aB;for(aC=0;aC<ab.length;++aC){aB=ab[aC];if(aB.series.bars.show){v(aB.series,aB.point)}else{ay(aB.series,aB.point)}}A.restore();an(ak.drawOverlay,[A])}function x(aD,aB,aF){if(typeof aD=="number"){aD=Q[aD]}if(typeof aB=="number"){var aE=aD.datapoints.pointsize;aB=aD.datapoints.points.slice(aE*aB,aE*(aB+1))}var aC=al(aD,aB);if(aC==-1){ab.push({series:aD,point:aB,auto:aF});f()}else{if(!aF){ab[aC].auto=false}}}function T(aD,aB){if(aD==null&&aB==null){ab=[];f()}if(typeof aD=="number"){aD=Q[aD]}if(typeof aB=="number"){aB=aD.data[aB]}var aC=al(aD,aB);if(aC!=-1){ab.splice(aC,1);f()}}function al(aD,aE){for(var aB=0;aB<ab.length;++aB){var aC=ab[aB];if(aC.series==aD&&aC.point[0]==aE[0]&&aC.point[1]==aE[1]){return aB}}return -1}function ay(aE,aD){var aC=aD[0],aI=aD[1],aH=aE.xaxis,aG=aE.yaxis;if(aC<aH.min||aC>aH.max||aI<aG.min||aI>aG.max){return}var aF=aE.points.radius+aE.points.lineWidth/2;A.lineWidth=aF;A.strokeStyle=c.color.parse(aE.color).scale("a",0.5).toString();var aB=1.5*aF,aC=aH.p2c(aC),aI=aG.p2c(aI);A.beginPath();if(aE.points.symbol=="circle"){A.arc(aC,aI,aB,0,2*Math.PI,false)}else{aE.points.symbol(A,aC,aI,aB,false)}A.closePath();A.stroke()}function v(aE,aB){A.lineWidth=aE.bars.lineWidth;A.strokeStyle=c.color.parse(aE.color).scale("a",0.5).toString();var aD=c.color.parse(aE.color).scale("a",0.5).toString();var aC=aE.bars.align=="left"?0:-aE.bars.barWidth/2;E(aB[0],aB[1],aB[2]||0,aC,aC+aE.bars.barWidth,0,function(){return aD},aE.xaxis,aE.yaxis,A,aE.bars.horizontal,aE.bars.lineWidth)}function am(aJ,aB,aH,aC){if(typeof aJ=="string"){return aJ}else{var aI=H.createLinearGradient(0,aH,0,aB);for(var aE=0,aD=aJ.colors.length;aE<aD;++aE){var aF=aJ.colors[aE];if(typeof aF!="string"){var aG=c.color.parse(aC);if(aF.brightness!=null){aG=aG.scale("rgb",aF.brightness)}if(aF.opacity!=null){aG.a*=aF.opacity}aF=aG.toString()}aI.addColorStop(aE/(aD-1),aF)}return aI}}}c.plot=function(g,e,d){var f=new b(c(g),e,d,c.plot.plugins);return f};c.plot.version="0.7";c.plot.plugins=[];c.plot.formatDate=function(l,f,h){var o=function(d){d=""+d;return d.length==1?"0"+d:d};var e=[];var p=false,j=false;var n=l.getUTCHours();var k=n<12;if(h==null){h=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]}if(f.search(/%p|%P/)!=-1){if(n>12){n=n-12}else{if(n==0){n=12}}}for(var g=0;g<f.length;++g){var m=f.charAt(g);if(p){switch(m){case"h":m=""+n;break;case"H":m=o(n);break;case"M":m=o(l.getUTCMinutes());break;case"S":m=o(l.getUTCSeconds());break;case"d":m=""+l.getUTCDate();break;case"m":m=""+(l.getUTCMonth()+1);break;case"y":m=""+l.getUTCFullYear();break;case"b":m=""+h[l.getUTCMonth()];break;case"p":m=(k)?("am"):("pm");break;case"P":m=(k)?("AM"):("PM");break;case"0":m="";j=true;break}if(m&&j){m=o(m);j=false}e.push(m);if(!j){p=false}}else{if(m=="%"){p=true}else{e.push(m)}}}return e.join("")};function a(e,d){return d*Math.floor(e/d)}})(jQuery);
\ No newline at end of file diff --git a/module/web/static/js/libs/lodash-0.5.2.js b/module/web/static/js/libs/lodash-0.7.0.js index 3c5448223..7843feb80 100644 --- a/module/web/static/js/libs/lodash-0.5.2.js +++ b/module/web/static/js/libs/lodash-0.7.0.js @@ -1,38 +1,13 @@  /*! - * Lo-Dash v0.5.2 <http://lodash.com> - * Copyright 2012 John-David Dalton <http://allyoucanleet.com/> - * Based on Underscore.js 1.3.3, copyright 2009-2012 Jeremy Ashkenas, DocumentCloud Inc. - * <http://documentcloud.github.com/underscore> + * Lo-Dash v0.7.0 <http://lodash.com> + * (c) 2012 John-David Dalton <http://allyoucanleet.com/> + * Based on Underscore.js 1.4.0 <http://underscorejs.org> + * (c) 2009-2012 Jeremy Ashkenas, DocumentCloud Inc.   * Available under MIT license <http://lodash.com/license>   */  ;(function(window, undefined) {    'use strict'; -  /** -   * Used to cache the last `_.templateSettings.evaluate` delimiter to avoid -   * unnecessarily assigning `reEvaluateDelimiter` a new generated regexp. -   * Assigned in `_.template`. -   */ -  var lastEvaluateDelimiter; - -  /** -   * Used to cache the last template `options.variable` to avoid unnecessarily -   * assigning `reDoubleVariable` a new generated regexp. Assigned in `_.template`. -   */ -  var lastVariable; - -  /** -   * Used to match potentially incorrect data object references, like `obj.obj`, -   * in compiled templates. Assigned in `_.template`. -   */ -  var reDoubleVariable; - -  /** -   * Used to match "evaluate" delimiters, including internal delimiters, -   * in template text. Assigned in `_.template`. -   */ -  var reEvaluateDelimiter; -    /** Detect free variable `exports` */    var freeExports = typeof exports == 'object' && exports &&      (typeof global == 'object' && global && global == global.global && (window = global), exports); @@ -47,11 +22,17 @@    /** Used to generate unique IDs */    var idCounter = 0; +  /** Used by `cachedContains` as the default size when optimizations are enabled for large arrays */ +  var largeArraySize = 30; +    /** Used to restore the original `_` reference in `noConflict` */    var oldDash = window._;    /** Used to detect delimiter values that should be processed by `tokenizeEvaluate` */ -  var reComplexDelimiter = /[-+=!~*%&^<>|{(\/]|\[\D|\b(?:delete|in|instanceof|new|typeof|void)\b/; +  var reComplexDelimiter = /[-?+=!~*%&^<>|{(\/]|\[\D|\b(?:delete|in|instanceof|new|typeof|void)\b/; + +  /** Used to match HTML entities */ +  var reEscapedHtml = /&(?:amp|lt|gt|quot|#x27);/g;    /** Used to match empty string literals in compiled template source */    var reEmptyStringLeading = /\b__p \+= '';/g, @@ -71,11 +52,11 @@        .replace(/valueOf|for [^\]]+/g, '.+?') + '$'    ); -  /** Used to match tokens in template text */ -  var reToken = /__token__(\d+)/g; +  /** Used to ensure capturing order and avoid matches for undefined delimiters */ +  var reNoMatch = /($^)/; -  /** Used to match unescaped characters in strings for inclusion in HTML */ -  var reUnescapedHtml = /[&<"']/g; +  /** Used to match HTML characters */ +  var reUnescapedHtml = /[&<>"']/g;    /** Used to match unescaped characters in compiled string literals */    var reUnescapedString = /['\n\r\t\u2028\u2029\\]/g; @@ -89,12 +70,6 @@    /** Used to make template sourceURLs easier to identify */    var templateCounter = 0; -  /** Used to replace template delimiters */ -  var token = '__token__'; - -  /** Used to store tokenized template text snippets */ -  var tokenized = []; -    /** Native method shortcuts */    var concat = ArrayProto.concat,        hasOwnProperty = ObjectProto.hasOwnProperty, @@ -105,9 +80,14 @@    /* Native method shortcuts for methods with the same name as other `lodash` methods */    var nativeBind = reNative.test(nativeBind = slice.bind) && nativeBind, +      nativeFloor = Math.floor, +      nativeGetPrototypeOf = reNative.test(nativeGetPrototypeOf = Object.getPrototypeOf) && nativeGetPrototypeOf,        nativeIsArray = reNative.test(nativeIsArray = Array.isArray) && nativeIsArray,        nativeIsFinite = window.isFinite, -      nativeKeys = reNative.test(nativeKeys = Object.keys) && nativeKeys; +      nativeKeys = reNative.test(nativeKeys = Object.keys) && nativeKeys, +      nativeMax = Math.max, +      nativeMin = Math.min, +      nativeRandom = Math.random;    /** `Object#toString` result shortcuts */    var argsClass = '[object Arguments]', @@ -126,11 +106,24 @@    /**     * Detect the JScript [[DontEnum]] bug: +   *     * In IE < 9 an objects own properties, shadowing non-enumerable ones, are     * made non-enumerable as well.     */    var hasDontEnumBug; +  /** +   * Detect if `Array#shift` and `Array#splice` augment array-like objects +   * incorrectly: +   * +   * Firefox < 10, IE compatibility mode, and IE < 9 have buggy Array `shift()` +   * and `splice()` functions that fail to remove the last element, `value[0]`, +   * of array-like objects even though the `length` property is set to `0`. +   * The `shift()` method is buggy in IE 8 compatibility mode, while `splice()` +   * is buggy regardless of mode in IE < 9 and buggy in compatibility mode in IE 9. +   */ +  var hasObjectSpliceBug; +    /** Detect if own properties are iterated after inherited properties (IE < 9) */    var iteratesOwnLast; @@ -138,13 +131,17 @@    var noArgsEnum = true;    (function() { -    var props = []; +    var object = { '0': 1, 'length': 1 }, +        props = []; +      function ctor() { this.x = 1; }      ctor.prototype = { 'valueOf': 1, 'y': 1 };      for (var prop in new ctor) { props.push(prop); }      for (prop in arguments) { noArgsEnum = !prop; } +      hasDontEnumBug = (props + '').length < 4;      iteratesOwnLast = props[0] != 'x'; +    hasObjectSpliceBug = (props.splice.call(object, 0, 1), object[0]);    }(1));    /** Detect if an `arguments` object's [[Class]] is unresolvable (Firefox < 4, IE < 9) */ @@ -155,6 +152,7 @@    /**     * Detect lack of support for accessing string characters by index: +   *     * IE < 8 can't access characters by index and IE 8 can only access     * characters by index on string literals.     */ @@ -175,18 +173,25 @@    /* Detect if `Object.keys` exists and is inferred to be fast (IE, Opera, V8) */    var isKeysFast = nativeKeys && /^.+$|true/.test(nativeKeys + !!window.attachEvent); -  /** Detect if sourceURL syntax is usable without erroring */ +  /* Detect if strict mode, "use strict", is inferred to be fast (V8) */ +  var isStrictFast = !isBindFast; + +  /** +   * Detect if sourceURL syntax is usable without erroring: +   * +   * The JS engine in Adobe products, like InDesign, will throw a syntax error +   * when it encounters a single line comment beginning with the `@` symbol. +   * +   * The JS engine in Narwhal will generate the function `function anonymous(){//}` +   * and throw a syntax error. +   * +   * Avoid comments beginning `@` symbols in IE because they are part of its +   * non-standard conditional compilation support. +   * http://msdn.microsoft.com/en-us/library/121hztk3(v=vs.94).aspx +   */    try { -    // The JS engine in Adobe products, like InDesign, will throw a syntax error -    // when it encounters a single line comment beginning with the `@` symbol. -    // The JS engine in Narwhal will generate the function `function anonymous(){//}` -    // and throw a syntax error. In IE, `@` symbols are part of its non-standard -    // conditional compilation support. The `@cc_on` statement activates its support -    // while the trailing ` !` induces a syntax error to exlude it. Compatibility -    // modes in IE > 8 require a space before the `!` to induce a syntax error. -    // See http://msdn.microsoft.com/en-us/library/121hztk3(v=vs.94).aspx -    var useSourceURL = (Function('//@cc_on !')(), true); -  } catch(e){ } +    var useSourceURL = (Function('//@')(), !window.attachEvent); +  } catch(e) { }    /** Used to identify object classifications that are array-like */    var arrayLikeClasses = {}; @@ -201,19 +206,6 @@    cloneableClasses[numberClass] = cloneableClasses[objectClass] = cloneableClasses[regexpClass] =    cloneableClasses[stringClass] = true; -  /** -   * Used to escape characters for inclusion in HTML. -   * The `>` and `/` characters don't require escaping in HTML and have no -   * special meaning unless they're part of a tag or an unquoted attribute value -   * http://mathiasbynens.be/notes/ambiguous-ampersands (semi-related fun fact) -   */ -  var htmlEscapes = { -    '&': '&', -    '<': '<', -    '"': '"', -    "'": ''' -  }; -    /** Used to determine if values are of the language type Object */    var objectTypes = {      'boolean': false, @@ -243,27 +235,19 @@     *     * @name _     * @constructor -   * @param {Mixed} value The value to wrap in a `LoDash` instance. -   * @returns {Object} Returns a `LoDash` instance. +   * @param {Mixed} value The value to wrap in a `lodash` instance. +   * @returns {Object} Returns a `lodash` instance.     */    function lodash(value) { -    // allow invoking `lodash` without the `new` operator -    return new LoDash(value); -  } - -  /** -   * Creates a `LoDash` instance that wraps a value to allow chaining. -   * -   * @private -   * @constructor -   * @param {Mixed} value The value to wrap. -   */ -  function LoDash(value) {      // exit early if already wrapped -    if (value && value._wrapped) { +    if (value && value.__wrapped__) {        return value;      } -    this._wrapped = value; +    // allow invoking `lodash` without the `new` operator +    if (!(this instanceof lodash)) { +      return new lodash(value); +    } +    this.__wrapped__ = value;    }    /** @@ -331,15 +315,13 @@      'var index, value, iteratee = <%= firstArg %>, ' +      // assign the `result` variable an initial value      'result<% if (init) { %> = <%= init %><% } %>;\n' + -    // add code to exit early or do so if the first argument is falsey -    '<%= exit %>;\n' + -    // add code after the exit snippet but before the iteration branches +    // add code before the iteration branches      '<%= top %>;\n' +      // the following branch is for iterating arrays and array-like objects      '<% if (arrayBranch) { %>' +      'var length = iteratee.length; index = -1;' + -    '  <% if (objectBranch) { %>\nif (length > -1 && length === length >>> 0) {<% } %>' + +    '  <% if (objectBranch) { %>\nif (length === +length) {<% } %>' +      // add support for accessing string characters by index if needed      '  <% if (noCharByIndex) { %>\n' + @@ -371,6 +353,12 @@      '  } else {' +      '  <% } %>' + +    // Firefox < 3.6, Opera > 9.50 - Opera < 11.60, and Safari < 5.1 +    // (if the prototype or a property on the prototype has been set) +    // incorrectly sets a function's `prototype` property [[Enumerable]] +    // value to `true`. Because of this Lo-Dash standardizes on skipping +    // the the `prototype` property of functions regardless of its +    // [[Enumerable]] value.      '  <% if (!hasDontEnumBug) { %>\n' +      '  var skipProto = typeof iteratee == \'function\' && \n' +      '    propertyIsEnumerable.call(iteratee, \'prototype\');\n' + @@ -393,26 +381,16 @@      // else using a for-in loop      '  <% } else { %>\n' +      '  <%= objectBranch.beforeLoop %>;\n' + -    '  for (index in iteratee) {' + -    '    <% if (hasDontEnumBug) { %>\n' + -    '    <%   if (useHas) { %>if (hasOwnProperty.call(iteratee, index)) {\n  <% } %>' + -    '    value = iteratee[index];\n' + -    '    <%= objectBranch.inLoop %>;\n' + -    '    <%   if (useHas) { %>}<% } %>' + - -    // Firefox < 3.6, Opera > 9.50 - Opera < 11.60, and Safari < 5.1 -    // (if the prototype or a property on the prototype has been set) -    // incorrectly sets a function's `prototype` property [[Enumerable]] -    // value to `true`. Because of this Lo-Dash standardizes on skipping -    // the the `prototype` property of functions regardless of its -    // [[Enumerable]] value. -    '    <% } else { %>\n' + -    '    if (!(skipProto && index == \'prototype\')<% if (useHas) { %> &&\n' + -    '        hasOwnProperty.call(iteratee, index)<% } %>) {\n' + -    '      value = iteratee[index];\n' + -    '      <%= objectBranch.inLoop %>\n' + -    '    }' + +    '  for (index in iteratee) {<%' + +    '    if (!hasDontEnumBug || useHas) { %>\n    if (<%' + +    '      if (!hasDontEnumBug) { %>!(skipProto && index == \'prototype\')<% }' + +    '      if (!hasDontEnumBug && useHas) { %> && <% }' + +    '      if (useHas) { %>hasOwnProperty.call(iteratee, index)<% }' + +    '    %>) {' +      '    <% } %>\n' + +    '    value = iteratee[index];\n' + +    '    <%= objectBranch.inLoop %>;' + +    '    <% if (!hasDontEnumBug || useHas) { %>\n    }<% } %>\n' +      '  }' +      '  <% } %>' + @@ -444,36 +422,22 @@    /**     * Reusable iterator options shared by -   * `every`, `filter`, `find`, `forEach`, `forIn`, `forOwn`, `groupBy`, `map`, -   * `reject`, `some`, and `sortBy`. +   * `countBy`, `every`, `filter`, `find`, `forEach`, `forIn`, `forOwn`, `groupBy`, +   * `map`, `reject`, `some`, and `sortBy`.     */    var baseIteratorOptions = {      'args': 'collection, callback, thisArg',      'init': 'collection', -    'top': -      'if (!callback) {\n' + -      '  callback = identity\n' + -      '}\n' + -      'else if (thisArg) {\n' + -      '  callback = iteratorBind(callback, thisArg)\n' + -      '}', +    'top': 'callback = createCallback(callback, thisArg)',      'inLoop': 'if (callback(value, index, collection) === false) return result'    };    /** Reusable iterator options for `countBy`, `groupBy`, and `sortBy` */    var countByIteratorOptions = {      'init': '{}', -    'top': -      'var prop;\n' + -      'if (typeof callback != \'function\') {\n' + -      '  var valueProp = callback;\n' + -      '  callback = function(value) { return value[valueProp] }\n' + -      '}\n' + -      'else if (thisArg) {\n' + -      '  callback = iteratorBind(callback, thisArg)\n' + -      '}', +    'top': 'callback = createCallback(callback, thisArg)',      'inLoop': -      'prop = callback(value, index, collection);\n' + +      'var prop = callback(value, index, collection);\n' +        '(hasOwnProperty.call(result, prop) ? result[prop]++ : result[prop] = 1)'    }; @@ -504,7 +468,7 @@    /** Reusable iterator options for `find`, `forEach`, `forIn`, and `forOwn` */    var forEachIteratorOptions = { -    'top': 'if (thisArg) callback = iteratorBind(callback, thisArg)' +    'top': 'callback = createCallback(callback, thisArg)'    };    /** Reusable iterator options for `forIn` and `forOwn` */ @@ -517,7 +481,6 @@    /** Reusable iterator options for `invoke`, `map`, `pluck`, and `sortBy` */    var mapIteratorOptions = {      'init': '', -    'exit': 'if (!collection) return []',      'beforeLoop': {        'array':  'result = Array(length)',        'object': 'result = ' + (isKeysFast ? 'Array(length)' : '[]') @@ -528,10 +491,26 @@      }    }; +  /** Reusable iterator options for `omit` and `pick` */ +  var omitIteratorOptions = { +    'useHas': false, +    'args': 'object, callback, thisArg', +    'init': '{}', +    'top': +      'var isFunc = typeof callback == \'function\';\n' + +      'if (isFunc) callback = createCallback(callback, thisArg);\n' + +      'else var props = concat.apply(ArrayProto, arguments)', +    'inLoop': +      'if (isFunc\n' + +      '  ? !callback(value, index, object)\n' + +      '  : indexOf(props, index) < 0\n' + +      ') result[index] = value' +  }; +    /*--------------------------------------------------------------------------*/    /** -   * Creates a new function optimized for searching large arrays for a given `value`, +   * Creates a function optimized for searching large arrays for a given `value`,     * starting at `fromIndex`, using strict equality for comparisons, i.e. `===`.     *     * @private @@ -545,7 +524,7 @@      fromIndex || (fromIndex = 0);      var length = array.length, -        isLarge = (length - fromIndex) >= (largeSize || 30), +        isLarge = (length - fromIndex) >= (largeSize || largeArraySize),          cache = isLarge ? {} : array;      if (isLarge) { @@ -570,6 +549,114 @@    }    /** +   * Used by `sortBy` to compare transformed `collection` values, stable sorting +   * them in ascending order. +   * +   * @private +   * @param {Object} a The object to compare to `b`. +   * @param {Object} b The object to compare to `a`. +   * @returns {Number} Returns the sort order indicator of `1` or `-1`. +   */ +  function compareAscending(a, b) { +    var ai = a.index, +        bi = b.index; + +    a = a.criteria; +    b = b.criteria; + +    // ensure a stable sort in V8 and other engines +    // http://code.google.com/p/v8/issues/detail?id=90 +    if (a !== b) { +      if (a > b || a === undefined) { +        return 1; +      } +      if (a < b || b === undefined) { +        return -1; +      } +    } +    return ai < bi ? -1 : 1; +  } + +  /** +   * Creates a function that, when called, invokes `func` with the `this` +   * binding of `thisArg` and prepends any `partailArgs` to the arguments passed +   * to the bound function. +   * +   * @private +   * @param {Function|String} func The function to bind or the method name. +   * @param {Mixed} [thisArg] The `this` binding of `func`. +   * @param {Array} partialArgs An array of arguments to be partially applied. +   * @returns {Function} Returns the new bound function. +   */ +  function createBound(func, thisArg, partialArgs) { +    var isFunc = isFunction(func), +        isPartial = !partialArgs, +        methodName = func; + +    // juggle arguments +    if (isPartial) { +      partialArgs = thisArg; +    } + +    function bound() { +      // `Function#bind` spec +      // http://es5.github.com/#x15.3.4.5 +      var args = arguments, +          thisBinding = isPartial ? this : thisArg; + +      if (!isFunc) { +        func = thisArg[methodName]; +      } +      if (partialArgs.length) { +        args = args.length +          ? partialArgs.concat(slice.call(args)) +          : partialArgs; +      } +      if (this instanceof bound) { +        // get `func` instance if `bound` is invoked in a `new` expression +        noop.prototype = func.prototype; +        thisBinding = new noop; + +        // mimic the constructor's `return` behavior +        // http://es5.github.com/#x13.2.2 +        var result = func.apply(thisBinding, args); +        return result && objectTypes[typeof result] +          ? result +          : thisBinding +      } +      return func.apply(thisBinding, args); +    } +    return bound; +  } + +  /** +   * Produces an iteration callback bound to an optional `thisArg`. If `func` is +   * a property name, the callback will return the property value for a given element. +   * +   * @private +   * @param {Function|String} [func=identity|property] The function called per +   * iteration or property name to query. +   * @param {Mixed} [thisArg] The `this` binding of `callback`. +   * @returns {Function} Returns a callback function. +   */ +  function createCallback(func, thisArg) { +    if (!func) { +      return identity; +    } +    if (typeof func != 'function') { +      return function(object) { +        return object[func]; +      }; +    } +    if (thisArg !== undefined) { +      return function(value, index, object) { +        return func.call(thisArg, value, index, object); +      }; +    } +    return func; +  } + +  /**     * Creates compiled iteration functions. The iteration function will be created     * to iterate over only objects if the first argument of `options.args` is     * "object" or `options.inLoop.array` is falsey. @@ -588,11 +675,7 @@     *     *  init - A string to specify the initial value of the `result` variable.     * -   *  exit - A string of code to use in place of the default exit-early check -   *   of `if (!arguments[0]) return result`. -   * -   *  top - A string of code to execute after the exit-early check but before -   *   the iteration branches. +   *  top - A string of code to execute before the iteration branches.     *     *  beforeLoop - A string or object containing an "array" or "object" property     *   of code to execute before the array or object loops. @@ -615,8 +698,6 @@      // merge options into a template data object      var data = {        'bottom': '', -      'exit': '', -      'init': '',        'top': '',        'arrayBranch': { 'beforeLoop': '' },        'objectBranch': { 'beforeLoop': '' } @@ -631,8 +712,8 @@            if (typeof value == 'string') {              value = { 'array': value, 'object': value };            } -          data.arrayBranch[prop] = value.array; -          data.objectBranch[prop] = value.object; +          data.arrayBranch[prop] = value.array || ''; +          data.objectBranch[prop] = value.object || '';          } else {            data[prop] = value;          } @@ -640,83 +721,44 @@      }      // set additional template `data` values      var args = data.args, -        firstArg = /^[^,]+/.exec(args)[0]; +        firstArg = /^[^,]+/.exec(args)[0], +        init = data.init, +        useStrict = data.useStrict;      data.firstArg = firstArg;      data.hasDontEnumBug = hasDontEnumBug; +    data.init = init == null ? firstArg : init;      data.isKeysFast = isKeysFast;      data.noArgsEnum = noArgsEnum;      data.shadowed = shadowed;      data.useHas = data.useHas !== false; -    data.useStrict = data.useStrict !== false; +    data.useStrict = useStrict == null ? isStrictFast : useStrict; -    if (!('noCharByIndex' in data)) { +    if (data.noCharByIndex == null) {        data.noCharByIndex = noCharByIndex;      } -    if (!data.exit) { -      data.exit = 'if (!' + firstArg + ') return result'; -    }      if (firstArg != 'collection' || !data.arrayBranch.inLoop) {        data.arrayBranch = null;      }      // create the function factory      var factory = Function( -        'arrayLikeClasses, ArrayProto, bind, compareAscending, concat, forIn, ' + -        'hasOwnProperty, identity, indexOf, isArguments, isArray, isFunction, ' + -        'isPlainObject, iteratorBind, objectClass, objectTypes, nativeKeys, ' + -        'propertyIsEnumerable, slice, stringClass, toString', +        'arrayLikeClasses, ArrayProto, bind, compareAscending, concat, createCallback, ' + +        'forIn, hasOwnProperty, identity, indexOf, isArguments, isArray, isFunction, ' + +        'isPlainObject, objectClass, objectTypes, nativeKeys, propertyIsEnumerable, ' + +        'slice, stringClass, toString, undefined',        'var callee = function(' + args + ') {\n' + iteratorTemplate(data) + '\n};\n' +        'return callee'      );      // return the compiled function      return factory( -      arrayLikeClasses, ArrayProto, bind, compareAscending, concat, forIn, -      hasOwnProperty, identity, indexOf, isArguments, isArray, isFunction, -      isPlainObject, iteratorBind, objectClass, objectTypes, nativeKeys, -      propertyIsEnumerable, slice, stringClass, toString +      arrayLikeClasses, ArrayProto, bind, compareAscending, concat, createCallback, +      forIn, hasOwnProperty, identity, indexOf, isArguments, isArray, isFunction, +      isPlainObject, objectClass, objectTypes, nativeKeys, propertyIsEnumerable, +      slice, stringClass, toString      );    }    /** -   * Used by `sortBy` to compare transformed `collection` values, stable sorting -   * them in ascending order. -   * -   * @private -   * @param {Object} a The object to compare to `b`. -   * @param {Object} b The object to compare to `a`. -   * @returns {Number} Returns the sort order indicator of `1` or `-1`. -   */ -  function compareAscending(a, b) { -    var ai = a.index, -        bi = b.index; - -    a = a.criteria; -    b = b.criteria; - -    if (a === undefined) { -      return 1; -    } -    if (b === undefined) { -      return -1; -    } -    // ensure a stable sort in V8 and other engines -    // http://code.google.com/p/v8/issues/detail?id=90 -    return a < b ? -1 : a > b ? 1 : ai < bi ? -1 : 1; -  } - -  /** -   * Used by `template` to replace tokens with their corresponding code snippets. -   * -   * @private -   * @param {String} match The matched token. -   * @param {String} index The `tokenized` index of the code snippet. -   * @returns {String} Returns the code snippet. -   */ -  function detokenize(match, index) { -    return tokenized[index]; -  } - -  /**     * Used by `template` to escape characters for inclusion in compiled     * string literals.     * @@ -729,7 +771,7 @@    }    /** -   * Used by `escape` to escape characters for inclusion in HTML. +   * Used by `escape` to convert characters to HTML entities.     *     * @private     * @param {String} match The matched character to escape. @@ -740,66 +782,6 @@    }    /** -   * Checks if a given `value` is an object created by the `Object` constructor -   * assuming objects created by the `Object` constructor have no inherited -   * enumerable properties and that there are no `Object.prototype` extensions. -   * -   * @private -   * @param {Mixed} value The value to check. -   * @param {Boolean} [skipArgsCheck=false] Internally used to skip checks for -   *  `arguments` objects. -   * @returns {Boolean} Returns `true` if the `value` is a plain `Object` object, -   *  else `false`. -   */ -  function isPlainObject(value, skipArgsCheck) { -    // avoid non-objects and false positives for `arguments` objects -    var result = false; -    if (!(value && typeof value == 'object') || (!skipArgsCheck && isArguments(value))) { -      return result; -    } -    // IE < 9 presents DOM nodes as `Object` objects except they have `toString` -    // methods that are `typeof` "string" and still can coerce nodes to strings. -    // Also check that the constructor is `Object` (i.e. `Object instanceof Object`) -    var ctor = value.constructor; -    if ((!noNodeClass || !(typeof value.toString != 'function' && typeof (value + '') == 'string')) && -        (!isFunction(ctor) || ctor instanceof ctor)) { -      // IE < 9 iterates inherited properties before own properties. If the first -      // iterated property is an object's own property then there are no inherited -      // enumerable properties. -      if (iteratesOwnLast) { -        forIn(value, function(objValue, objKey) { -          result = !hasOwnProperty.call(value, objKey); -          return false; -        }); -        return result === false; -      } -      // In most environments an object's own properties are iterated before -      // its inherited properties. If the last iterated property is an object's -      // own property then there are no inherited enumerable properties. -      forIn(value, function(objValue, objKey) { -        result = objKey; -      }); -      return result === false || hasOwnProperty.call(value, result); -    } -    return result; -  } - -  /** -   * Creates a new function that, when called, invokes `func` with the `this` -   * binding of `thisArg` and the arguments (value, index, object). -   * -   * @private -   * @param {Function} func The function to bind. -   * @param {Mixed} [thisArg] The `this` binding of `func`. -   * @returns {Function} Returns the new bound function. -   */ -  function iteratorBind(func, thisArg) { -    return function(value, index, object) { -      return func.call(thisArg, value, index, object); -    }; -  } - -  /**     * A no-operation function.     *     * @private @@ -809,62 +791,36 @@    }    /** -   * Used by `template` to replace "escape" template delimiters with tokens. +   * Used by `unescape` to convert HTML entities to characters.     *     * @private -   * @param {String} match The matched template delimiter. -   * @param {String} value The delimiter value. -   * @returns {String} Returns a token. +   * @param {String} match The matched character to unescape. +   * @returns {String} Returns the unescaped character.     */ -  function tokenizeEscape(match, value) { -    if (match && reComplexDelimiter.test(value)) { -      return '<e%-' + value + '%>'; -    } -    var index = tokenized.length; -    tokenized[index] = "' +\n__e(" + value + ") +\n'"; -    return token + index; +  function unescapeHtmlChar(match) { +    return htmlUnescapes[match];    } -  /** -   * Used by `template` to replace "evaluate" template delimiters, or complex -   * "escape" and "interpolate" delimiters, with tokens. -   * -   * @private -   * @param {String} match The matched template delimiter. -   * @param {String} escapeValue The complex "escape" delimiter value. -   * @param {String} interpolateValue The complex "interpolate" delimiter value. -   * @param {String} [evaluateValue] The "evaluate" delimiter value. -   * @returns {String} Returns a token. -   */ -  function tokenizeEvaluate(match, escapeValue, interpolateValue, evaluateValue) { -    if (evaluateValue) { -      var index = tokenized.length; -      tokenized[index] = "';\n" + evaluateValue + ";\n__p += '"; -      return token + index; -    } -    return escapeValue -      ? tokenizeEscape(null, escapeValue) -      : tokenizeInterpolate(null, interpolateValue); -  } +  /*--------------------------------------------------------------------------*/    /** -   * Used by `template` to replace "interpolate" template delimiters with tokens. +   * Creates an object composed of the inverted keys and values of the given `object`.     * -   * @private -   * @param {String} match The matched template delimiter. -   * @param {String} value The delimiter value. -   * @returns {String} Returns a token. +   * @static +   * @memberOf _ +   * @category Objects +   * @param {Object} object The object to invert. +   * @returns {Object} Returns the created inverted object. +   * @example +   * +   *  _.invert({ 'first': 'Moe', 'second': 'Larry', 'third': 'Curly' }); +   * // => { 'Moe': 'first', 'Larry': 'second', 'Curly': 'third' } (order is not guaranteed)     */ -  function tokenizeInterpolate(match, value) { -    if (match && reComplexDelimiter.test(value)) { -      return '<e%=' + value + '%>'; -    } -    var index = tokenized.length; -    tokenized[index] = "' +\n((__t = (" + value + ")) == null ? '' : __t) +\n'"; -    return token + index; -  } - -  /*--------------------------------------------------------------------------*/ +  var invert = createIterator({ +    'args': 'object', +    'init': '{}', +    'inLoop': 'result[value] = index' +  });    /**     * Checks if `value` is an `arguments` object. @@ -922,7 +878,7 @@     * @returns {Boolean} Returns `true` if the `value` is a function, else `false`.     * @example     * -   * _.isFunction(''.concat); +   * _.isFunction(_);     * // => true     */    function isFunction(value) { @@ -936,6 +892,85 @@    }    /** +   * Checks if a given `value` is an object created by the `Object` constructor. +   * +   * @static +   * @memberOf _ +   * @category Objects +   * @param {Mixed} value The value to check. +   * @returns {Boolean} Returns `true` if `value` is a plain object, else `false`. +   * @example +   * +   * function Stooge(name, age) { +   *   this.name = name; +   *   this.age = age; +   * } +   * +   * _.isPlainObject(new Stooge('moe', 40)); +   * // false +   * +   * _.isPlainObject([1, 2, 3]); +   * // false +   * +   * _.isPlainObject({ 'name': 'moe', 'age': 40 }); +   * // => true +   */ +  var isPlainObject = !nativeGetPrototypeOf ? isPlainFallback : function(value) { +    if (!(value && typeof value == 'object')) { +      return false; +    } +    var valueOf = value.valueOf, +        objProto = typeof valueOf == 'function' && (objProto = nativeGetPrototypeOf(valueOf)) && nativeGetPrototypeOf(objProto); + +    return objProto +      ? value == objProto || (nativeGetPrototypeOf(value) == objProto && !isArguments(value)) +      : isPlainFallback(value); +  }; + +  /** +   * A fallback implementation of `isPlainObject` that checks if a given `value` +   * is an object created by the `Object` constructor, assuming objects created +   * by the `Object` constructor have no inherited enumerable properties and that +   * there are no `Object.prototype` extensions. +   * +   * @private +   * @param {Mixed} value The value to check. +   * @returns {Boolean} Returns `true` if `value` is a plain object, else `false`. +   */ +  function isPlainFallback(value) { +    // avoid non-objects and false positives for `arguments` objects +    var result = false; +    if (!(value && typeof value == 'object') || isArguments(value)) { +      return result; +    } +    // IE < 9 presents DOM nodes as `Object` objects except they have `toString` +    // methods that are `typeof` "string" and still can coerce nodes to strings. +    // Also check that the constructor is `Object` (i.e. `Object instanceof Object`) +    var ctor = value.constructor; +    if ((!noNodeClass || !(typeof value.toString != 'function' && typeof (value + '') == 'string')) && +        (!isFunction(ctor) || ctor instanceof ctor)) { +      // IE < 9 iterates inherited properties before own properties. If the first +      // iterated property is an object's own property then there are no inherited +      // enumerable properties. +      if (iteratesOwnLast) { +        forIn(value, function(value, key, object) { +          result = !hasOwnProperty.call(object, key); +          return false; +        }); +        return result === false; +      } +      // In most environments an object's own properties are iterated before +      // its inherited properties. If the last iterated property is an object's +      // own property then there are no inherited enumerable properties. +      forIn(value, function(value, key) { +        result = key; +      }); +      return result === false || hasOwnProperty.call(value, result); +    } +    return result; +  } + +  /**     * A shim implementation of `Object.keys` that produces an array of the given     * object's own enumerable property names.     * @@ -949,26 +984,43 @@      'inLoop': 'result.push(index)'    }); +  /** +   * Used to convert characters to HTML entities: +   * +   * Though the `>` character is escaped for symmetry, characters like `>` and `/` +   * don't require escaping in HTML and have no special meaning unless they're part +   * of a tag or an unquoted attribute value. +   * http://mathiasbynens.be/notes/ambiguous-ampersands (under "semi-related fun fact") +   */ +  var htmlEscapes = { +    '&': '&', +    '<': '<', +    '>': '>', +    '"': '"', +    "'": ''' +  }; + +  /** Used to convert HTML entities to characters */ +  var htmlUnescapes = invert(htmlEscapes); +    /*--------------------------------------------------------------------------*/    /**     * Creates a clone of `value`. If `deep` is `true`, all nested objects will -   * also be cloned otherwise they will be assigned by reference. If a value has -   * a `clone` method it will be used to perform the clone. Functions, DOM nodes, -   * `arguments` objects, and objects created by constructors other than `Object` -   * are **not** cloned unless they have a custom `clone` method. +   * also be cloned otherwise they will be assigned by reference. Functions, DOM +   * nodes, `arguments` objects, and objects created by constructors other than +   * `Object` are **not** cloned.     *     * @static     * @memberOf _     * @category Objects     * @param {Mixed} value The value to clone.     * @param {Boolean} deep A flag to indicate a deep clone. -   * @param {Object} [guard] Internally used to allow this method to work with +   * @param- {Object} [guard] Internally used to allow this method to work with     *  others like `_.map` without using their callback `index` argument for `deep`. -   * @param {Array} [stack=[]] Internally used to keep track of traversed objects -   *  to avoid circular references. -   * @param {Object} thorough Internally used to indicate whether or not to perform -   *  a more thorough clone of non-object values. +   * @param- {Array} [stackA=[]] Internally used to track traversed source objects. +   * @param- {Array} [stackB=[]] Internally used to associate clones with their +   *  source counterparts.     * @returns {Mixed} Returns the cloned `value`.     * @example     * @@ -989,26 +1041,15 @@     * shallow[0] === stooges[0];     * // => false     */ -  function clone(value, deep, guard, stack, thorough) { +  function clone(value, deep, guard, stackA, stackB) {      if (value == null) {        return value;      }      if (guard) {        deep = false;      } -    // avoid slower checks on primitives -    thorough || (thorough = { 'value': null }); -    if (thorough.value == null) { -      // primitives passed from iframes use the primary document's native prototypes -      thorough.value = !!(BoolProto.clone || NumberProto.clone || StringProto.clone); -    } -    // use custom `clone` method if available -    var isObj = objectTypes[typeof value]; -    if ((isObj || thorough.value) && value.clone && isFunction(value.clone)) { -      thorough.value = null; -      return value.clone(deep); -    }      // inspect [[Class]] +    var isObj = objectTypes[typeof value];      if (isObj) {        // don't clone `arguments` objects, functions, or non-object Objects        var className = toString.call(value); @@ -1016,7 +1057,7 @@          return value;        }        var isArr = className == arrayClass; -      isObj = isArr || (className == objectClass ? isPlainObject(value, true) : isObj); +      isObj = isArr || (className == objectClass ? isPlainObject(value) : isObj);      }      // shallow clone      if (!isObj || !deep) { @@ -1043,30 +1084,33 @@      }      // check for circular references and return corresponding clone -    stack || (stack = []); -    var length = stack.length; +    stackA || (stackA = []); +    stackB || (stackB = []); + +    var length = stackA.length;      while (length--) { -      if (stack[length].source == value) { -        return stack[length].value; +      if (stackA[length] == value) { +        return stackB[length];        }      }      // init cloned object -    length = value.length; -    var result = isArr ? ctor(length) : {}; +    var result = isArr ? ctor(length = value.length) : {}; -    // add current clone and original source value to the stack of traversed objects -    stack.push({ 'value': result, 'source': value }); +    // add the source value to the stack of traversed objects +    // and associate it with its clone +    stackA.push(value); +    stackB.push(result);      // recursively populate clone (susceptible to call stack limits)      if (isArr) {        var index = -1;        while (++index < length) { -        result[index] = clone(value[index], deep, null, stack, thorough); +        result[index] = clone(value[index], deep, null, stackA, stackB);        }      } else {        forOwn(value, function(objValue, key) { -        result[key] = clone(objValue, deep, null, stack, thorough); +        result[key] = clone(objValue, deep, null, stackA, stackB);        });      }      return result; @@ -1095,30 +1139,6 @@    });    /** -   * Creates a shallow clone of `object` excluding the specified properties. -   * Property names may be specified as individual arguments or as arrays of -   * property names. -   * -   * @static -   * @memberOf _ -   * @category Objects -   * @param {Object} object The source object. -   * @param {Object} [prop1, prop2, ...] The properties to drop. -   * @returns {Object} Returns an object without the dropped properties. -   * @example -   * -   * _.drop({ 'name': 'moe', 'age': 40, 'userid': 'moe1' }, 'userid'); -   * // => { 'name': 'moe', 'age': 40 } -   */ -  var drop = createIterator({ -    'useHas': false, -    'args': 'object', -    'init': '{}', -    'top': 'var props = concat.apply(ArrayProto, arguments)', -    'inLoop': 'if (indexOf(props, index) < 0) result[index] = value' -  }); - -  /**     * Assigns enumerable properties of the source object(s) to the `destination`     * object. Subsequent sources will overwrite propery assignments of previous     * sources. @@ -1139,7 +1159,7 @@    /**     * Iterates over `object`'s own and inherited enumerable properties, executing     * the `callback` for each property. The `callback` is bound to `thisArg` and -   * invoked with 3 arguments; (value, key, object). Callbacks may exit iteration +   * invoked with three arguments; (value, key, object). Callbacks may exit iteration     * early by explicitly returning `false`.     *     * @static @@ -1147,7 +1167,7 @@     * @category Objects     * @param {Object} object The object to iterate over.     * @param {Function} callback The function called per iteration. -   * @param {Mixed} [thisArg] The `this` binding for the callback. +   * @param {Mixed} [thisArg] The `this` binding of `callback`.     * @returns {Object} Returns `object`.     * @example     * @@ -1170,16 +1190,16 @@    /**     * Iterates over `object`'s own enumerable properties, executing the `callback` -   * for each property. The `callback` is bound to `thisArg` and invoked with 3 -   * arguments; (value, key, object). Callbacks may exit iteration early by -   * explicitly returning `false`. +   * for each property. The `callback` is bound to `thisArg` and invoked with three +   * arguments; (value, key, object). Callbacks may exit iteration early by explicitly +   * returning `false`.     *     * @static     * @memberOf _     * @category Objects     * @param {Object} object The object to iterate over.     * @param {Function} callback The function called per iteration. -   * @param {Mixed} [thisArg] The `this` binding for the callback. +   * @param {Mixed} [thisArg] The `this` binding of `callback`.     * @returns {Object} Returns `object`.     * @example     * @@ -1229,7 +1249,7 @@     * // => true     */    function has(object, property) { -    return object ? hasOwnProperty.call(object, property) : false; +    return hasOwnProperty.call(object, property);    }    /** @@ -1308,11 +1328,12 @@      'args': 'value',      'init': 'true',      'top': +      'if (!value) return result;\n' +        'var className = toString.call(value),\n' +        '    length = value.length;\n' +        'if (arrayLikeClasses[className]' +        (noArgsClass ? ' || isArguments(value)' : '') + ' ||\n' + -      '  (className == objectClass && length > -1 && length === length >>> 0 &&\n' + +      '  (className == objectClass && length === +length &&\n' +        '  isFunction(value.splice))' +        ') return !length',      'inLoop': { @@ -1322,18 +1343,15 @@    /**     * Performs a deep comparison between two values to determine if they are -   * equivalent to each other. If a value has an `isEqual` method it will be -   * used to perform the comparison. +   * equivalent to each other.     *     * @static     * @memberOf _     * @category Objects     * @param {Mixed} a The value to compare.     * @param {Mixed} b The other value to compare. -   * @param {Array} [stack=[]] Internally used to keep track of traversed objects -   *  to avoid circular references. -   * @param {Object} thorough Internally used to indicate whether or not to perform -   *  a more thorough comparison of non-object values. +   * @param- {Object} [stackA=[]] Internally used track traversed `a` objects. +   * @param- {Object} [stackB=[]] Internally used track traversed `b` objects.     * @returns {Boolean} Returns `true` if the values are equvalent, else `false`.     * @example     * @@ -1346,40 +1364,21 @@     * _.isEqual(moe, clone);     * // => true     */ -  function isEqual(a, b, stack, thorough) { +  function isEqual(a, b, stackA, stackB) {      // a strict comparison is necessary because `null == undefined`      if (a == null || b == null) {        return a === b;      } -    // avoid slower checks on non-objects -    thorough || (thorough = { 'value': null }); -    if (thorough.value == null) { -      // primitives passed from iframes use the primary document's native prototypes -      thorough.value = !!(BoolProto.isEqual || NumberProto.isEqual || StringProto.isEqual); -    } -    if (objectTypes[typeof a] || objectTypes[typeof b] || thorough.value) { -      // unwrap any LoDash wrapped values -      if (a._chain) { -        a = a._wrapped; -      } -      if (b._chain) { -        b = b._wrapped; -      } -      // use custom `isEqual` method if available -      if (a.isEqual && isFunction(a.isEqual)) { -        thorough.value = null; -        return a.isEqual(b); -      } -      if (b.isEqual && isFunction(b.isEqual)) { -        thorough.value = null; -        return b.isEqual(a); -      } -    }      // exit early for identical values      if (a === b) {        // treat `+0` vs. `-0` as not equal        return a !== 0 || (1 / a == 1 / b);      } +    // unwrap any `lodash` wrapped values +    if (objectTypes[typeof a] || objectTypes[typeof b]) { +      a = a.__wrapped__ || a; +      b = b.__wrapped__ || b; +    }      // compare [[Class]] names      var className = toString.call(a);      if (className != toString.call(b)) { @@ -1420,11 +1419,13 @@      // assume cyclic structures are equal      // the algorithm for detecting cyclic structures is adapted from ES 5.1      // section 15.12.3, abstract operation `JO` (http://es5.github.com/#x15.12.3) -    stack || (stack = []); -    var length = stack.length; +    stackA || (stackA = []); +    stackB || (stackB = []); + +    var length = stackA.length;      while (length--) { -      if (stack[length] == a) { -        return true; +      if (stackA[length] == a) { +        return stackB[length] == b;        }      } @@ -1432,8 +1433,9 @@          result = true,          size = 0; -    // add `a` to the stack of traversed objects -    stack.push(a); +    // add `a` and `b` to the stack of traversed objects +    stackA.push(a); +    stackB.push(b);      // recursively compare objects and arrays (susceptible to call stack limits)      if (isArr) { @@ -1444,7 +1446,7 @@        if (result) {          // deep compare the contents, ignoring non-numeric properties          while (size--) { -          if (!(result = isEqual(a[size], b[size], stack, thorough))) { +          if (!(result = isEqual(a[size], b[size], stackA, stackB))) {              break;            }          } @@ -1468,7 +1470,7 @@          // count the number of properties.          size++;          // deep compare each property value. -        if (!(hasOwnProperty.call(b, prop) && isEqual(a[prop], b[prop], stack, thorough))) { +        if (!(hasOwnProperty.call(b, prop) && isEqual(a[prop], b[prop], stackA, stackB))) {            return false;          }        } @@ -1488,7 +1490,7 @@        while (++index < 7) {          prop = shadowed[index];          if (hasOwnProperty.call(a, prop) && -            !(hasOwnProperty.call(b, prop) && isEqual(a[prop], b[prop], stack, thorough))) { +            !(hasOwnProperty.call(b, prop) && isEqual(a[prop], b[prop], stackA, stackB))) {            return false;          }        } @@ -1537,6 +1539,9 @@     * _.isObject({});     * // => true     * +   * _.isObject([1, 2, 3]); +   * // => true +   *     * _.isObject(1);     * // => false     */ @@ -1611,7 +1616,7 @@     * @returns {Boolean} Returns `true` if the `value` is a number, else `false`.     * @example     * -   * _.isNumber(8.4 * 5; +   * _.isNumber(8.4 * 5);     * // => true     */    function isNumber(value) { @@ -1684,15 +1689,10 @@     * // => ['one', 'two', 'three'] (order is not guaranteed)     */    var keys = !nativeKeys ? shimKeys : function(object) { -    var type = typeof object; -      // avoid iterating over the `prototype` property -    if (type == 'function' && propertyIsEnumerable.call(object, 'prototype')) { -      return shimKeys(object); -    } -    return object && objectTypes[type] -      ? nativeKeys(object) -      : []; +    return typeof object == 'function' && propertyIsEnumerable.call(object, 'prototype') +      ? shimKeys(object) +      : nativeKeys(object);    };    /** @@ -1705,10 +1705,11 @@     * @category Objects     * @param {Object} object The destination object.     * @param {Object} [source1, source2, ...] The source objects. -   * @param {Object} [indicator] Internally used to indicate that the `stack` +   * @param- {Object} [indicator] Internally used to indicate that the `stack`     *  argument is an array of traversed objects instead of another source object. -   * @param {Array} [stack=[]] Internally used to keep track of traversed objects -   *  to avoid circular references. +   * @param- {Array} [stackA=[]] Internally used to track traversed source objects. +   * @param- {Array} [stackB=[]] Internally used to associate clones with their +   *  source counterparts.     * @returns {Object} Returns the destination object.     * @example     * @@ -1726,107 +1727,125 @@     * // => [{ 'name': 'moe', 'age': 40 }, { 'name': 'larry', 'age': 50 }]     */    var merge = createIterator(extendIteratorOptions, { -    'args': 'object, source, indicator, stack', +    'args': 'object, source, indicator',      'top': -      'var destValue, found, isArr, stackLength, recursive = indicator == isPlainObject;\n' + -      'if (!recursive) stack = [];\n' + -      'for (var argsIndex = 1, argsLength = recursive ? 2 : arguments.length; argsIndex < argsLength; argsIndex++) {\n' + -      '  if (iteratee = arguments[argsIndex]) {', +      'var isArr, args = arguments, argsIndex = 0;\n' + +      'if (indicator == compareAscending) {\n' + +      '  var argsLength = 2, stackA = args[3], stackB = args[4]\n' + +      '} else {\n' + +      '  var argsLength = args.length, stackA = [], stackB = []\n' + +      '}\n' + +      'while (++argsIndex < argsLength) {\n' + +      '  if (iteratee = args[argsIndex]) {',      'inLoop': -      'if (value && ((isArr = isArray(value)) || isPlainObject(value))) {\n' + -      '  found = false; stackLength = stack.length;\n' + +      'if ((source = value) && ((isArr = isArray(source)) || isPlainObject(source))) {\n' + +      '  var found = false, stackLength = stackA.length;\n' +        '  while (stackLength--) {\n' + -      '    if (found = stack[stackLength].source == value) break\n' + +      '    if (found = stackA[stackLength] == source) break\n' +        '  }\n' +        '  if (found) {\n' + -      '    result[index] = stack[stackLength].value\n' + +      '    result[index] = stackB[stackLength]\n' +        '  } else {\n' + -      '    destValue = (destValue = result[index]) && isArr\n' + -      '      ? (isArray(destValue) ? destValue : [])\n' + -      '      : (isPlainObject(destValue) ? destValue : {});\n' + -      '    stack.push({ value: destValue, source: value });\n' + -      '    result[index] = callee(destValue, value, isPlainObject, stack)\n' + +      '    stackA.push(source);\n' + +      '    stackB.push(value = (value = result[index]) && isArr\n' + +      '      ? (isArray(value) ? value : [])\n' + +      '      : (isPlainObject(value) ? value : {})\n' + +      '    );\n' + +      '    result[index] = callee(value, source, compareAscending, stackA, stackB)\n' +        '  }\n' + -      '} else if (value != null) {\n' + -      '  result[index] = value\n' + +      '} else if (source != null) {\n' + +      '  result[index] = source\n' +        '}'    });    /** -   * Creates a shallow clone of `object` composed of the specified properties. +   * Creates a shallow clone of `object` excluding the specified properties.     * Property names may be specified as individual arguments or as arrays of -   * property names. +   * property names. If `callback` is passed, it will be executed for each property +   * in the `object`, omitting the properties `callback` returns truthy for. The +   * `callback` is bound to `thisArg` and invoked with three arguments; (value, key, object).     *     * @static     * @memberOf _     * @category Objects     * @param {Object} object The source object. -   * @param {Object} [prop1, prop2, ...] The properties to pick. -   * @returns {Object} Returns an object composed of the picked properties. +   * @param {Function|String} callback|[prop1, prop2, ...] The properties to omit +   *  or the function called per iteration. +   * @param {Mixed} [thisArg] The `this` binding of `callback`. +   * @returns {Object} Returns an object without the omitted properties.     * @example     * -   * _.pick({ 'name': 'moe', 'age': 40, 'userid': 'moe1' }, 'name', 'age'); +   * _.omit({ 'name': 'moe', 'age': 40, 'userid': 'moe1' }, 'userid');     * // => { 'name': 'moe', 'age': 40 } +   * +   * _.omit({ 'name': 'moe', '_hint': 'knucklehead', '_seed': '96c4eb' }, function(value, key) { +   *   return key.charAt(0) == '_'; +   * }); +   * // => { 'name': 'moe' }     */ -  function pick(object) { -    var result = {}; -    if (!object) { -      return result; -    } -    var prop, -        index = 0, -        props = concat.apply(ArrayProto, arguments), -        length = props.length; - -    // start `index` at `1` to skip `object` -    while (++index < length) { -      prop = props[index]; -      if (prop in object) { -        result[prop] = object[prop]; -      } -    } -    return result; -  } +  var omit = createIterator(omitIteratorOptions);    /** -   * Gets the size of `value` by returning `value.length` if `value` is an -   * array, string, or `arguments` object. If `value` is an object, size is -   * determined by returning the number of own enumerable properties it has. +   * Creates a two dimensional array of the given object's key-value pairs, +   * i.e. `[[key1, value1], [key2, value2]]`.     * -   * @deprecated     * @static     * @memberOf _     * @category Objects -   * @param {Array|Object|String} value The value to inspect. -   * @returns {Number} Returns `value.length` or number of own enumerable properties. +   * @param {Object} object The object to inspect. +   * @returns {Array} Returns new array of key-value pairs.     * @example     * -   * _.size([1, 2]); -   * // => 2 +   * _.pairs({ 'moe': 30, 'larry': 40, 'curly': 50 }); +   * // => [['moe', 30], ['larry', 40], ['curly', 50]] (order is not guaranteed) +   */ +  var pairs = createIterator({ +    'args': 'object', +    'init':'[]', +    'inLoop': 'result'  + (isKeysFast ? '[ownIndex] = ' : '.push') + '([index, value])' +  }); + +  /** +   * Creates a shallow clone of `object` composed of the specified properties. +   * Property names may be specified as individual arguments or as arrays of +   * property names. If `callback` is passed, it will be executed for each property +   * in the `object`, picking the properties `callback` returns truthy for. The +   * `callback` is bound to `thisArg` and invoked with three arguments; (value, key, object).     * -   * _.size({ 'one': 1, 'two': 2, 'three': 3 }); -   * // => 3 +   * @static +   * @memberOf _ +   * @category Objects +   * @param {Object} object The source object. +   * @param {Function|String} callback|[prop1, prop2, ...] The properties to pick +   *  or the function called per iteration. +   * @param {Mixed} [thisArg] The `this` binding of `callback`. +   * @returns {Object} Returns an object composed of the picked properties. +   * @example     * -   * _.size('curly'); -   * // => 5 +   * _.pick({ 'name': 'moe', 'age': 40, 'userid': 'moe1' }, 'name', 'age'); +   * // => { 'name': 'moe', 'age': 40 } +   * +   * _.pick({ 'name': 'moe', '_hint': 'knucklehead', '_seed': '96c4eb' }, function(value, key) { +   *   return key.charAt(0) != '_'; +   * }); +   * // => { 'name': 'moe' }     */ -  function size(value) { -    if (!value) { -      return 0; -    } -    var className = toString.call(value), -        length = value.length; - -    // return `value.length` for `arguments` objects, arrays, strings, and DOM -    // query collections of libraries like jQuery and MooTools -    // http://code.google.com/p/fbug/source/browse/branches/firebug1.9/content/firebug/chrome/reps.js?r=12614#653 -    // http://trac.webkit.org/browser/trunk/Source/WebCore/inspector/InjectedScriptSource.js?rev=125186#L609 -    if (arrayLikeClasses[className] || (noArgsClass && isArguments(value)) || -        (className == objectClass && length > -1 && length === length >>> 0 && isFunction(value.splice))) { -      return length; -    } -    return keys(value).length; -  } +  var pick = createIterator(omitIteratorOptions, { +    'top': +      'if (typeof callback != \'function\') {\n' + +      '  var prop,\n' + +      '      props = concat.apply(ArrayProto, arguments),\n' + +      '      length = props.length;\n' + +      '  for (index = 1; index < length; index++) {\n' + +      '    prop = props[index];\n' + +      '    if (prop in object) result[prop] = object[prop]\n' + +      '  }\n' + +      '} else {\n' + +      '  callback = createCallback(callback, thisArg)', +    'inLoop': +      'if (callback(value, index, object)) result[index] = value', +    'bottom': '}' +  });    /**     * Creates an array composed of the own enumerable property values of `object`. @@ -1876,7 +1895,7 @@      'init': 'false',      'noCharByIndex': false,      'beforeLoop': { -      'array': 'if (toString.call(iteratee) == stringClass) return collection.indexOf(target) > -1' +      'array': 'if (toString.call(collection) == stringClass) return collection.indexOf(target) > -1'      },      'inLoop': 'if (value === target) return true'    }); @@ -1885,16 +1904,16 @@     * Creates an object composed of keys returned from running each element of     * `collection` through a `callback`. The corresponding value of each key is     * the number of times the key was returned by `callback`. The `callback` is -   * bound to `thisArg` and invoked with 3 arguments; (value, index|key, collection). +   * bound to `thisArg` and invoked with three arguments; (value, index|key, collection).     * The `callback` argument may also be the name of a property to count by (e.g. 'length').     *     * @static     * @memberOf _     * @category Collections     * @param {Array|Object|String} collection The collection to iterate over. -   * @param {Function|String} callback The function called per iteration or -   *  property name to count by. -   * @param {Mixed} [thisArg] The `this` binding for the callback. +   * @param {Function|String} callback|property The function called per iteration +   *  or property name to count by. +   * @param {Mixed} [thisArg] The `this` binding of `callback`.     * @returns {Object} Returns the composed aggregate object.     * @example     * @@ -1911,7 +1930,7 @@    /**     * Checks if the `callback` returns a truthy value for **all** elements of a -   * `collection`. The `callback` is bound to `thisArg` and invoked with 3 +   * `collection`. The `callback` is bound to `thisArg` and invoked with three     * arguments; (value, index|key, collection).     *     * @static @@ -1920,8 +1939,9 @@     * @category Collections     * @param {Array|Object|String} collection The collection to iterate over.     * @param {Function} [callback=identity] The function called per iteration. -   * @param {Mixed} [thisArg] The `this` binding for the callback. -   * @returns {Boolean} Returns `true` if all elements pass the callback check, else `false`. +   * @param {Mixed} [thisArg] The `this` binding of `callback`. +   * @returns {Boolean} Returns `true` if all elements pass the callback check, +   *  else `false`.     * @example     *     * _.every([true, 1, null, 'yes'], Boolean); @@ -1932,7 +1952,7 @@    /**     * Examines each element in a `collection`, returning an array of all elements     * the `callback` returns truthy for. The `callback` is bound to `thisArg` and -   * invoked with 3 arguments; (value, index|key, collection). +   * invoked with three arguments; (value, index|key, collection).     *     * @static     * @memberOf _ @@ -1940,8 +1960,8 @@     * @category Collections     * @param {Array|Object|String} collection The collection to iterate over.     * @param {Function} [callback=identity] The function called per iteration. -   * @param {Mixed} [thisArg] The `this` binding for the callback. -   * @returns {Array} Returns a new array of elements that passed callback check. +   * @param {Mixed} [thisArg] The `this` binding of `callback`. +   * @returns {Array} Returns a new array of elements that passed the callback check.     * @example     *     * var evens = _.filter([1, 2, 3, 4, 5, 6], function(num) { return num % 2 == 0; }); @@ -1953,7 +1973,7 @@     * Examines each element in a `collection`, returning the first one the `callback`     * returns truthy for. The function returns as soon as it finds an acceptable     * element, and does not iterate over the entire `collection`. The `callback` is -   * bound to `thisArg` and invoked with 3 arguments; (value, index|key, collection). +   * bound to `thisArg` and invoked with three arguments; (value, index|key, collection).     *     * @static     * @memberOf _ @@ -1961,8 +1981,9 @@     * @category Collections     * @param {Array|Object|String} collection The collection to iterate over.     * @param {Function} callback The function called per iteration. -   * @param {Mixed} [thisArg] The `this` binding for the callback. -   * @returns {Mixed} Returns the element that passed the callback check, else `undefined`. +   * @param {Mixed} [thisArg] The `this` binding of `callback`. +   * @returns {Mixed} Returns the element that passed the callback check, +   *  else `undefined`.     * @example     *     * var even = _.find([1, 2, 3, 4, 5, 6], function(num) { return num % 2 == 0; }); @@ -1975,9 +1996,9 @@    /**     * Iterates over a `collection`, executing the `callback` for each element in -   * the `collection`. The `callback` is bound to `thisArg` and invoked with 3 -   * arguments; (value, index|key, collection). Callbacks may exit iteration -   * early by explicitly returning `false`. +   * the `collection`. The `callback` is bound to `thisArg` and invoked with three +   * arguments; (value, index|key, collection). Callbacks may exit iteration early +   * by explicitly returning `false`.     *     * @static     * @memberOf _ @@ -1985,8 +2006,8 @@     * @category Collections     * @param {Array|Object|String} collection The collection to iterate over.     * @param {Function} callback The function called per iteration. -   * @param {Mixed} [thisArg] The `this` binding for the callback. -   * @returns {Array|Object} Returns `collection`. +   * @param {Mixed} [thisArg] The `this` binding of `callback`. +   * @returns {Array|Object|String} Returns `collection`.     * @example     *     * _([1, 2, 3]).forEach(alert).join(','); @@ -2001,16 +2022,16 @@     * Creates an object composed of keys returned from running each element of     * `collection` through a `callback`. The corresponding value of each key is an     * array of elements passed to `callback` that returned the key. The `callback` -   * is bound to `thisArg` and invoked with 3 arguments; (value, index|key, collection). +   * is bound to `thisArg` and invoked with three arguments; (value, index|key, collection).     * The `callback` argument may also be the name of a property to count by (e.g. 'length').     *     * @static     * @memberOf _     * @category Collections     * @param {Array|Object|String} collection The collection to iterate over. -   * @param {Function|String} callback The function called per iteration or -   *  property name to group by. -   * @param {Mixed} [thisArg] The `this` binding for the callback. +   * @param {Function|String} callback|property The function called per iteration +   *  or property name to group by. +   * @param {Mixed} [thisArg] The `this` binding of `callback`.     * @returns {Object} Returns the composed aggregate object.     * @example     * @@ -2025,15 +2046,15 @@     */    var groupBy = createIterator(baseIteratorOptions, countByIteratorOptions, {      'inLoop': -      'prop = callback(value, index, collection);\n' + +      'var prop = callback(value, index, collection);\n' +        '(hasOwnProperty.call(result, prop) ? result[prop] : result[prop] = []).push(value)'    });    /** -   * Invokes the method named by `methodName` on each element in the `collection`. -   * Additional arguments will be passed to each invoked method. If `methodName` -   * is a function it will be invoked for, and `this` bound to, each element -   * in the `collection`. +   * Invokes the method named by `methodName` on each element in the `collection`, +   * returning an array of the results of each invoked method. Additional arguments +   * will be passed to each invoked method. If `methodName` is a function it will +   * be invoked for, and `this` bound to, each element in the `collection`.     *     * @static     * @memberOf _ @@ -2042,7 +2063,7 @@     * @param {Function|String} methodName The name of the method to invoke or     *  the function invoked per iteration.     * @param {Mixed} [arg1, arg2, ...] Arguments to invoke the method with. -   * @returns {Array} Returns a new array of values returned from each invoked method. +   * @returns {Array} Returns a new array of the results of each invoked method.     * @example     *     * _.invoke([[5, 1, 7], [3, 2, 1]], 'sort'); @@ -2066,9 +2087,9 @@    });    /** -   * Creates a new array of values by running each element in the `collection` +   * Creates an array of values by running each element in the `collection`     * through a `callback`. The `callback` is bound to `thisArg` and invoked with -   * 3 arguments; (value, index|key, collection). +   * three arguments; (value, index|key, collection).     *     * @static     * @memberOf _ @@ -2076,8 +2097,8 @@     * @category Collections     * @param {Array|Object|String} collection The collection to iterate over.     * @param {Function} [callback=identity] The function called per iteration. -   * @param {Mixed} [thisArg] The `this` binding for the callback. -   * @returns {Array} Returns a new array of elements returned by the callback. +   * @param {Mixed} [thisArg] The `this` binding of `callback`. +   * @returns {Array} Returns a new array of the results of each `callback` execution.     * @example     *     * _.map([1, 2, 3], function(num) { return num * 3; }); @@ -2130,7 +2151,7 @@     * @param {Array|Object|String} collection The collection to iterate over.     * @param {Function} callback The function called per iteration.     * @param {Mixed} [accumulator] Initial value of the accumulator. -   * @param {Mixed} [thisArg] The `this` binding for the callback. +   * @param {Mixed} [thisArg] The `this` binding of `callback`.     * @returns {Mixed} Returns the accumulated value.     * @example     * @@ -2142,9 +2163,9 @@      'init': 'accumulator',      'top':        'var noaccum = arguments.length < 3;\n' + -      'if (thisArg) callback = iteratorBind(callback, thisArg)', +      'callback = createCallback(callback, thisArg)',      'beforeLoop': { -      'array': 'if (noaccum) result = collection[++index]' +      'array': 'if (noaccum) result = iteratee[++index]'      },      'inLoop': {        'array': @@ -2166,7 +2187,7 @@     * @param {Array|Object|String} collection The collection to iterate over.     * @param {Function} callback The function called per iteration.     * @param {Mixed} [accumulator] Initial value of the accumulator. -   * @param {Mixed} [thisArg] The `this` binding for the callback. +   * @param {Mixed} [thisArg] The `this` binding of `callback`.     * @returns {Mixed} Returns the accumulated value.     * @example     * @@ -2175,42 +2196,22 @@     * // => [4, 5, 2, 3, 0, 1]     */    function reduceRight(collection, callback, accumulator, thisArg) { -    if (!collection) { -      return accumulator; -    } - -    var length = collection.length, +    var iteratee = collection, +        length = collection.length,          noaccum = arguments.length < 3; -    if(thisArg) { -      callback = iteratorBind(callback, thisArg); -    } -    // Opera 10.53-10.60 JITted `length >>> 0` returns the wrong value for negative numbers -    if (length > -1 && length === length >>> 0) { -      var iteratee = noCharByIndex && toString.call(collection) == stringClass -        ? collection.split('') -        : collection; - -      if (length && noaccum) { -        accumulator = iteratee[--length]; -      } -      while (length--) { -        accumulator = callback(accumulator, iteratee[length], length, collection); -      } -      return accumulator; -    } - -    var prop, -        props = keys(collection); - -    length = props.length; -    if (length && noaccum) { -      accumulator = collection[props[--length]]; -    } -    while (length--) { -      prop = props[length]; -      accumulator = callback(accumulator, collection[prop], prop, collection); +    if (length !== +length) { +      var props = keys(collection); +      length = props.length; +    } else if (noCharByIndex && toString.call(collection) == stringClass) { +      iteratee = collection.split('');      } +    forEach(collection, function(value, index, object) { +      index = props ? props[--length] : --length; +      accumulator = noaccum +        ? (noaccum = false, iteratee[index]) +        : callback.call(thisArg, accumulator, iteratee[index], index, object); +    });      return accumulator;    } @@ -2223,8 +2224,9 @@     * @category Collections     * @param {Array|Object|String} collection The collection to iterate over.     * @param {Function} [callback=identity] The function called per iteration. -   * @param {Mixed} [thisArg] The `this` binding for the callback. -   * @returns {Array} Returns a new array of elements that did **not** pass the callback check. +   * @param {Mixed} [thisArg] The `this` binding of `callback`. +   * @returns {Array} Returns a new array of elements that did **not** pass the +   *  callback check.     * @example     *     * var odds = _.reject([1, 2, 3, 4, 5, 6], function(num) { return num % 2 == 0; }); @@ -2235,10 +2237,35 @@    });    /** +   * Gets the size of the `collection` by returning `collection.length` for arrays +   * and array-like objects or the number of own enumerable properties for objects. +   * +   * @static +   * @memberOf _ +   * @category Collections +   * @param {Array|Object|String} collection The collection to inspect. +   * @returns {Number} Returns `collection.length` or number of own enumerable properties. +   * @example +   * +   * _.size([1, 2]); +   * // => 2 +   * +   * _.size({ 'one': 1, 'two': 2, 'three': 3 }); +   * // => 3 +   * +   * _.size('curly'); +   * // => 5 +   */ +  function size(collection) { +    var length = collection ? collection.length : 0; +    return length === +length ? length : keys(collection).length; +  } + +  /**     * Checks if the `callback` returns a truthy value for **any** element of a     * `collection`. The function returns as soon as it finds passing value, and     * does not iterate over the entire `collection`. The `callback` is bound to -   * `thisArg` and invoked with 3 arguments; (value, index|key, collection). +   * `thisArg` and invoked with three arguments; (value, index|key, collection).     *     * @static     * @memberOf _ @@ -2246,8 +2273,9 @@     * @category Collections     * @param {Array|Object|String} collection The collection to iterate over.     * @param {Function} [callback=identity] The function called per iteration. -   * @param {Mixed} [thisArg] The `this` binding for the callback. -   * @returns {Boolean} Returns `true` if any element passes the callback check, else `false`. +   * @param {Mixed} [thisArg] The `this` binding of `callback`. +   * @returns {Boolean} Returns `true` if any element passes the callback check, +   *  else `false`.     * @example     *     * _.some([null, 0, 'yes', false]); @@ -2259,18 +2287,18 @@    });    /** -   * Creates a new array, stable sorted in ascending order by the results of +   * Creates an array, stable sorted in ascending order by the results of     * running each element of `collection` through a `callback`. The `callback` -   * is bound to `thisArg` and invoked with 3 arguments; (value, index|key, collection). +   * is bound to `thisArg` and invoked with three arguments; (value, index|key, collection).     * The `callback` argument may also be the name of a property to sort by (e.g. 'length').     *     * @static     * @memberOf _     * @category Collections     * @param {Array|Object|String} collection The collection to iterate over. -   * @param {Function|String} callback The function called per iteration or -   *  property name to sort by. -   * @param {Mixed} [thisArg] The `this` binding for the callback. +   * @param {Function|String} callback|property The function called per iteration +   *  or property name to sort by. +   * @param {Mixed} [thisArg] The `this` binding of `callback`.     * @returns {Array} Returns a new array of sorted elements.     * @example     * @@ -2307,8 +2335,7 @@    });    /** -   * Converts the `collection`, to an array. Useful for converting the -   * `arguments` object. +   * Converts the `collection`, to an array.     *     * @static     * @memberOf _ @@ -2321,14 +2348,8 @@     * // => [2, 3, 4]     */    function toArray(collection) { -    if (!collection) { -      return []; -    } -    if (collection.toArray && isFunction(collection.toArray)) { -      return collection.toArray(); -    } -    var length = collection.length; -    if (length > -1 && length === length >>> 0) { +    var length = collection ? collection.length : 0; +    if (length === +length) {        return (noArraySliceOnStrings ? toString.call(collection) == stringClass : typeof collection == 'string')          ? collection.split('')          : slice.call(collection); @@ -2360,21 +2381,21 @@    var where = createIterator(filterIteratorOptions, {      'args': 'collection, properties',      'top': -      'var pass, prop, propIndex, props = [];\n' + +      'var props = [];\n' +        'forIn(properties, function(value, prop) { props.push(prop) });\n' +        'var propsLength = props.length',      'inLoop': -      'for (pass = true, propIndex = 0; propIndex < propsLength; propIndex++) {\n' + +      'for (var prop, pass = true, propIndex = 0; propIndex < propsLength; propIndex++) {\n' +        '  prop = props[propIndex];\n' +        '  if (!(pass = value[prop] === properties[prop])) break\n' +        '}\n' + -      'if (pass) result.push(value)' +      'pass && result.push(value)'    });    /*--------------------------------------------------------------------------*/    /** -   * Creates a new array with all falsey values of `array` removed. The values +   * Creates an array with all falsey values of `array` removed. The values     * `false`, `null`, `0`, `""`, `undefined` and `NaN` are all falsey.     *     * @static @@ -2388,12 +2409,9 @@     * // => [1, 2, 3]     */    function compact(array) { -    var result = []; -    if (!array) { -      return result; -    }      var index = -1, -        length = array.length; +        length = array.length, +        result = [];      while (++index < length) {        if (array[index]) { @@ -2404,7 +2422,7 @@    }    /** -   * Creates a new array of `array` elements not present in the other arrays +   * Creates an array of `array` elements not present in the other arrays     * using strict equality for comparisons, i.e. `===`.     *     * @static @@ -2420,14 +2438,11 @@     * // => [1, 3, 4]     */    function difference(array) { -    var result = []; -    if (!array) { -      return result; -    }      var index = -1,          length = array.length, -        flattened = concat.apply(result, arguments), -        contains = cachedContains(flattened, length); +        flattened = concat.apply(ArrayProto, arguments), +        contains = cachedContains(flattened, length), +        result = [];      while (++index < length) {        if (!contains(array[index])) { @@ -2447,7 +2462,7 @@     * @category Arrays     * @param {Array} array The array to query.     * @param {Number} [n] The number of elements to return. -   * @param {Object} [guard] Internally used to allow this method to work with +   * @param- {Object} [guard] Internally used to allow this method to work with     *  others like `_.map` without using their callback `index` argument for `n`.     * @returns {Mixed} Returns the first element or an array of the first `n`     *  elements of `array`. @@ -2457,9 +2472,7 @@     * // => 5     */    function first(array, n, guard) { -    if (array) { -      return (n == null || guard) ? array[0] : slice.call(array, 0, n); -    } +    return (n == null || guard) ? array[0] : slice.call(array, 0, n);    }    /** @@ -2481,16 +2494,15 @@     * // => [1, 2, 3, [[4]]];     */    function flatten(array, shallow) { -    var result = []; -    if (!array) { -      return result; -    }      var value,          index = -1, -        length = array.length; +        length = array.length, +        result = [];      while (++index < length) {        value = array[index]; + +      // recursively flatten arrays (susceptible to call stack limits)        if (isArray(value)) {          push.apply(result, shallow ? value : flatten(value));        } else { @@ -2525,15 +2537,12 @@     * // => 2     */    function indexOf(array, value, fromIndex) { -    if (!array) { -      return -1; -    }      var index = -1,          length = array.length;      if (fromIndex) {        if (typeof fromIndex == 'number') { -        index = (fromIndex < 0 ? Math.max(0, length + fromIndex) : fromIndex) - 1; +        index = (fromIndex < 0 ? nativeMax(0, length + fromIndex) : fromIndex) - 1;        } else {          index = sortedIndex(array, value);          return array[index] === value ? index : -1; @@ -2556,7 +2565,7 @@     * @category Arrays     * @param {Array} array The array to query.     * @param {Number} [n] The number of elements to return. -   * @param {Object} [guard] Internally used to allow this method to work with +   * @param- {Object} [guard] Internally used to allow this method to work with     *  others like `_.map` without using their callback `index` argument for `n`.     * @returns {Array} Returns all but the last element or `n` elements of `array`.     * @example @@ -2565,9 +2574,6 @@     * // => [3, 2]     */    function initial(array, n, guard) { -    if (!array) { -      return []; -    }      return slice.call(array, 0, -((n == null || guard) ? 1 : n));    } @@ -2587,22 +2593,21 @@     * // => [1, 2]     */    function intersection(array) { -    var result = []; -    if (!array) { -      return result; -    }      var value, +        argsLength = arguments.length, +        cache = [],          index = -1,          length = array.length, -        others = slice.call(arguments, 1), -        cache = []; +        result = []; -    while (++index < length) { +    array: while (++index < length) {        value = array[index]; -      if (indexOf(result, value) < 0 && -          every(others, function(other, index) { -            return (cache[index] || (cache[index] = cachedContains(other)))(value); -          })) { +      if (indexOf(result, value) < 0) { +        for (var argsIndex = 1; argsIndex < argsLength; argsIndex++) { +          if (!(cache[argsIndex] || (cache[argsIndex] = cachedContains(arguments[argsIndex])))(value)) { +            continue array; +          } +        }          result.push(value);        }      } @@ -2610,15 +2615,15 @@    }    /** -   * Gets the last element of the `array`. Pass `n` to return the lasy `n` -   * elementsvof the `array`. +   * Gets the last element of the `array`. Pass `n` to return the last `n` +   * elements of the `array`.     *     * @static     * @memberOf _     * @category Arrays     * @param {Array} array The array to query.     * @param {Number} [n] The number of elements to return. -   * @param {Object} [guard] Internally used to allow this method to work with +   * @param- {Object} [guard] Internally used to allow this method to work with     *  others like `_.map` without using their callback `index` argument for `n`.     * @returns {Mixed} Returns the last element or an array of the last `n`     *  elements of `array`. @@ -2628,10 +2633,8 @@     * // => 1     */    function last(array, n, guard) { -    if (array) { -      var length = array.length; -      return (n == null || guard) ? array[length - 1] : slice.call(array, -n || length); -    } +    var length = array.length; +    return (n == null || guard) ? array[length - 1] : slice.call(array, -n || length);    }    /** @@ -2654,12 +2657,9 @@     * // => 1     */    function lastIndexOf(array, value, fromIndex) { -    if (!array) { -      return -1; -    }      var index = array.length;      if (fromIndex && typeof fromIndex == 'number') { -      index = (fromIndex < 0 ? Math.max(0, index + fromIndex) : Math.min(fromIndex, index - 1)) + 1; +      index = (fromIndex < 0 ? nativeMax(0, index + fromIndex) : nativeMin(fromIndex, index - 1)) + 1;      }      while (index--) {        if (array[index] === value) { @@ -2673,14 +2673,14 @@     * Retrieves the maximum value of an `array`. If `callback` is passed,     * it will be executed for each value in the `array` to generate the     * criterion by which the value is ranked. The `callback` is bound to -   * `thisArg` and invoked with 3 arguments; (value, index, array). +   * `thisArg` and invoked with three arguments; (value, index, array).     *     * @static     * @memberOf _     * @category Arrays     * @param {Array} array The array to iterate over.     * @param {Function} [callback] The function called per iteration. -   * @param {Mixed} [thisArg] The `this` binding for the callback. +   * @param {Mixed} [thisArg] The `this` binding of `callback`.     * @returns {Mixed} Returns the maximum value.     * @example     * @@ -2694,27 +2694,13 @@     * // => { 'name': 'curly', 'age': 60 };     */    function max(array, callback, thisArg) { -    var computed = -Infinity, -        result = computed; - -    if (!array) { -      return result; -    }      var current, +        computed = -Infinity,          index = -1, -        length = array.length; +        length = array ? array.length : 0, +        result = computed; -    if (!callback) { -      while (++index < length) { -        if (array[index] > result) { -          result = array[index]; -        } -      } -      return result; -    } -    if (thisArg) { -      callback = iteratorBind(callback, thisArg); -    } +    callback = createCallback(callback, thisArg);      while (++index < length) {        current = callback(array[index], index, array);        if (current > computed) { @@ -2729,14 +2715,14 @@     * Retrieves the minimum value of an `array`. If `callback` is passed,     * it will be executed for each value in the `array` to generate the     * criterion by which the value is ranked. The `callback` is bound to `thisArg` -   * and invoked with 3 arguments; (value, index, array). +   * and invoked with three arguments; (value, index, array).     *     * @static     * @memberOf _     * @category Arrays     * @param {Array} array The array to iterate over.     * @param {Function} [callback] The function called per iteration. -   * @param {Mixed} [thisArg] The `this` binding for the callback. +   * @param {Mixed} [thisArg] The `this` binding of `callback`.     * @returns {Mixed} Returns the minimum value.     * @example     * @@ -2744,27 +2730,13 @@     * // => 2     */    function min(array, callback, thisArg) { -    var computed = Infinity, -        result = computed; - -    if (!array) { -      return result; -    }      var current, +        computed = Infinity,          index = -1, -        length = array.length; +        length = array ? array.length : 0, +        result = computed; -    if (!callback) { -      while (++index < length) { -        if (array[index] < result) { -          result = array[index]; -        } -      } -      return result; -    } -    if (thisArg) { -      callback = iteratorBind(callback, thisArg); -    } +    callback = createCallback(callback, thisArg);      while (++index < length) {        current = callback(array[index], index, array);        if (current < computed) { @@ -2776,6 +2748,38 @@    }    /** +   * Creates an object composed from arrays of `keys` and `values`. Pass either +   * a single two dimensional array, i.e. `[[key1, value1], [key2, value2]]`, or +   * two arrays, one of `keys` and one of corresponding `values`. +   * +   * @static +   * @memberOf _ +   * @category Arrays +   * @param {Array} keys The array of keys. +   * @param {Array} [values=[]] The array of values. +   * @returns {Object} Returns an object composed of the given keys and +   *  corresponding values. +   * @example +   * +   * _.object(['moe', 'larry', 'curly'], [30, 40, 50]); +   * // => { 'moe': 30, 'larry': 40, 'curly': 50 } +   */ +  function object(keys, values) { +    var index = -1, +        length = keys.length, +        result = {}; + +    while (++index < length) { +      if (values) { +        result[keys[index]] = values[index]; +      } else { +        result[keys[index][0]] = keys[index][1]; +      } +    } +    return result; +  } + +  /**     * Creates an array of numbers (positive and/or negative) progressing from     * `start` up to but not including `stop`. This method is a port of Python's     * `range()` function. See http://docs.python.org/library/functions.html#range. @@ -2815,7 +2819,7 @@      // use `Array(length)` so V8 will avoid the slower "dictionary" mode      // http://www.youtube.com/watch?v=XAqIpGU8ZZk#t=16m27s      var index = -1, -        length = Math.max(0, Math.ceil((end - start) / step)), +        length = nativeMax(0, Math.ceil((end - start) / step)),          result = Array(length);      while (++index < length) { @@ -2831,11 +2835,11 @@     *     * @static     * @memberOf _ -   * @alias tail +   * @alias drop, tail     * @category Arrays     * @param {Array} array The array to query.     * @param {Number} [n] The number of elements to return. -   * @param {Object} [guard] Internally used to allow this method to work with +   * @param- {Object} [guard] Internally used to allow this method to work with     *  others like `_.map` without using their callback `index` argument for `n`.     * @returns {Array} Returns all but the first value or `n` values of `array`.     * @example @@ -2844,14 +2848,11 @@     * // => [2, 1]     */    function rest(array, n, guard) { -    if (!array) { -      return []; -    }      return slice.call(array, (n == null || guard) ? 1 : n);    }    /** -   * Creates a new array of shuffled `array` values, using a version of the +   * Creates an array of shuffled `array` values, using a version of the     * Fisher-Yates shuffle. See http://en.wikipedia.org/wiki/Fisher-Yates_shuffle.     *     * @static @@ -2865,16 +2866,13 @@     * // => [4, 1, 6, 3, 5, 2]     */    function shuffle(array) { -    if (!array) { -      return []; -    }      var rand,          index = -1,          length = array.length,          result = Array(length);      while (++index < length) { -      rand = Math.floor(Math.random() * (index + 1)); +      rand = nativeFloor(nativeRandom() * (index + 1));        result[index] = result[rand];        result[rand] = array[index];      } @@ -2886,58 +2884,51 @@     * should be inserted into `array` in order to maintain the sort order of the     * sorted `array`. If `callback` is passed, it will be executed for `value` and     * each element in `array` to compute their sort ranking. The `callback` is -   * bound to `thisArg` and invoked with 1 argument; (value). +   * bound to `thisArg` and invoked with one argument; (value). The `callback` +   * argument may also be the name of a property to order by.     *     * @static     * @memberOf _     * @category Arrays     * @param {Array} array The array to iterate over.     * @param {Mixed} value The value to evaluate. -   * @param {Function} [callback=identity] The function called per iteration. -   * @param {Mixed} [thisArg] The `this` binding for the callback. +   * @param {Function|String} [callback=identity|property] The function called +   *  per iteration or property name to order by. +   * @param {Mixed} [thisArg] The `this` binding of `callback`.     * @returns {Number} Returns the index at which the value should be inserted     *  into `array`.     * @example     * -   * _.sortedIndex([20, 30, 40], 35); +   * _.sortedIndex([20, 30, 50], 40); +   * // => 2 +   * +   * _.sortedIndex([{ 'x': 20 }, { 'x': 30 }, { 'x': 50 }], { 'x': 40 }, 'x');     * // => 2     *     * var dict = { -   *   'wordToNumber': { 'twenty': 20, 'thirty': 30, 'thirty-five': 35, 'fourty': 40 } +   *   'wordToNumber': { 'twenty': 20, 'thirty': 30, 'fourty': 40, 'fifty': 50 }     * };     * -   * _.sortedIndex(['twenty', 'thirty', 'fourty'], 'thirty-five', function(word) { +   * _.sortedIndex(['twenty', 'thirty', 'fifty'], 'fourty', function(word) {     *   return dict.wordToNumber[word];     * });     * // => 2     * -   * _.sortedIndex(['twenty', 'thirty', 'fourty'], 'thirty-five', function(word) { +   * _.sortedIndex(['twenty', 'thirty', 'fifty'], 'fourty', function(word) {     *   return this.wordToNumber[word];     * }, dict);     * // => 2     */    function sortedIndex(array, value, callback, thisArg) { -    if (!array) { -      return 0; -    }      var mid,          low = 0,          high = array.length; -    if (callback) { -      if (thisArg) { -        callback = bind(callback, thisArg); -      } -      value = callback(value); -      while (low < high) { -        mid = (low + high) >>> 1; -        callback(array[mid]) < value ? low = mid + 1 : high = mid; -      } -    } else { -      while (low < high) { -        mid = (low + high) >>> 1; -        array[mid] < value ? low = mid + 1 : high = mid; -      } +    callback = createCallback(callback, thisArg); +    value = callback(value); +    while (low < high) { +      mid = (low + high) >>> 1; +      callback(array[mid]) < value ? low = mid + 1 : high = mid;      }      return low;    } @@ -2959,9 +2950,9 @@     */    function union() {      var index = -1, -        result = [], -        flattened = concat.apply(result, arguments), -        length = flattened.length; +        flattened = concat.apply(ArrayProto, arguments), +        length = flattened.length, +        result = [];      while (++index < length) {        if (indexOf(result, flattened[index]) < 0) { @@ -2976,7 +2967,7 @@     * for comparisons, i.e. `===`. If the `array` is already sorted, passing `true`     * for `isSorted` will run a faster algorithm. If `callback` is passed, each     * element of `array` is passed through a callback` before uniqueness is computed. -   * The `callback` is bound to `thisArg` and invoked with 3 arguments; (value, index, array). +   * The `callback` is bound to `thisArg` and invoked with three arguments; (value, index, array).     *     * @static     * @memberOf _ @@ -2985,7 +2976,7 @@     * @param {Array} array The array to process.     * @param {Boolean} [isSorted=false] A flag to indicate that the `array` is already sorted.     * @param {Function} [callback=identity] The function called per iteration. -   * @param {Mixed} [thisArg] The `this` binding for the callback. +   * @param {Mixed} [thisArg] The `this` binding of `callback`.     * @returns {Array} Returns a duplicate-value-free array.     * @example     * @@ -3002,13 +2993,10 @@     * // => [1, 2, 3]     */    function uniq(array, isSorted, callback, thisArg) { -    var result = []; -    if (!array) { -      return result; -    }      var computed,          index = -1,          length = array.length, +        result = [],          seen = [];      // juggle arguments @@ -3017,11 +3005,7 @@        callback = isSorted;        isSorted = false;      } -    if (!callback) { -      callback = identity; -    } else if (thisArg) { -      callback = iteratorBind(callback, thisArg); -    } +    callback = createCallback(callback, thisArg);      while (++index < length) {        computed = callback(array[index], index, array);        if (isSorted @@ -3036,7 +3020,7 @@    }    /** -   * Creates a new array with all occurrences of the passed values removed using +   * Creates an array with all occurrences of the passed values removed using     * strict equality for comparisons, i.e. `===`.     *     * @static @@ -3051,13 +3035,10 @@     * // => [2, 3, 4]     */    function without(array) { -    var result = []; -    if (!array) { -      return result; -    }      var index = -1,          length = array.length, -        contains = cachedContains(arguments, 1, 20); +        contains = cachedContains(arguments, 1, 20), +        result = [];      while (++index < length) {        if (!contains(array[index])) { @@ -3084,9 +3065,6 @@     * // => [['moe', 30, true], ['larry', 40, false], ['curly', 50, false]]     */    function zip(array) { -    if (!array) { -      return []; -    }      var index = -1,          length = max(pluck(arguments, 'length')),          result = Array(length); @@ -3097,40 +3075,10 @@      return result;    } -  /** -   * Creates an object composed from an array of `keys` and an array of `values`. -   * -   * @static -   * @memberOf _ -   * @category Arrays -   * @param {Array} keys The array of keys. -   * @param {Array} [values=[]] The array of values. -   * @returns {Object} Returns an object composed of the given keys and -   *  corresponding values. -   * @example -   * -   * _.zipObject(['moe', 'larry', 'curly'], [30, 40, 50]); -   * // => { 'moe': 30, 'larry': 40, 'curly': 50 } -   */ -  function zipObject(keys, values) { -    if (!keys) { -      return {}; -    } -    var index = -1, -        length = keys.length, -        result = {}; - -    values || (values = []); -    while (++index < length) { -      result[keys[index]] = values[index]; -    } -    return result; -  } -    /*--------------------------------------------------------------------------*/    /** -   * Creates a new function that is restricted to executing only after it is +   * Creates a function that is restricted to executing only after it is     * called `n` times.     *     * @static @@ -3160,21 +3108,19 @@    }    /** -   * Creates a new function that, when called, invokes `func` with the `this` +   * Creates a function that, when called, invokes `func` with the `this`     * binding of `thisArg` and prepends any additional `bind` arguments to those -   * passed to the bound function. Lazy defined methods may be bound by passing -   * the object they are bound to as `func` and the method name as `thisArg`. +   * passed to the bound function.     *     * @static     * @memberOf _     * @category Functions -   * @param {Function|Object} func The function to bind or the object the method belongs to. -   * @param {Mixed} [thisArg] The `this` binding of `func` or the method name. +   * @param {Function} func The function to bind. +   * @param {Mixed} [thisArg] The `this` binding of `func`.     * @param {Mixed} [arg1, arg2, ...] Arguments to be partially applied.     * @returns {Function} Returns the new bound function.     * @example     * -   * // basic bind     * var func = function(greeting) {     *   return greeting + ' ' + this.name;     * }; @@ -3182,72 +3128,13 @@     * func = _.bind(func, { 'name': 'moe' }, 'hi');     * func();     * // => 'hi moe' -   * -   * // lazy bind -   * var object = { -   *   'name': 'moe', -   *   'greet': function(greeting) { -   *     return greeting + ' ' + this.name; -   *   } -   * }; -   * -   * var func = _.bind(object, 'greet', 'hi'); -   * func(); -   * // => 'hi moe' -   * -   * object.greet = function(greeting) { -   *   return greeting + ', ' + this.name + '!'; -   * }; -   * -   * func(); -   * // => 'hi, moe!'     */    function bind(func, thisArg) { -    var methodName, -        isFunc = isFunction(func); - -    // juggle arguments -    if (!isFunc) { -      methodName = thisArg; -      thisArg = func; -    }      // use `Function#bind` if it exists and is fast      // (in V8 `Function#bind` is slower except when partially applied) -    else if (isBindFast || (nativeBind && arguments.length > 2)) { -      return nativeBind.call.apply(nativeBind, arguments); -    } - -    var partialArgs = slice.call(arguments, 2); - -    function bound() { -      // `Function#bind` spec -      // http://es5.github.com/#x15.3.4.5 -      var args = arguments, -          thisBinding = thisArg; - -      if (!isFunc) { -        func = thisArg[methodName]; -      } -      if (partialArgs.length) { -        args = args.length -          ? partialArgs.concat(slice.call(args)) -          : partialArgs; -      } -      if (this instanceof bound) { -        // get `func` instance if `bound` is invoked in a `new` expression -        noop.prototype = func.prototype; -        thisBinding = new noop; - -        // mimic the constructor's `return` behavior -        // http://es5.github.com/#x13.2.2 -        var result = func.apply(thisBinding, args); -        return result && objectTypes[typeof result] -          ? result -          : thisBinding -      } -      return func.apply(thisBinding, args); -    } -    return bound; +    return isBindFast || (nativeBind && arguments.length > 2) +      ? nativeBind.call.apply(nativeBind, arguments) +      : createBound(func, thisArg, slice.call(arguments, 2));    }    /** @@ -3276,7 +3163,6 @@      'useHas': false,      'useStrict': false,      'args': 'object', -    'init': 'object',      'top':        'var funcs = arguments,\n' +        '    length = funcs.length;\n' + @@ -3293,7 +3179,7 @@    });    /** -   * Creates a new function that is the composition of the passed functions, +   * Creates a function that is the composition of the passed functions,     * where each function consumes the return value of the function that follows.     * In math terms, composing the functions `f()`, `g()`, and `h()` produces `f(g(h()))`.     * @@ -3324,7 +3210,7 @@    }    /** -   * Creates a new function that will delay the execution of `func` until after +   * Creates a function that will delay the execution of `func` until after     * `wait` milliseconds have elapsed since the last time it was invoked. Pass     * `true` for `immediate` to cause debounce to invoke `func` on the leading,     * instead of the trailing, edge of the `wait` timeout. Subsequent calls to @@ -3352,7 +3238,7 @@      function delayed() {        timeoutId = null;        if (!immediate) { -        func.apply(thisArg, args); +        result = func.apply(thisArg, args);        }      } @@ -3414,7 +3300,43 @@    }    /** -   * Creates a new function that memoizes the result of `func`. If `resolver` is +   * Creates a function that, when called, invokes `object[methodName]` and +   * prepends any additional `lateBind` arguments to those passed to the bound +   * function. This method +   * +   * @static +   * @memberOf _ +   * @category Functions +   * @param {Object} object The object the method belongs to. +   * @param {String} methodName The method name. +   * @param {Mixed} [arg1, arg2, ...] Arguments to be partially applied. +   * @returns {Function} Returns the new bound function. +   * @example +   * +   * var object = { +   *   'name': 'moe', +   *   'greet': function(greeting) { +   *     return greeting + ' ' + this.name; +   *   } +   * }; +   * +   * var func = _.bind(object, 'greet', 'hi'); +   * func(); +   * // => 'hi moe' +   * +   * object.greet = function(greeting) { +   *   return greeting + ', ' + this.name + '!'; +   * }; +   * +   * func(); +   * // => 'hi, moe!' +   */ +  function lateBind(object, methodName) { +    return createBound(methodName, object, slice.call(arguments, 2)); +  } + +  /** +   * Creates a function that memoizes the result of `func`. If `resolver` is     * passed, it will be used to determine the cache key for storing the result     * based on the arguments passed to the memoized function. By default, the first     * argument passed to the memoized function is used as the cache key. @@ -3442,7 +3364,7 @@    }    /** -   * Creates a new function that is restricted to one execution. Repeat calls to +   * Creates a function that is restricted to one execution. Repeat calls to     * the function will return the value of the first call.     *     * @static @@ -3475,9 +3397,9 @@    }    /** -   * Creates a new function that, when called, invokes `func` with any additional +   * Creates a function that, when called, invokes `func` with any additional     * `partial` arguments prepended to those passed to the new function. This method -   * is similar `bind`, except it does **not** alter the `this` binding. +   * is similar to `bind`, except it does **not** alter the `this` binding.     *     * @static     * @memberOf _ @@ -3493,25 +3415,11 @@     * // => 'hi: moe'     */    function partial(func) { -    var args = slice.call(arguments, 1), -        argsLength = args.length; - -    return function() { -      var result, -          others = arguments; - -      if (others.length) { -        args.length = argsLength; -        push.apply(args, others); -      } -      result = args.length == 1 ? func.call(this, args[0]) : func.apply(this, args); -      args.length = argsLength; -      return result; -    }; +    return createBound(func, slice.call(arguments, 1));    }    /** -   * Creates a new function that, when executed, will only call the `func` +   * Creates a function that, when executed, will only call the `func`     * function at most once per every `wait` milliseconds. If the throttled     * function is invoked more than once during the `wait` timeout, `func` will     * also be called on the trailing edge of the timeout. Subsequent calls to the @@ -3538,7 +3446,7 @@      function trailingCall() {        lastCalled = new Date;        timeoutId = null; -      func.apply(thisArg, args); +      result = func.apply(thisArg, args);      }      return function() { @@ -3560,7 +3468,7 @@    }    /** -   * Creates a new function that passes `value` to the `wrapper` function as its +   * Creates a function that passes `value` to the `wrapper` function as its     * first argument. Additional arguments passed to the new function are appended     * to those passed to the `wrapper` function.     * @@ -3592,8 +3500,8 @@    /*--------------------------------------------------------------------------*/    /** -   * Escapes a string for inclusion in HTML, replacing `&`, `<`, `"`, and `'` -   * characters. +   * Converts the characters `&`, `<`, `>`, `"`, and `'` in `string` to their +   * corresponding HTML entities.     *     * @static     * @memberOf _ @@ -3655,15 +3563,15 @@      forEach(functions(object), function(methodName) {        var func = lodash[methodName] = object[methodName]; -      LoDash.prototype[methodName] = function() { -        var args = [this._wrapped]; +      lodash.prototype[methodName] = function() { +        var args = [this.__wrapped__];          if (arguments.length) {            push.apply(args, arguments);          }          var result = func.apply(lodash, args); -        if (this._chain) { -          result = new LoDash(result); -          result._chain = true; +        if (this.__chain__) { +          result = new lodash(result); +          result.__chain__ = true;          }          return result;        }; @@ -3688,6 +3596,36 @@    }    /** +   * Produces a random number between `min` and `max` (inclusive). If only one +   * argument is passed, a number between `0` and the given number will be returned. +   * +   * @static +   * @memberOf _ +   * @category Utilities +   * @param {Number} [min=0] The minimum possible value. +   * @param {Number} [max=1] The maximum possible value. +   * @returns {Number} Returns a random number. +   * @example +   * +   * _.random(0, 5); +   * // => a number between 1 and 5 +   * +   * _.random(5); +   * // => also a number between 1 and 5 +   */ +  function random(min, max) { +    if (min == null && max == null) { +      max = 1; +    } +    min = +min || 0; +    if (max == null) { +      max = min; +      min = 0; +    } +    return min + nativeFloor(nativeRandom() * ((+max || 0) - min + 1)); +  } + +  /**     * Resolves the value of `property` on `object`. If `property` is a function     * it will be invoked and its result returned, else the property value is     * returned. If `object` is falsey, then `null` is returned. @@ -3697,7 +3635,7 @@     * @memberOf _     * @category Utilities     * @param {Object} object The object to inspect. -   * @param {String} property The property to get the result of. +   * @param {String} property The property to get the value of.     * @returns {Mixed} Returns the resolved value.     * @example     * @@ -3717,10 +3655,7 @@    function result(object, property) {      // based on Backbone's private `getValue` function      // https://github.com/documentcloud/backbone/blob/0.9.2/backbone.js#L1419-1424 -    if (!object) { -      return null; -    } -    var value = object[property]; +    var value = object ? object[property] : null;      return isFunction(value) ? object[property]() : value;    } @@ -3750,7 +3685,7 @@     * compiled({ 'name': 'moe' });     * // => 'hello: moe'     * -   * var list = '<% _.forEach(people, function(name) { %> <li><%= name %></li> <% }); %>'; +   * var list = '<% _.forEach(people, function(name) { %><li><%= name %></li><% }); %>';     * _.template(list, { 'people': ['moe', 'larry', 'curly'] });     * // => '<li>moe</li><li>larry</li><li>curly</li>'     * @@ -3759,12 +3694,12 @@     * // => '<b><script></b>'     *     * // using the internal `print` function in "evaluate" delimiters -   * _.template('<% print("Hello " + epithet); %>', { 'epithet': 'stooge' }); +   * _.template('<% print("Hello " + epithet); %>.', { 'epithet': 'stooge' });     * // => 'Hello stooge.'     *     * // using custom template delimiter settings     * _.templateSettings = { -   *   'interpolate': /\{\{(.+?)\}\}/g +   *   'interpolate': /\{\{([\s\S]+?)\}\}/g     * };     *     * _.template('Hello {{ name }}!', { 'name': 'Mustache' }); @@ -3793,89 +3728,62 @@      // and Laura Doktorova's doT.js      // https://github.com/olado/doT      options || (options = {}); -    text += '';      var isEvaluating,          result, -        escapeDelimiter = options.escape, -        evaluateDelimiter = options.evaluate, -        interpolateDelimiter = options.interpolate, +        index = 0,          settings = lodash.templateSettings, +        source = "__p += '",          variable = options.variable || settings.variable,          hasVariable = variable; -    // use default settings if no options object is provided -    if (escapeDelimiter == null) { -      escapeDelimiter = settings.escape; -    } -    if (evaluateDelimiter == null) { -      // use `false` as the fallback value, instead of leaving it `undefined`, -      // so the initial assignment of `reEvaluateDelimiter` will still occur -      evaluateDelimiter = settings.evaluate || false; -    } -    if (interpolateDelimiter == null) { -      interpolateDelimiter = settings.interpolate; -    } - -    // tokenize delimiters to avoid escaping them -    if (escapeDelimiter) { -      text = text.replace(escapeDelimiter, tokenizeEscape); -    } -    if (interpolateDelimiter) { -      text = text.replace(interpolateDelimiter, tokenizeInterpolate); -    } -    if (evaluateDelimiter != lastEvaluateDelimiter) { -      // generate `reEvaluateDelimiter` to match `_.templateSettings.evaluate` -      // and internal `<e%- %>`, `<e%= %>` delimiters -      lastEvaluateDelimiter = evaluateDelimiter; -      reEvaluateDelimiter = RegExp( -        '<e%-([\\s\\S]+?)%>|<e%=([\\s\\S]+?)%>' + -        (evaluateDelimiter ? '|' + evaluateDelimiter.source : '') -      , 'g'); -    } -    isEvaluating = tokenized.length; -    text = text.replace(reEvaluateDelimiter, tokenizeEvaluate); -    isEvaluating = isEvaluating != tokenized.length; - -    // escape characters that cannot be included in string literals and -    // detokenize delimiter code snippets -    text = "__p += '" + text -      .replace(reUnescapedString, escapeStringChar) -      .replace(reToken, detokenize) + "';\n"; +    // compile regexp to match each delimiter +    var reDelimiters = RegExp( +      (options.escape || settings.escape || reNoMatch).source + '|' + +      (options.interpolate || settings.interpolate || reNoMatch).source + '|' + +      (options.evaluate || settings.evaluate || reNoMatch).source + '|$' +    , 'g'); + +    text.replace(reDelimiters, function(match, escapeValue, interpolateValue, evaluateValue, offset) { +      // escape characters that cannot be included in string literals +      source += text.slice(index, offset).replace(reUnescapedString, escapeStringChar); + +      // replace delimiters with snippets +      source += +        escapeValue ? "' +\n__e(" + escapeValue + ") +\n'" : +        evaluateValue ? "';\n" + evaluateValue + ";\n__p += '" : +        interpolateValue ? "' +\n((__t = (" + interpolateValue + ")) == null ? '' : __t) +\n'" : ''; + +      isEvaluating || (isEvaluating = evaluateValue || reComplexDelimiter.test(escapeValue || interpolateValue)); +      index = offset + match.length; +    }); -    // clear stored code snippets -    tokenized.length = 0; +    source += "';\n";      // if `variable` is not specified and the template contains "evaluate"      // delimiters, wrap a with-statement around the generated code to add the      // data object to the top of the scope chain      if (!hasVariable) { -      variable = lastVariable || 'obj'; - +      variable = 'obj';        if (isEvaluating) { -        text = 'with (' + variable + ') {\n' + text + '\n}\n'; +        source = 'with (' + variable + ') {\n' + source + '\n}\n';        }        else { -        if (variable != lastVariable) { -          // generate `reDoubleVariable` to match references like `obj.obj` inside -          // transformed "escape" and "interpolate" delimiters -          lastVariable = variable; -          reDoubleVariable = RegExp('(\\(\\s*)' + variable + '\\.' + variable + '\\b', 'g'); -        }          // avoid a with-statement by prepending data object references to property names -        text = text +        var reDoubleVariable = RegExp('(\\(\\s*)' + variable + '\\.' + variable + '\\b', 'g'); +        source = source            .replace(reInsertVariable, '$&' + variable + '.')            .replace(reDoubleVariable, '$1__d');        }      }      // cleanup code by stripping empty strings -    text = ( isEvaluating ? text.replace(reEmptyStringLeading, '') : text) +    source = (isEvaluating ? source.replace(reEmptyStringLeading, '') : source)        .replace(reEmptyStringMiddle, '$1')        .replace(reEmptyStringTrailing, '$1;');      // frame code as the function body -    text = 'function(' + variable + ') {\n' + +    source = 'function(' + variable + ') {\n' +        (hasVariable ? '' : variable + ' || (' + variable + ' = {});\n') +        'var __t, __p = \'\', __e = _.escape' +        (isEvaluating @@ -3883,22 +3791,20 @@            'function print() { __p += __j.call(arguments, \'\') }\n'          : (hasVariable ? '' : ', __d = ' + variable + '.' + variable + ' || ' + variable) + ';\n'        ) + -      text + +      source +        'return __p\n}'; -    // add a sourceURL for easier debugging +    // use a sourceURL for easier debugging      // http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/#toc-sourceurl -    if (useSourceURL) { -      text += '\n//@ sourceURL=/lodash/template/source[' + (templateCounter++) + ']'; -    } +    var sourceURL = useSourceURL +      ? '\n//@ sourceURL=/lodash/template/source[' + (templateCounter++) + ']' +      : '';      try { -      result = Function('_', 'return ' + text)(lodash); +      result = Function('_', 'return ' + source + sourceURL)(lodash);      } catch(e) { -      // defer syntax errors until the compiled template is executed to allow -      // examining the `source` property beforehand and for consistency, -      // because other template related errors occur at execution -      result = function() { throw e; }; +      e.source = source; +      throw e;      }      if (data) { @@ -3907,39 +3813,60 @@      // provide the compiled function's source via its `toString` method, in      // supported environments, or the `source` property as a convenience for      // inlining compiled templates during the build process -    result.source = text; +    result.source = source;      return result;    }    /** -   * Executes the `callback` function `n` times. The `callback` is bound to -   * `thisArg` and invoked with 1 argument; (index). +   * Executes the `callback` function `n` times, returning an array of the results +   * of each `callback` execution. The `callback` is bound to `thisArg` and invoked +   * with one argument; (index).     *     * @static     * @memberOf _     * @category Utilities     * @param {Number} n The number of times to execute the callback.     * @param {Function} callback The function called per iteration. -   * @param {Mixed} [thisArg] The `this` binding for the callback. +   * @param {Mixed} [thisArg] The `this` binding of `callback`. +   * @returns {Array} Returns a new array of the results of each `callback` execution.     * @example     * -   * _.times(3, function() { genie.grantWish(); }); -   * // => calls `genie.grantWish()` 3 times +   * var diceRolls = _.times(3, _.partial(_.random, 1, 6)); +   * // => [3, 6, 4]     * -   * _.times(3, function() { this.grantWish(); }, genie); -   * // => also calls `genie.grantWish()` 3 times +   * _.times(3, function(n) { mage.castSpell(n); }); +   * // => calls `mage.castSpell(n)` three times, passing `n` of `0`, `1`, and `2` respectively +   * +   * _.times(3, function(n) { this.cast(n); }, mage); +   * // => also calls `mage.castSpell(n)` three times     */    function times(n, callback, thisArg) { -    var index = -1; -    if (thisArg) { -      while (++index < n) { -        callback.call(thisArg, index); -      } -    } else { -      while (++index < n) { -        callback(index); -      } +    n = +n || 0; +    var index = -1, +        result = Array(n); + +    while (++index < n) { +      result[index] = callback.call(thisArg, index);      } +    return result; +  } + +  /** +   * Converts the HTML entities `&`, `<`, `>`, `"`, and `'` +   * in `string` to their corresponding characters. +   * +   * @static +   * @memberOf _ +   * @category Utilities +   * @param {String} string The string to unescape. +   * @returns {String} Returns the unescaped string. +   * @example +   * +   * _.unescape('Moe, Larry & Curly'); +   * // => "Moe, Larry & Curly" +   */ +  function unescape(string) { +    return string == null ? '' : (string + '').replace(reEscapedHtml, unescapeHtmlChar);    }    /** @@ -3987,8 +3914,8 @@     * // => 'moe is 40'     */    function chain(value) { -    value = new LoDash(value); -    value._chain = true; +    value = new lodash(value); +    value.__chain__ = true;      return value;    } @@ -4005,7 +3932,7 @@     * @returns {Mixed} Returns `value`.     * @example     * -   * _.chain([1,2,3,200]) +   * _.chain([1, 2, 3, 200])     *  .filter(function(num) { return num % 2 == 0; })     *  .tap(alert)     *  .map(function(num) { return num * num }) @@ -4032,7 +3959,7 @@     * // => [1, 2, 3]     */    function wrapperChain() { -    this._chain = true; +    this.__chain__ = true;      return this;    } @@ -4049,7 +3976,7 @@     * // => [1, 2, 3]     */    function wrapperValue() { -    return this._wrapped; +    return this.__wrapped__;    }    /*--------------------------------------------------------------------------*/ @@ -4061,7 +3988,7 @@     * @memberOf _     * @type String     */ -  lodash.VERSION = '0.5.2'; +  lodash.VERSION = '0.7.0';    // assign static methods    lodash.after = after; @@ -4078,7 +4005,6 @@    lodash.defer = defer;    lodash.delay = delay;    lodash.difference = difference; -  lodash.drop = drop;    lodash.escape = escape;    lodash.every = every;    lodash.extend = extend; @@ -4096,6 +4022,7 @@    lodash.indexOf = indexOf;    lodash.initial = initial;    lodash.intersection = intersection; +  lodash.invert = invert;    lodash.invoke = invoke;    lodash.isArguments = isArguments;    lodash.isArray = isArray; @@ -4110,12 +4037,14 @@    lodash.isNull = isNull;    lodash.isNumber = isNumber;    lodash.isObject = isObject; +  lodash.isPlainObject = isPlainObject;    lodash.isRegExp = isRegExp;    lodash.isString = isString;    lodash.isUndefined = isUndefined;    lodash.keys = keys;    lodash.last = last;    lodash.lastIndexOf = lastIndexOf; +  lodash.lateBind = lateBind;    lodash.map = map;    lodash.max = max;    lodash.memoize = memoize; @@ -4123,10 +4052,14 @@    lodash.min = min;    lodash.mixin = mixin;    lodash.noConflict = noConflict; +  lodash.object = object; +  lodash.omit = omit;    lodash.once = once; +  lodash.pairs = pairs;    lodash.partial = partial;    lodash.pick = pick;    lodash.pluck = pluck; +  lodash.random = random;    lodash.range = range;    lodash.reduce = reduce;    lodash.reduceRight = reduceRight; @@ -4143,6 +4076,7 @@    lodash.throttle = throttle;    lodash.times = times;    lodash.toArray = toArray; +  lodash.unescape = unescape;    lodash.union = union;    lodash.uniq = uniq;    lodash.uniqueId = uniqueId; @@ -4151,13 +4085,13 @@    lodash.without = without;    lodash.wrap = wrap;    lodash.zip = zip; -  lodash.zipObject = zipObject;    // assign aliases    lodash.all = every;    lodash.any = some;    lodash.collect = map;    lodash.detect = find; +  lodash.drop = rest;    lodash.each = forEach;    lodash.foldl = reduce;    lodash.foldr = reduceRight; @@ -4176,37 +4110,30 @@    /*--------------------------------------------------------------------------*/ -  // assign private `LoDash` constructor's prototype -  LoDash.prototype = lodash.prototype; - -  // add all static functions to `LoDash.prototype` +  // add all static functions to `lodash.prototype`    mixin(lodash); -  // add `LoDash.prototype.chain` after calling `mixin()` to avoid overwriting +  // add `lodash.prototype.chain` after calling `mixin()` to avoid overwriting    // it with the wrapped `lodash.chain` -  LoDash.prototype.chain = wrapperChain; -  LoDash.prototype.value = wrapperValue; +  lodash.prototype.chain = wrapperChain; +  lodash.prototype.value = wrapperValue;    // add all mutator Array functions to the wrapper.    forEach(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(methodName) {      var func = ArrayProto[methodName]; -    LoDash.prototype[methodName] = function() { -      var value = this._wrapped; +    lodash.prototype[methodName] = function() { +      var value = this.__wrapped__;        func.apply(value, arguments); -      // Firefox < 10, IE compatibility mode, and IE < 9 have buggy Array -      // `shift()` and `splice()` functions that fail to remove the last element, -      // `value[0]`, of array-like objects even though the `length` property is -      // set to `0`. The `shift()` method is buggy in IE 8 compatibility mode, -      // while `splice()` is buggy regardless of mode in IE < 9 and buggy in -      // compatibility mode in IE 9. -      if (value.length === 0) { +      // avoid array-like object bugs with `Array#shift` and `Array#splice` in +      // Firefox < 10 and IE < 9 +      if (hasObjectSpliceBug && value.length === 0) {          delete value[0];        } -      if (this._chain) { -        value = new LoDash(value); -        value._chain = true; +      if (this.__chain__) { +        value = new lodash(value); +        value.__chain__ = true;        }        return value;      }; @@ -4216,13 +4143,13 @@    forEach(['concat', 'join', 'slice'], function(methodName) {      var func = ArrayProto[methodName]; -    LoDash.prototype[methodName] = function() { -      var value = this._wrapped, +    lodash.prototype[methodName] = function() { +      var value = this.__wrapped__,            result = func.apply(value, arguments); -      if (this._chain) { -        result = new LoDash(result); -        result._chain = true; +      if (this.__chain__) { +        result = new lodash(result); +        result.__chain__ = true;        }        return result;      }; diff --git a/module/web/static/js/views/packageView.js b/module/web/static/js/views/packageView.js index 1fbcd0613..3b743b448 100644 --- a/module/web/static/js/views/packageView.js +++ b/module/web/static/js/views/packageView.js @@ -1,10 +1,12 @@ -define(['jquery', 'views/abstract/itemView', 'underscore', 'views/fileView', 'utils/lazyRequire'], +define(['jquery', 'views/abstract/itemView', 'underscore', 'views/fileView', 'utils/lazyRequire', 'flotpie'],      function($, itemView, _, fileView, lazyLoader) {      // Renders a single package item      return itemView.extend({          tagName: 'li', +        className: 'package-view', +        template: _.template($("#template-package").html()),          events: {              'click .load': 'load',              'click .delete': 'delete', @@ -22,14 +24,36 @@ define(['jquery', 'views/abstract/itemView', 'underscore', 'views/fileView', 'ut          },          onDestroy: function() { -            this.modal.off('filter:added', this.hide); // TODO +            this.model.off('filter:added', this.hide); // TODO          },          render: function() { -            this.$el.html('Package ' + this.model.get('pid') + ': ' + this.model.get('name')); -            this.$el.append($('<a class="load" href="#"> Load</a>')); -            this.$el.append($('<a class="delete" href="#"> Delete</a>')); -            this.$el.append($('<a class="show-dialog" href="#"> Show</a>')); +            this.$el.html(this.template(this.model.toJSON())); + +            var data = [ +             { label: "Series1", data: 30}, +             { label: "Series2", data: 90} +             ]; +            var pie = this.$('.package-graph'); +            $.plot(pie, data, +                { +                    series: { +                        pie: { +                            radius: 1, +                            show: true, +                            label: { +                                show: false +                            }, +                            offset: { +                              top: 0, +                              left: 0 +                            } +                        } +                    }, +                    legend: { +                        show: false +                    } +                });              if (this.model.isLoaded()) {                  var ul = $('<ul></ul>'); | 
