| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943 | <!DOCTYPE html><html><head>  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />  <title>The source code</title>  <link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />  <script type="text/javascript" src="../resources/prettify/prettify.js"></script>  <style type="text/css">    .highlight { display: block; background-color: #ddd; }  </style>  <script type="text/javascript">    function highlight() {      document.getElementById(location.hash.replace(/#/, "")).className = "highlight";    }  </script></head><body onload="prettyPrint(); highlight();">  <pre class="prettyprint lang-js"><span id='Ext-chart-axis-Axis'>/**</span> * @class Ext.chart.axis.Axis * * Defines axis for charts. The axis position, type, style can be configured. * The axes are defined in an axes array of configuration objects where the type, * field, grid and other configuration options can be set. To know more about how * to create a Chart please check the Chart class documentation. Here's an example for the axes part: * An example of axis for a series (in this case for an area chart that has multiple layers of yFields) could be: * *     axes: [{ *         type: 'Numeric', *         position: 'left', *         fields: ['data1', 'data2', 'data3'], *         title: 'Number of Hits', *         grid: { *             odd: { *                 opacity: 1, *                 fill: '#ddd', *                 stroke: '#bbb', *                 'stroke-width': 1 *             } *         }, *         minimum: 0 *     }, { *         type: 'Category', *         position: 'bottom', *         fields: ['name'], *         title: 'Month of the Year', *         grid: true, *         label: { *             rotate: { *                 degrees: 315 *             } *         } *     }] * * In this case we use a `Numeric` axis for displaying the values of the Area series and a `Category` axis for displaying the names of * the store elements. The numeric axis is placed on the left of the screen, while the category axis is placed at the bottom of the chart. * Both the category and numeric axes have `grid` set, which means that horizontal and vertical lines will cover the chart background. In the * category axis the labels will be rotated so they can fit the space better. */Ext.define('Ext.chart.axis.Axis', {    /* Begin Definitions */    extend: 'Ext.chart.axis.Abstract',    alternateClassName: 'Ext.chart.Axis',    requires: ['Ext.draw.Draw'],    /* End Definitions */<span id='Ext-chart-axis-Axis-cfg-grid'>    /**</span>     * @cfg {Boolean/Object} grid     * The grid configuration enables you to set a background grid for an axis.     * If set to *true* on a vertical axis, vertical lines will be drawn.     * If set to *true* on a horizontal axis, horizontal lines will be drawn.     * If both are set, a proper grid with horizontal and vertical lines will be drawn.     *     * You can set specific options for the grid configuration for odd and/or even lines/rows.     * Since the rows being drawn are rectangle sprites, you can set to an odd or even property     * all styles that apply to {@link Ext.draw.Sprite}. For more information on all the style     * properties you can set please take a look at {@link Ext.draw.Sprite}. Some useful style properties are `opacity`, `fill`, `stroke`, `stroke-width`, etc.     *     * The possible values for a grid option are then *true*, *false*, or an object with `{ odd, even }` properties     * where each property contains a sprite style descriptor object that is defined in {@link Ext.draw.Sprite}.     *     * For example:     *     *     axes: [{     *         type: 'Numeric',     *         position: 'left',     *         fields: ['data1', 'data2', 'data3'],     *         title: 'Number of Hits',     *         grid: {     *             odd: {     *                 opacity: 1,     *                 fill: '#ddd',     *                 stroke: '#bbb',     *                 'stroke-width': 1     *             }     *         }     *     }, {     *         type: 'Category',     *         position: 'bottom',     *         fields: ['name'],     *         title: 'Month of the Year',     *         grid: true     *     }]     *     */<span id='Ext-chart-axis-Axis-cfg-majorTickSteps'>    /**</span>     * @cfg {Number} majorTickSteps     * If `minimum` and `maximum` are specified it forces the number of major ticks to the specified value.     * If a number of major ticks is forced, it wont search for pretty numbers at the ticks.     */<span id='Ext-chart-axis-Axis-cfg-minorTickSteps'>    /**</span>     * @cfg {Number} minorTickSteps     * The number of small ticks between two major ticks. Default is zero.     */<span id='Ext-chart-axis-Axis-cfg-title'>    /**</span>     * @cfg {String} title     * The title for the Axis     */    // @private force min/max values from store    forceMinMax: false,<span id='Ext-chart-axis-Axis-cfg-dashSize'>    /**</span>     * @cfg {Number} dashSize     * The size of the dash marker. Default's 3.     */    dashSize: 3,<span id='Ext-chart-axis-Axis-cfg-position'>    /**</span>     * @cfg {String} position     * Where to set the axis. Available options are `left`, `bottom`, `right`, `top`. Default's `bottom`.     */    position: 'bottom',    // @private    skipFirst: false,<span id='Ext-chart-axis-Axis-cfg-length'>    /**</span>     * @cfg {Number} length     * Offset axis position. Default's 0.     */    length: 0,<span id='Ext-chart-axis-Axis-cfg-width'>    /**</span>     * @cfg {Number} width     * Offset axis width. Default's 0.     */    width: 0,<span id='Ext-chart-axis-Axis-cfg-adjustEnd'>    /**</span>     * @cfg {Boolean} adjustEnd     * Whether to adjust the label at the end of the axis.     */    adjustEnd: true,    majorTickSteps: false,    // @private    applyData: Ext.emptyFn,    getRange: function () {        var me = this,            chart = me.chart,            store = chart.getChartStore(),            data = store.data.items,            series = chart.series.items,            position = me.position,            boundedAxes,            seriesClasses = Ext.chart.series,            aggregations = [],            min = Infinity, max = -Infinity,            vertical = me.position === 'left' || me.position === 'right',            i, ln, ln2, j, k, dataLength = data.length, aggregates,            countedFields = {},            allFields = {},            excludable = true,            fields, fieldMap, record, field, value;        fields = me.fields;        for (j = 0, ln = fields.length; j < ln; j++) {            allFields[fields[j]] = true;        }        for (i = 0, ln = series.length; i < ln; i++) {            if (series[i].seriesIsHidden) {                continue;            }            if (!series[i].getAxesForXAndYFields) {                continue;            }            boundedAxes = series[i].getAxesForXAndYFields();            if (boundedAxes.xAxis && boundedAxes.xAxis !== position && boundedAxes.yAxis && boundedAxes.yAxis !== position) {                // If the series explicitly exclude current Axis, then exit.                continue;            }            if (seriesClasses.Bar && series[i] instanceof seriesClasses.Bar && !series[i].column) {                // If this is a horizontal bar series, then flip xField and yField.                fields = vertical ? Ext.Array.from(series[i].xField) : Ext.Array.from(series[i].yField);            } else {                fields = vertical ? Ext.Array.from(series[i].yField) : Ext.Array.from(series[i].xField);            }            if (me.fields.length) {                for (j = 0, ln2 = fields.length; j < ln2; j++) {                    if (allFields[fields[j]]) {                        break;                    }                }                if (j == ln2) {                    // Not matching fields, skipping this series.                    continue;                }            }            if (aggregates = series[i].stacked) {                // If this is a bar/column series, then it will be aggregated if it is of the same direction of the axis.                if (seriesClasses.Bar && series[i] instanceof seriesClasses.Bar) {                    if (series[i].column != vertical) {                        aggregates = false;                        excludable = false;                    }                }                // Otherwise it is stacked vertically                else if (!vertical) {                    aggregates = false;                    excludable = false;                }            }            if (aggregates) {                fieldMap = {};                for (j = 0; j < fields.length; j++) {                    if (excludable && series[i].__excludes && series[i].__excludes[j]) {                        continue;                    }                    if (!allFields[fields[j]]) {                        Ext.Logger.warn('Field `' + fields[j] + '` is not included in the ' + position + ' axis config.');                    }                    allFields[fields[j]] = fieldMap[fields[j]] = true;                }                aggregations.push({                    fields: fieldMap,                    value: 0                });            } else {                if (!fields || fields.length == 0) {                    fields = me.fields;                }                for (j = 0; j < fields.length; j++) {                    if (excludable && series[i].__excludes && series[i].__excludes[j]) {                        continue;                    }                    allFields[fields[j]] = countedFields[fields[j]] = true;                }            }        }        for (i = 0; i < dataLength; i++) {            record = data[i];            for (k = 0; k < aggregations.length; k++) {                aggregations[k].value = 0;            }            for (field in allFields) {                value = record.get(field);                if (isNaN(value)) {                    continue;                }                if (value === undefined) {                    value = 0;                }                if (countedFields[field]) {                    if (min > value) {                        min = value;                    }                    if (max < value) {                        max = value;                    }                }                for (k = 0; k < aggregations.length; k++) {                    if (aggregations[k].fields[field]) {                        aggregations[k].value += value;                        // If any aggregation is actually hit, then the min value should be at most 0.                        if (min > 0) {                            min = 0;                        }                        if (max < aggregations[k].value) {                            max = aggregations[k].value;                        }                    }                }            }        }        if (!isFinite(max)) {            max = me.prevMax || 0;        }        if (!isFinite(min)) {            min = me.prevMin || 0;        }        //normalize min max for snapEnds.        if (min != max && (max != Math.floor(max))) {            max = Math.floor(max) + 1;        }        if (!isNaN(me.minimum)) {            min = me.minimum;        }        if (!isNaN(me.maximum)) {            max = me.maximum;        }        if (min >= max) {            // snapEnds will return NaN if max >= min;            max = min + 1;        }        return {min: min, max: max};    },    // @private creates a structure with start, end and step points.    calcEnds: function () {        var me = this,            range = me.getRange(),            min = range.min,            max = range.max,            steps, prettyNumbers, out, changedRange;        steps = (Ext.isNumber(me.majorTickSteps) ? me.majorTickSteps + 1 : me.steps);        prettyNumbers = !(Ext.isNumber(me.maximum) && Ext.isNumber(me.minimum) && Ext.isNumber(me.majorTickSteps) && me.majorTickSteps > 0);        out = Ext.draw.Draw.snapEnds(min, max, steps, prettyNumbers);        if (Ext.isNumber(me.maximum)) {            out.to = me.maximum;            changedRange = true;        }        if (Ext.isNumber(me.minimum)) {            out.from = me.minimum;            changedRange = true;        }        if (me.adjustMaximumByMajorUnit) {            out.to = Math.ceil(out.to / out.step) * out.step;            changedRange = true;        }        if (me.adjustMinimumByMajorUnit) {            out.from = Math.floor(out.from / out.step) * out.step;            changedRange = true;        }        if (changedRange) {            out.steps = Math.ceil((out.to - out.from) / out.step);                    }        me.prevMin = (min == max ? 0 : min);        me.prevMax = max;        return out;    },<span id='Ext-chart-axis-Axis-method-drawAxis'>    /**</span>     * Renders the axis into the screen and updates its position.     */    drawAxis: function (init) {        var me = this,            i,             x = me.x,            y = me.y,            gutterX = me.chart.maxGutter[0],            gutterY = me.chart.maxGutter[1],            dashSize = me.dashSize,            subDashesX = me.minorTickSteps || 0,            subDashesY = me.minorTickSteps || 0,            length = me.length,            position = me.position,            inflections = [],            calcLabels = false,            stepCalcs = me.applyData(),            step = stepCalcs.step,            steps = stepCalcs.steps,            from = stepCalcs.from,            to = stepCalcs.to,            trueLength,            currentX,            currentY,            path,            dashesX,            dashesY,            delta;        //If no steps are specified        //then don't draw the axis. This generally happens        //when an empty store.        if (me.hidden || isNaN(step) || (from > to)) {            return;        }        me.from = stepCalcs.from;        me.to = stepCalcs.to;        if (position == 'left' || position == 'right') {            currentX = Math.floor(x) + 0.5;            path = ["M", currentX, y, "l", 0, -length];            trueLength = length - (gutterY * 2);        }        else {            currentY = Math.floor(y) + 0.5;            path = ["M", x, currentY, "l", length, 0];            trueLength = length - (gutterX * 2);        }        // Supports the case that we have only 1 record.        delta = steps && trueLength / steps;        dashesX = Math.max(subDashesX + 1, 0);        dashesY = Math.max(subDashesY + 1, 0);        if (me.type == 'Numeric' || me.type == 'Time') {            calcLabels = true;            me.labels = [stepCalcs.from];        }        if (position == 'right' || position == 'left') {            currentY = y - gutterY;            currentX = x - ((position == 'left') * dashSize * 2);            while (currentY >= y - gutterY - trueLength) {                path.push("M", currentX, Math.floor(currentY) + 0.5, "l", dashSize * 2 + 1, 0);                if (currentY != y - gutterY) {                    for (i = 1; i < dashesY; i++) {                        path.push("M", currentX + dashSize, Math.floor(currentY + delta * i / dashesY) + 0.5, "l", dashSize + 1, 0);                    }                }                inflections.push([ Math.floor(x), Math.floor(currentY) ]);                currentY -= delta;                if (calcLabels) {                    me.labels.push(me.labels[me.labels.length - 1] + step);                }                if (delta === 0) {                    break;                }            }            if (Math.round(currentY + delta - (y - gutterY - trueLength))) {                path.push("M", currentX, Math.floor(y - length + gutterY) + 0.5, "l", dashSize * 2 + 1, 0);                for (i = 1; i < dashesY; i++) {                    path.push("M", currentX + dashSize, Math.floor(y - length + gutterY + delta * i / dashesY) + 0.5, "l", dashSize + 1, 0);                }                inflections.push([ Math.floor(x), Math.floor(currentY) ]);                if (calcLabels) {                    me.labels.push(me.labels[me.labels.length - 1] + step);                }            }        } else {            currentX = x + gutterX;            currentY = y - ((position == 'top') * dashSize * 2);            while (currentX <= x + gutterX + trueLength) {                path.push("M", Math.floor(currentX) + 0.5, currentY, "l", 0, dashSize * 2 + 1);                if (currentX != x + gutterX) {                    for (i = 1; i < dashesX; i++) {                        path.push("M", Math.floor(currentX - delta * i / dashesX) + 0.5, currentY, "l", 0, dashSize + 1);                    }                }                inflections.push([ Math.floor(currentX), Math.floor(y) ]);                currentX += delta;                if (calcLabels) {                    me.labels.push(me.labels[me.labels.length - 1] + step);                }                if (delta === 0) {                    break;                }            }            if (Math.round(currentX - delta - (x + gutterX + trueLength))) {                path.push("M", Math.floor(x + length - gutterX) + 0.5, currentY, "l", 0, dashSize * 2 + 1);                for (i = 1; i < dashesX; i++) {                    path.push("M", Math.floor(x + length - gutterX - delta * i / dashesX) + 0.5, currentY, "l", 0, dashSize + 1);                }                inflections.push([ Math.floor(currentX), Math.floor(y) ]);                if (calcLabels) {                    me.labels.push(me.labels[me.labels.length - 1] + step);                }            }        }        // the label on index "inflections.length-1" is the last label that gets rendered        if (calcLabels) {            me.labels[inflections.length - 1] = +(me.labels[inflections.length - 1]).toFixed(10);        }        if (!me.axis) {            me.axis = me.chart.surface.add(Ext.apply({                type: 'path',                path: path            }, me.axisStyle));        }        me.axis.setAttributes({            path: path        }, true);        me.inflections = inflections;        if (!init && me.grid) {            me.drawGrid();        }        me.axisBBox = me.axis.getBBox();        me.drawLabel();    },<span id='Ext-chart-axis-Axis-method-drawGrid'>    /**</span>     * Renders an horizontal and/or vertical grid into the Surface.     */    drawGrid: function () {        var me = this,            surface = me.chart.surface,            grid = me.grid,            odd = grid.odd,            even = grid.even,            inflections = me.inflections,            ln = inflections.length - ((odd || even) ? 0 : 1),            position = me.position,            gutter = me.chart.maxGutter,            width = me.width - 2,            point, prevPoint,            i = 1,            path = [], styles, lineWidth, dlineWidth,            oddPath = [], evenPath = [];        if ((gutter[1] !== 0 && (position == 'left' || position == 'right')) ||            (gutter[0] !== 0 && (position == 'top' || position == 'bottom'))) {            i = 0;            ln++;        }        for (; i < ln; i++) {            point = inflections[i];            prevPoint = inflections[i - 1];            if (odd || even) {                path = (i % 2) ? oddPath : evenPath;                styles = ((i % 2) ? odd : even) || {};                lineWidth = (styles.lineWidth || styles['stroke-width'] || 0) / 2;                dlineWidth = 2 * lineWidth;                if (position == 'left') {                    path.push("M", prevPoint[0] + 1 + lineWidth, prevPoint[1] + 0.5 - lineWidth,                        "L", prevPoint[0] + 1 + width - lineWidth, prevPoint[1] + 0.5 - lineWidth,                        "L", point[0] + 1 + width - lineWidth, point[1] + 0.5 + lineWidth,                        "L", point[0] + 1 + lineWidth, point[1] + 0.5 + lineWidth, "Z");                }                else if (position == 'right') {                    path.push("M", prevPoint[0] - lineWidth, prevPoint[1] + 0.5 - lineWidth,                        "L", prevPoint[0] - width + lineWidth, prevPoint[1] + 0.5 - lineWidth,                        "L", point[0] - width + lineWidth, point[1] + 0.5 + lineWidth,                        "L", point[0] - lineWidth, point[1] + 0.5 + lineWidth, "Z");                }                else if (position == 'top') {                    path.push("M", prevPoint[0] + 0.5 + lineWidth, prevPoint[1] + 1 + lineWidth,                        "L", prevPoint[0] + 0.5 + lineWidth, prevPoint[1] + 1 + width - lineWidth,                        "L", point[0] + 0.5 - lineWidth, point[1] + 1 + width - lineWidth,                        "L", point[0] + 0.5 - lineWidth, point[1] + 1 + lineWidth, "Z");                }                else {                    path.push("M", prevPoint[0] + 0.5 + lineWidth, prevPoint[1] - lineWidth,                        "L", prevPoint[0] + 0.5 + lineWidth, prevPoint[1] - width + lineWidth,                        "L", point[0] + 0.5 - lineWidth, point[1] - width + lineWidth,                        "L", point[0] + 0.5 - lineWidth, point[1] - lineWidth, "Z");                }            } else {                if (position == 'left') {                    path = path.concat(["M", point[0] + 0.5, point[1] + 0.5, "l", width, 0]);                }                else if (position == 'right') {                    path = path.concat(["M", point[0] - 0.5, point[1] + 0.5, "l", -width, 0]);                }                else if (position == 'top') {                    path = path.concat(["M", point[0] + 0.5, point[1] + 0.5, "l", 0, width]);                }                else {                    path = path.concat(["M", point[0] + 0.5, point[1] - 0.5, "l", 0, -width]);                }            }        }        if (odd || even) {            if (oddPath.length) {                if (!me.gridOdd && oddPath.length) {                    me.gridOdd = surface.add({                        type: 'path',                        path: oddPath                    });                }                me.gridOdd.setAttributes(Ext.apply({                    path: oddPath,                    hidden: false                }, odd || {}), true);            }            if (evenPath.length) {                if (!me.gridEven) {                    me.gridEven = surface.add({                        type: 'path',                        path: evenPath                    });                }                me.gridEven.setAttributes(Ext.apply({                    path: evenPath,                    hidden: false                }, even || {}), true);            }        }        else {            if (path.length) {                if (!me.gridLines) {                    me.gridLines = me.chart.surface.add({                        type: 'path',                        path: path,                        "stroke-width": me.lineWidth || 1,                        stroke: me.gridColor || '#ccc'                    });                }                me.gridLines.setAttributes({                    hidden: false,                    path: path                }, true);            }            else if (me.gridLines) {                me.gridLines.hide(true);            }        }    },    // @private    getOrCreateLabel: function (i, text) {        var me = this,            labelGroup = me.labelGroup,            textLabel = labelGroup.getAt(i),            surface = me.chart.surface;        if (textLabel) {            if (text != textLabel.attr.text) {                textLabel.setAttributes(Ext.apply({                    text: text                }, me.label), true);                textLabel._bbox = textLabel.getBBox();            }        }        else {            textLabel = surface.add(Ext.apply({                group: labelGroup,                type: 'text',                x: 0,                y: 0,                text: text            }, me.label));            surface.renderItem(textLabel);            textLabel._bbox = textLabel.getBBox();        }        //get untransformed bounding box        if (me.label.rotation) {            textLabel.setAttributes({                rotation: {                    degrees: 0                }            }, true);            textLabel._ubbox = textLabel.getBBox();            textLabel.setAttributes(me.label, true);        } else {            textLabel._ubbox = textLabel._bbox;        }        return textLabel;    },    rect2pointArray: function (sprite) {        var surface = this.chart.surface,            rect = surface.getBBox(sprite, true),            p1 = [rect.x, rect.y],            p1p = p1.slice(),            p2 = [rect.x + rect.width, rect.y],            p2p = p2.slice(),            p3 = [rect.x + rect.width, rect.y + rect.height],            p3p = p3.slice(),            p4 = [rect.x, rect.y + rect.height],            p4p = p4.slice(),            matrix = sprite.matrix;        //transform the points        p1[0] = matrix.x.apply(matrix, p1p);        p1[1] = matrix.y.apply(matrix, p1p);        p2[0] = matrix.x.apply(matrix, p2p);        p2[1] = matrix.y.apply(matrix, p2p);        p3[0] = matrix.x.apply(matrix, p3p);        p3[1] = matrix.y.apply(matrix, p3p);        p4[0] = matrix.x.apply(matrix, p4p);        p4[1] = matrix.y.apply(matrix, p4p);        return [p1, p2, p3, p4];    },    intersect: function (l1, l2) {        var r1 = this.rect2pointArray(l1),            r2 = this.rect2pointArray(l2);        return !!Ext.draw.Draw.intersect(r1, r2).length;    },    drawHorizontalLabels: function () {        var me = this,            labelConf = me.label,            floor = Math.floor,            max = Math.max,            axes = me.chart.axes,            insetPadding = me.chart.insetPadding,            position = me.position,            inflections = me.inflections,            ln = inflections.length,            labels = me.labels,            maxHeight = 0,            ratio,            bbox, point, prevLabel, prevLabelId,            adjustEnd = me.adjustEnd,            hasLeft = axes.findIndex('position', 'left') != -1,            hasRight = axes.findIndex('position', 'right') != -1,            textLabel, text,            last, x, y, i, firstLabel;        last = ln - 1;        //get a reference to the first text label dimensions        point = inflections[0];        firstLabel = me.getOrCreateLabel(0, me.label.renderer(labels[0]));        ratio = Math.floor(Math.abs(Math.sin(labelConf.rotate && (labelConf.rotate.degrees * Math.PI / 180) || 0)));        for (i = 0; i < ln; i++) {            point = inflections[i];            text = me.label.renderer(labels[i]);            textLabel = me.getOrCreateLabel(i, text);            bbox = textLabel._bbox;            maxHeight = max(maxHeight, bbox.height + me.dashSize + me.label.padding);            x = floor(point[0] - (ratio ? bbox.height : bbox.width) / 2);            if (adjustEnd && me.chart.maxGutter[0] == 0) {                if (i == 0 && !hasLeft) {                    x = point[0];                }                else if (i == last && !hasRight) {                    x = Math.min(x, point[0] - bbox.width + insetPadding);                }            }            if (position == 'top') {                y = point[1] - (me.dashSize * 2) - me.label.padding - (bbox.height / 2);            }            else {                y = point[1] + (me.dashSize * 2) + me.label.padding + (bbox.height / 2);            }            textLabel.setAttributes({                hidden: false,                x: x,                y: y            }, true);            // Skip label if there isn't available minimum space            if (i != 0 && (me.intersect(textLabel, prevLabel)                || me.intersect(textLabel, firstLabel))) {                if (i === last && prevLabelId !== 0) {                    prevLabel.hide(true);                } else {                    textLabel.hide(true);                    continue;                }            }            prevLabel = textLabel;            prevLabelId = i;        }        return maxHeight;    },    drawVerticalLabels: function () {        var me = this,            inflections = me.inflections,            position = me.position,            ln = inflections.length,            chart = me.chart,            insetPadding = chart.insetPadding,            labels = me.labels,            maxWidth = 0,            max = Math.max,            floor = Math.floor,            ceil = Math.ceil,            axes = me.chart.axes,            gutterY = me.chart.maxGutter[1],            bbox, point, prevLabel, prevLabelId,            hasTop = axes.findIndex('position', 'top') != -1,            hasBottom = axes.findIndex('position', 'bottom') != -1,            adjustEnd = me.adjustEnd,            textLabel, text,            last = ln - 1, x, y, i;        for (i = 0; i < ln; i++) {            point = inflections[i];            text = me.label.renderer(labels[i]);            textLabel = me.getOrCreateLabel(i, text);            bbox = textLabel._bbox;            maxWidth = max(maxWidth, bbox.width + me.dashSize + me.label.padding);            y = point[1];            if (adjustEnd && gutterY < bbox.height / 2) {                if (i == last && !hasTop) {                    y = Math.max(y, me.y - me.length + ceil(bbox.height / 2) - insetPadding);                }                else if (i == 0 && !hasBottom) {                    y = me.y + gutterY - floor(bbox.height / 2);                }            }            if (position == 'left') {                x = point[0] - bbox.width - me.dashSize - me.label.padding - 2;            }            else {                x = point[0] + me.dashSize + me.label.padding + 2;            }            textLabel.setAttributes(Ext.apply({                hidden: false,                x: x,                y: y            }, me.label), true);            // Skip label if there isn't available minimum space            if (i != 0 && me.intersect(textLabel, prevLabel)) {                if (i === last && prevLabelId !== 0) {                    prevLabel.hide(true);                } else {                    textLabel.hide(true);                    continue;                }            }            prevLabel = textLabel;            prevLabelId = i;        }        return maxWidth;    },<span id='Ext-chart-axis-Axis-method-drawLabel'>    /**</span>     * Renders the labels in the axes.     */    drawLabel: function () {        var me = this,            position = me.position,            labelGroup = me.labelGroup,            inflections = me.inflections,            maxWidth = 0,            maxHeight = 0,            ln, i;        if (position == 'left' || position == 'right') {            maxWidth = me.drawVerticalLabels();        } else {            maxHeight = me.drawHorizontalLabels();        }        // Hide unused bars        ln = labelGroup.getCount();        i = inflections.length;        for (; i < ln; i++) {            labelGroup.getAt(i).hide(true);        }        me.bbox = {};        Ext.apply(me.bbox, me.axisBBox);        me.bbox.height = maxHeight;        me.bbox.width = maxWidth;        if (Ext.isString(me.title)) {            me.drawTitle(maxWidth, maxHeight);        }    },<span id='Ext-chart-axis-Axis-method-setTitle'>    /**</span>     * Updates the {@link #title} of this axis.     * @param {String} title     */    setTitle: function (title) {        this.title = title;        this.drawLabel();    },    // @private draws the title for the axis.    drawTitle: function (maxWidth, maxHeight) {        var me = this,            position = me.position,            surface = me.chart.surface,            displaySprite = me.displaySprite,            title = me.title,            rotate = (position == 'left' || position == 'right'),            x = me.x,            y = me.y,            base, bbox, pad;        if (displaySprite) {            displaySprite.setAttributes({text: title}, true);        } else {            base = {                type: 'text',                x: 0,                y: 0,                text: title            };            displaySprite = me.displaySprite = surface.add(Ext.apply(base, me.axisTitleStyle, me.labelTitle));            surface.renderItem(displaySprite);        }        bbox = displaySprite.getBBox();        pad = me.dashSize + me.label.padding;        if (rotate) {            y -= ((me.length / 2) - (bbox.height / 2));            if (position == 'left') {                x -= (maxWidth + pad + (bbox.width / 2));            }            else {                x += (maxWidth + pad + bbox.width - (bbox.width / 2));            }            me.bbox.width += bbox.width + 10;        }        else {            x += (me.length / 2) - (bbox.width * 0.5);            if (position == 'top') {                y -= (maxHeight + pad + (bbox.height * 0.3));            }            else {                y += (maxHeight + pad + (bbox.height * 0.8));            }            me.bbox.height += bbox.height + 10;        }        displaySprite.setAttributes({            translate: {                x: x,                y: y            }        }, true);    }});</pre></body></html>
 |