Best Python code snippet using autotest_python
d3SVGUtil.js
Source:d3SVGUtil.js  
1// The point of this util file2// is to centralize common functions on3// graphs such as building axes, etc.4// it relies on the d3 package5// all 'frac' means value between 0 and 16function getSVG(svg_id) {7        //svg_id is str8     9        let svg_d3 = d3.select("#" + svg_id);10        return svg_d311}12function clearSVG(svg_id) {13        //svg_id is str14        let svg_d3 = getSVG(svg_id)15        svg_d3.selectAll("*").remove();16}17function makeSVGAxesAndLabels(svg_id, svg_axes_info, draw_x_axis=true,18                              draw_y_axis=true, draw_x_label=true,19                              draw_y_label=true) {20    // This function takes the standard form of 21    // SVG graph axes inputs as defined in this package22    // and creates the axis and the labels.23   24    // Args:25    //  svg_id: (str) DOM id of svg26    //  svg_axes_info: Object27    //     graph_i (Where points are plotted)28    //          blc <x frac from left, y frac from top>29    //          trc <x frac from left, y frac from top>30    //     x_i: 31    //          x_title_i:32    //              blc: <x frac from left, y frac from top>33    //              trc: <x frac from left, y frac from top>34    //              style_i:35    //                  name (Str) -> value (str)36    //              label: str37    //          x_axis_i:38    //              lc: <x frac from left, y frac from top>39    //              len_frac: Number (frac length of axis)40    //              style_i (as above in x_title_i)41    //      y_i: Same as x_i with property "bc" instead of "lc"42    let d3svg = getSVG(svg_id)43    // we get a document object model version of the svg for dimensions44    let svg_dobj = document.getElementById(svg_id)45    let dWidth = svg_dobj.clientWidth46    let dHeight = svg_dobj.clientHeight47    let x_i = svg_axes_info["x_i"]48    let y_i = svg_axes_info["y_i"]49   // We create the axis lengths 50   let x_axis_len = x_i["x_axis_i"]["len_frac"]*dWidth;51   let y_axis_len = y_i["y_axis_i"]["len_frac"]*dHeight;52    if (draw_x_axis) {53         // We get the location of the bottom left corner of where the 54        // x values start55         let x_axis_org = [x_i["x_axis_i"]["lc"][0] * dWidth, 56                           x_i["x_axis_i"]["lc"][1] * dHeight]57        //Making X axis line (no ticks)58        makeLine(d3svg, "black", x_axis_org[0], x_axis_org[1], 59                 x_axis_org[0] + x_axis_len, x_axis_org[1], 60                 x_i["x_axis_i"]["style_i"]["strokeWidth"]);61    }62   if (draw_y_axis) {63         // We get the location of the bottom left corner of where the 64        // y values start65        let y_axis_org = [y_i["y_axis_i"]["bc"][0] * dWidth, 66                           y_i["y_axis_i"]["bc"][1] * dHeight]67        //Making Y axis line (no ticks)68        makeLine(d3svg, "black", y_axis_org[0], y_axis_org[1], 69                 y_axis_org[0], y_axis_org[1] - y_axis_len, 70                 y_i["y_axis_i"]["style_i"]["strokeWidth"]);71    }72   73   let graph_blc = [svg_axes_info["graph_i"]["blc"][0]*dWidth,74                    svg_axes_info["graph_i"]["blc"][1]*dHeight];75   let graph_trc = [svg_axes_info["graph_i"]["trc"][0]*dWidth,76                    svg_axes_info["graph_i"]["trc"][1]*dHeight];77    // Labels78    if (draw_x_label) {79         let xt = x_i["x_title_i"]80        // X-Axis Text Label81        makeText(d3svg, xt["style_i"]["fontWeight"], xt["style_i"]["fontSize"],82                 graph_blc[0] + x_axis_len*xt["blc"][0],83                dHeight*xt["blc"][1], xt["label"], xt["style_i"]["fontColor"])84    }85    if (draw_y_label) {86        let yt = y_i["y_title_i"]87         makeYAxisLabel(d3svg, dWidth, dHeight, yt);88    }89    ret_d = {90        "x_axis_len": x_axis_len,91        "y_axis_len": y_axis_len,92        "graph_blc": graph_blc,93        "graph_trc": graph_trc94    }95    if (draw_x_axis)  {96        ret_d["x_axis_start"] = [x_i["x_axis_i"]["lc"][0] * dWidth, 97                                x_i["x_axis_i"]["lc"][1] * dHeight]98    }99    if (draw_y_axis)  {100        ret_d["y_axis_start"] = [y_i["y_axis_i"]["bc"][0] * dWidth, 101                                y_i["y_axis_i"]["bc"][1] * dHeight]102    }103    return ret_d104}105function makeDashedLine(d3svg, start_coordinates, end_coordinates,106                        dash_length, break_length,107                        color, stroke_width) {108    /*109     * In order to draw a dashed line we need110     *      the slope of the line111     *112     *113     *114     */115        116        let stroke_break_str = dash_length.toString() + ", " + 117                                break_length.toString();118        119        d3svg.append("line")120        .attr('x1', start_coordinates[0])121        .attr('y1', start_coordinates[1])122        .attr('x2', end_coordinates[0])123        .attr('y2', end_coordinates[1])124        .attr("stroke", color)125        .attr("stroke-width", stroke_width)126        .style("stroke-dasharray", (stroke_break_str));  127        // ^ This line here!!128}129function makeYAxisLabel(d3svg, svgWidth, svgHeight, yt) {130    // yt: 131    //              blc: <x frac from left, y frac from top>132    //              trc: <x frac from left, y frac from top>133    //              style_i:134    //                  name (Str) -> value (str)135    //              label: str136            // Yx and Yy refer to the Y label location x and y coordinates137            let Yx = svgWidth * (yt["blc"][0] + yt["trc"][0]) * (0.5)138            let Yy = svgHeight * (yt["blc"][1] + yt["trc"][1]) * (0.5)139            let ax_tsl = Yx.toString() + "," + Yy.toString()140            141            let font_weight = yt["style_i"]["fontWeight"]142            if (font_weight == null) {143                font_weight = "bold"144            }145            let font_color = yt["style_i"]["fontColor"]146            if (font_color == null) {147                font_color = "black"148            }149            150            d3svg.append('text')151                .attr('font-weight', font_weight )152                .attr("transform", "translate(" + ax_tsl + ") rotate(270)")153                .attr('font-size', yt["style_i"]["fontSize"])154                .attr('fill', font_color)155                .text(yt["label"]);156}157function makeAxisTicksAndTickLabels(svg_id, axis_type,158                                    axis_start_loc, axis_end_loc,159                                    tick_info_list) {160    // This function takes the starting and ending points of 161    // an axis and adds ticks with information coming162    // from the 'tick_info_list'163    //164    // Args:165    //      svg_id: (str) The id of the svg object we want to add to166    //      axis_start_loc: list< x_val (Number), y_val (Number)>167    //      axis_end_loc: list< x_val (Number), y_val (Number)>168    //      axis_type: (str) fixed vocab ["x" (horizontal), or "y" (vertical)]169    //      tick_info_list: list<tick_info>170    //          tick_info: list<frac (Number), tick_length (Number), tick_style,171    //                          tick_color, tick_stroke_width (Number),172    //                          label_style (s), label_text (s), label_color (s)173    //                          label_font_size (Number), label_dist (Number)>174    //              frac: The TOTAL fraction of the axis where this tick lies175    //              tick_length: The length of the tick within the SVG176    //              tick_style: str fixed vocab: ["top", "cross", "bottom"]177    //                  this refers to if the tick points up from the axis,178    //                  crosses the axis, or protrudes from the bottom of179    //                  the axis.180    //              tick_color: str (CSS color)181    //              label_style: str fixed vocab ["above", "below"]182    //              label_text: str The actual label value.183   184    let d3svg = getSVG(svg_id)185    let axis_length = null186    if (axis_type == "x") {187        axis_length = axis_end_loc[0] - axis_start_loc[0]188    } else if (axis_type == "y") {189        axis_length = axis_start_loc[1] - axis_end_loc[1]190    } else {191        throw "unknown axis type: " + axis_type192    }193    // Here we add the ticks194    for (let i=0; i < tick_info_list.length; i++) {195        let crnt_tick_info = tick_info_list[i];196        // This variable holds the [x,y] location of tick center197        let tick_axis_loc = null198        if (axis_type == "x") {199           tick_axis_loc = [axis_start_loc[0] + crnt_tick_info[0]*axis_length,200                            axis_start_loc[1]]201        } else if (axis_type == "y") {202           tick_axis_loc = [axis_start_loc[0],203                            axis_start_loc[1] - crnt_tick_info[0]*axis_length ]204        }205        addTickAndLabel(d3svg, axis_type, crnt_tick_info, tick_axis_loc)206    }207}208function addTickAndLabel(d3svg, axis_type, tick_info, tick_axis_loc) {209    // Info regarding params comes from function 210    //  makeAxisTicksAndTickLabels211    //  tick_axis_loc: list<x (Num), y (Num)>212    //  tick_info: list<frac (Number), tick_length (Number), tick_style,213    //                  tick_color, tick_stroke_width,214    //                  label_style, label_text, label_color,215    //                  label_font_size, label_dist>216    let tick_start_loc = null217    let tick_end_loc = null218    if (axis_type == "x") {219        if (tick_info[2] == "top") {220            tick_start_loc = tick_axis_loc221            tick_end_loc = [tick_axis_loc[0], tick_axis_loc[1] - tick_info[1]]222        } else if (tick_info[2] == "cross") {223            tick_start_loc = [tick_axis_loc[0], tick_axis_loc[1] + tick_info[1]/2]224            tick_end_loc = [tick_axis_loc[0], tick_axis_loc[1] - tick_info[1]/2]225        } else if (tick_info[2] == "bottom") {226            tick_start_loc = tick_axis_loc227            tick_end_loc = [tick_axis_loc[0], tick_axis_loc[1] + tick_info[1]]228        }229        makeLine(d3svg, tick_info[3], tick_start_loc[0],230                tick_start_loc[1], tick_end_loc[0], 231                tick_end_loc[1], tick_info[4])232        // Label - checking label style ("top" or "bottom")233        let init_label_loc = null;234        if (tick_info[5] == "top") {235            init_label_loc = [tick_axis_loc[0], tick_axis_loc[1] - tick_info[9]]236        } else if (tick_info[5] == "bottom") {237            init_label_loc = [tick_axis_loc[0], tick_axis_loc[1] + tick_info[9]]238        } else {239            console.log(" label style needs to be top or bottom!")240        }241        makeText(d3svg, "normal", tick_info[8], init_label_loc[0] - 3,242                 init_label_loc[1], tick_info[6], tick_info[7])243    } else if (axis_type == "y") {244        if (tick_info[2] == "top") {245            tick_start_loc = tick_axis_loc246            tick_end_loc = [tick_axis_loc[0] + tick_info[1], tick_axis_loc[1]]247        } else if (tick_info[2] == "cross") {248            tick_start_loc = [tick_axis_loc[0] - tick_info[1]/2, tick_axis_loc[1]]249            tick_end_loc = [tick_axis_loc[0] + tick_info[1]/2, tick_axis_loc[1]]250        } else if (tick_info[2] == "bottom") {251            tick_start_loc = tick_axis_loc252            tick_end_loc = [tick_axis_loc[0] - tick_info[1], tick_axis_loc[1]]253        }254        255        makeLine(d3svg, tick_info[3], tick_start_loc[0],256                tick_start_loc[1], tick_end_loc[0], 257                tick_end_loc[1], tick_info[4])258        // Label - checking label style ("top" or "bottom")259        let init_label_loc = null;260        if (tick_info[5] == "top") {261            init_label_loc = [tick_axis_loc[0] + tick_info[9], tick_axis_loc[1]]262        } else if (tick_info[5] == "bottom") {263            let label_distance_addition = 0;264            if (tick_info[6][0] == "-") {265                label_distance_addition = 3;266            }267            init_label_loc = [tick_axis_loc[0] -  tick_info[9] - label_distance_addition,268                            tick_axis_loc[1]]269        }270        makeText(d3svg, "normal", tick_info[8], init_label_loc[0],271                 init_label_loc[1] + 3, tick_info[6], tick_info[7])272    }273}274function makeLine(d3svg, color, x1, y1, x2, y2, stroke_width ) {275    /*276     * Args: 277     *  d3svg: A d3 svg object278     *  color: str, like "black"279     *  x1 - y2, Numbers280     *  stroke_width: width of line, Number281     *282     * Note: We need to make sure the numbers are relatively close to integers283     */284               return d3svg.append('line')285                   .attr('x1', x1.toFixed(2))286                   .attr('y1', y1.toFixed(2))287                   .attr('x2', x2.toFixed(2))288                   .attr('y2', y2.toFixed(2))289                   .attr('stroke', color)290                   .attr('stroke-width', stroke_width)291                   .attr('position', 'absolute')292                   ;293}294function makeText(d3svg, font_weight, font_size, x, y, text_str, font_color, id_txt=null) {295    /*296     *  Args:297     *  298     *      d3svg: A d3 svg object299     *      font_weight: (str) like "bold", "normal",300     *      font_size, x, y: Number301     *      text_str: (str) Text you want to make302     *303     */304    if (font_color == null) {305        font_color = "black"306    }307    if (text_str == null) {308        text_str = "Default Text"309    }310    if (id_txt == null) {311        d3svg.append('text')312            .attr('font-weight', font_weight)313            .attr('font-size', font_size)314            .attr('fill', font_color)315            .attr('x', x)316            .attr('y', y)317            .text(text_str);318    } else {319         d3svg.append('text')320            .attr('font-weight', font_weight)321            .attr('font-size', font_size)322            .attr('fill', font_color)323            .attr('x', x)324            .attr('y', y)325            .attr('id', id_txt) 326            .text(text_str);327    }328}329function GetProperTicks(start_val, end_val) {330    /*331    This function is to get properly spread ticks between332    two INTEGER values where end_val > start_val333    Min ticks in axis is ~ 8, Max is ~ 16334    Note: dif must be an integer value335    start_val: Number336    end_val Number337    Returns:338        ticks_list = [start_val, start_val + subdivs, start_val + 2subdivs,..., end_val]339    */340    // Checking inputs341    if ( (Math.floor(start_val) != start_val) || 342        (Math.floor(end_val) != end_val) ) {343            console.log(start_val)344            console.log(end_val)345            throw "start_val and end_val must be integers"346    }347    let tick_values = null;348    let subdivs = null;349    let dif = end_val - start_val350    if (dif >= 16) {351        subdivs = ConvertValueIntoSubDivs(dif);352    } else {353        if (dif >= 8) {354            subdivs = 1355        } else if (dif >= 4) {356            subdivs = .5;357        } else if (dif >= 2) {358            subdivs = .25;359        } else if (dif == 1) {360            subdivs = .1361        } else {362            throw "end_val must be greater than start_val in GetProperTicks"363        }364    }365    tick_values = GetTickValues(start_val, end_val, subdivs);366    return tick_values;367}368function ConvertValueIntoSubDivs(Val) {369    /*370     *371     * Args: 372     *      Val is a positive integer373    Important Questions:374    1. Max ticks in axis assuming no more than 3 digits per value?375        Answer: 16376    2. Min ticks in axis?377        Answer: 8378    Meaning: 379        if N = d * 10^n, d > 5 implies division is 5 * 10^(n-2)380        4 < d < 5 implies division is  2.5 * 10^(n-2)381        2 < d < 4 implies division is  2 * 10^(n-2)382        1 < d < 2 implies division is 1 * 10^(n-2)383    */384    let val_info = BaseNotation(Val, 10, 20);385    let dig = val_info[0];386    let power = val_info[1];387    let subdivs = null;388    if (power === 0) {389        subdivs = 1 ;390    } else {391            if (dig >=8) { 392            subdivs =  Math.pow(10,power);393            } else if (dig >= 6) { 394            subdivs = 5 * Math.pow(10, power-1);395            } else {396            subdivs = Math.floor(dig) * Math.pow(10, power-1);397            }398    }399    return subdivs;400}401function BaseNotation(N, base, max_power) {402    /* We get power of base and digit multiplier.403        Eg. if N = 346, base = 10 we return [3.46, 2] because404            3.46 * 10^2 = 346 405    Args:406        N: int, number to find bounds for. MUST BE > 0407        base: int 408        max_power: int (limit so function doesn't run forever with while loop)409    Returns:410        [a, b (power of 10)] where a*10^b = N411        OR [-1, -1] if it failed for some reason412    */413    if (N <= 0) {414        return [-1, -1]415    }416    for (i=0; i < max_power + 1 ;i++){417        if (N >= Math.pow(base,i) && N < Math.pow(base,i+1)) {418            return [ N/Math.pow(base,i), i]419        }420    }421    return [-1, -1]422}423function GetTickValues(start_val, end_val, subdivs) {424    /* Simple function that adds subdivs from start val 425     * until it reaches end_val426     * We go from a value and subdivs to actual graph ticks427    Args:428        start_val: (int)429        end_val: (int)430        subdivs: (int)431    Returns:432        ticks_list = [start_val, start_val + subdivs, start_val + 2subdivs,...]433    Specifically, this function starts from start_val and adds subdiv until reaching434        end_val. Note that difference between start_val and end_val does not 435        need t436    */437    // First we get a list of just each tick, not the start and end ticks (no dbl)438    let init_tick_list = [start_val];439    let crnt_val = start_val + subdivs;440    while (crnt_val < end_val){441        init_tick_list.push(crnt_val);442        crnt_val = crnt_val + subdivs;443    }444    init_tick_list.push(end_val);445    return init_tick_list;446}447function makeStandardTickInfoListFromTicks(tick_list, label_font_size,448                                            tick_length=8, tick_width=2,449                                           tick_style="bottom",450                                            tick_color="black",451                                            label_style="bottom",452                                            label_color="black",453                                            label_dist=20) {454    /*455     *456     * Args: tick_list: list<Num> (increasing order (?))457     * We return:458          tick_info_list: list<tick_info>459              tick_info: list<frac (Number), tick_length (Number), tick_style,460                              tick_color, tick_stroke_width (Number),461                              label_style, label_text, label_color462                              label_font_size (Number), label_dist (Number)>463                    ^ * all string except those labelled Number464                  frac: The TOTAL fraction of the axis where this tick lies465                  tick_length: The length of the tick within the SVG466                  tick_style: str fixed vocab: ["top", "cross", "bottom"]467                      this refers to if the tick points up from the axis,468                      crosses the axis, or protrudes from the bottom of469                      the axis.470                  tick_color: str (CSS color)471                  label_style: str fixed vocab ["above", "below"]472                  label_text: str The actual label value.473     *474     *475     */476    let total_tick_range = tick_list[tick_list.length -1] - tick_list[0];477    let low_val = tick_list[0];478    let tick_info_list = [];479    for (let i=0; i<tick_list.length; i++) {480        let new_tick_info = [481            (tick_list[i] - low_val)/total_tick_range,482            tick_length,483            tick_style,484            tick_color,485            tick_width,486            label_style,487            tick_list[i].toString(),488            label_color,489            label_font_size,490            label_dist 491        ]492       tick_info_list.push(new_tick_info) 493    }494    return tick_info_list495}496function addManyPointsToPlot(d3svg,497                            point_list,498                            total_x_range,499                            x_axis_len,500                            x_axis_start,501                            lowest_x,502                            total_y_range,503                            y_axis_len,504                            y_axis_start,505                            lowest_y,506                            point_radius,507                            async=false,508                            quadrant_coloration=null,509                            point_contains_data=false,510                            point_click_function=null,511                            point_opacity=0.25,512                            point_color="black",513                            point_shape="circle"514                            ) {515    /*516     * This function simultaneously adds multiple points517     *      in coordinate form (values related to the plot,518     *          not the svg). Points are in list point_list.519     *520     * point_list: list<Coordinate + data, Coord...>521     *      Coordinate: list<x (Num), y (Num), [data]> (NOT SVG, but graph vals)522     *  total_x_range: (Num) Distance from lowest x value to highest523     *  x_axis_len: (Num) in svg terms, length of axis524     *  x_axis_start: coordinates in svg terms525     *  lowest_x: (Num) Lowest X value526     *  total_y_range: (Num) Distance from lowest y value to highest527     *  y_axis_len: (Num) in svg terms, length of axis528     *  y_axis_start: coordinates in svg terms529     *  lowest_y: (Num) Lowest Y value530     *  point_radius: Num531     *  async: (bool) If true, we return a Promise532     *  quadrant_coloration: (Object) Choose colors per quadrant533     *      'q1' -> q1 color (str)534     *      'q2' -> q2 color, ... etc535     *  point_opacity: (Num)536     *537     * point_click_function: (javascript function)538     * point_contains_data: boolean if point has data at 3rd index539     *540     *541     * Returns:542     *      fulfilled Promise if it succeeds543     */544    // Add dots545    if (point_shape == "circle") {546        d3svg.append('g')547            .selectAll('dot')548            .data(point_list)549            .enter()550            .append('circle')551                .attr('cx', function (d) { 552                    return x_axis_start[0] + (553                                 (d[0] - lowest_x)/total_x_range)*x_axis_len;} )554                .attr('cy', function (d) { 555                    return y_axis_start[1] - (556                                 (d[1] - lowest_y)/total_y_range)*y_axis_len;} )557                .attr('r', point_radius)558                .attr('fill', function(d) {559                    if (quadrant_coloration == null) {560                        return point_color561                    } else {562                        if (d[0]*d[1] >= 0) {563                            if (d[0] > 0) {564                                return quadrant_coloration['q1']565                            } else {566                                return quadrant_coloration['q3']567                            }568                        } else {569                            if (d[0] > 0) {570                                return quadrant_coloration['q4']571                            } else {572                                return quadrant_coloration['q2']573                            }574                        }575                    }576                 })577                .attr('opacity', point_opacity)578                .on('click', function(d) {579                    if (point_click_function != null) {580                        if (point_contains_data) {581                            point_click_function(d[2])582                        } else {583                            return point_click_function(d)584                        }585                    }586                }); 587    }588    if (async == true) {589        return new Promise((resolve, reject) => {590            resolve(true)591        })592    } else {593        return true594    }595}596                            597function addSinglePointToPlot(d3svg,598                        point_coordinates, 599                        point_color,600                        point_shape,601                        point_radius,602                        point_opacity,603                        point_data,604                        point_click_function) {605    /*606     * d3svg: d3 svg object607     * point_coordinates: [x (Num), y (Num)]608     * point_color: (str)609     * point_shape: (str) ["circle", "square", "triangle"]610     * point_radius: (Num) distance from center to corner611     * point_opacity: (Num)612     * point_data: list<>613     * point_click_function: function614     *615     */616   if (point_shape == "circle") {617       if (point_data.length > 0) {618        d3svg.append("circle")619        .attr("cx", point_coordinates[0])620        .attr("cy", point_coordinates[1])621        .attr("r", point_radius)622        .attr("fill", point_color)623        .attr("opacity", point_opacity)624        .data(point_data)625        .on("click", function(d) {626            point_click_function(d); 627        });} else {628            d3svg.append("circle")629            .attr("cx", point_coordinates[0])630            .attr("cy", point_coordinates[1])631            .attr("r", point_radius)632            .attr("fill", point_color)633            .attr("opacity", point_opacity);634        }635   } else {636       throw "No code written for adding rects or triangles yet"637   }638        639}640/*641function sleep(ms) {642  return new Promise(resolve => setTimeout(resolve, ms));643}...player.js
Source:player.js  
2  setup(function() {3    webAnimationsMinifill.timeline._players = [];4  });5  test('zero duration animation works', function() {6    tick(90);7    var p = document.body.animate([], 0);8    tick(100);9    assert.equal(p.startTime, 100);10    assert.equal(p.currentTime, 0);11  });12  test('playing works as expected', function() {13    tick(90);14    var p = document.body.animate([], 2000);15    tick(100);16    assert.equal(p.startTime, 100);17    assert.equal(p.currentTime, 0);18    tick(300);19    assert.equal(p.startTime, 100);20    assert.equal(p.currentTime, 200);21  });22  test('pause at start of play', function() {23    tick(90);24    var p = document.body.animate([], 2000);25    p.pause();26    tick(100);27    assert.equal(p.currentTime, 0);28    tick(300);29    p.play();30    assert.equal(p.currentTime, 0);31    tick(310);32    assert.equal(p.currentTime, 0);33    assert.equal(p.startTime, 310);34    var p = document.body.animate([], 2000);35    p.startTime = -690;36    p.pause();37    assert.equal(p.currentTime, null);38    tick(700);39    p.play();40    tick(701);41    assert.equal(p.currentTime, 1000);42    tick(800);43    assert.equal(p.currentTime, 1099);44    assert.equal(p.startTime, -299);45  });46  test('pausing works as expected', function() {47    tick(190);48    var p = document.body.animate([], 3000);49    tick(200);50    tick(1500);51    assert.equal(p.startTime, 200);52    assert.equal(p.currentTime, 1300);53    p.pause();54    assert.equal(p.startTime, null);55    assert.equal(p.currentTime, null);56    tick(2500);57    assert.equal(p.startTime, null);58    assert.equal(p.currentTime, 1300);59    p.play();60    tick(2510);61    assert.equal(p.startTime, 1210);62    assert.equal(p.currentTime, 1300);63    tick(3500);64    assert.equal(p.startTime, 1210);65    assert.equal(p.currentTime, 2290);66  });67  test('reversing works as expected', function() {68    tick(290);69    var p = document.body.animate([], 1000);70    tick(300);71    assert.equal(p.startTime, 300);72    assert.equal(p.currentTime, 0);73    tick(600);74    assert.equal(p.startTime, 300);75    assert.equal(p.currentTime, 300);76    assert.equal(p.playbackRate, 1);77    p.reverse();78    tick(600);79    assert.equal(p.startTime, 900);80    assert.equal(p.currentTime, 300);81    assert.equal(p.playbackRate, -1);82    tick(700);83    assert.equal(p.startTime, 900);84    assert.equal(p.currentTime, 200);85  });86  test('reversing after pausing', function() {87    tick(90);88    var p = document.body.animate([], 1000);89    tick(100);90    tick(600);91    p.reverse();92    tick(601);93    tick(700);94    assert.equal(p.startTime, 1101);95    assert.equal(p.currentTime, 401);96  });97  test('reversing after finishing works as expected', function() {98    tick(90);99    var p = document.body.animate([], 1000);100    tick(100);101    tick(1200);102    assert.equal(p.finished, true);103    assert.equal(p.startTime, 100);104    assert.equal(p.currentTime, 1000);105    tick(1500);106    assert.equal(p.currentTime, 1000);107    assert.equal(isTicking(), false);108    p.reverse();109    assert.equal(p._startTime, null);110    assert.equal(p.currentTime, 1000);111    tick(1600);112    assert.equal(p.startTime, 2600);113    assert.equal(p.currentTime, 1000);114  });115  test('playing after finishing works as expected', function() {116    tick(90);117    var p = document.body.animate([], 1000);118    tick(100);119    tick(1200);120    assert.equal(p.finished, true);121    assert.equal(p.startTime, 100);122    assert.equal(p.currentTime, 1000);123    tick(1500);124    assert.equal(p.currentTime, 1000);125    assert.equal(isTicking(), false);126    p.play();127    assert.equal(p.startTime, null);128    assert.equal(p.currentTime, 0);129    tick(1600);130    assert.equal(p.startTime, 1600);131    assert.equal(p.currentTime, 0);132  });133  test('limiting works as expected', function() {134    tick(390);135    var p = document.body.animate([], 1000);136    tick(400);137    assert.equal(p.startTime, 400);138    assert.equal(p.currentTime, 0);139    tick(900);140    assert.equal(p.startTime, 400);141    assert.equal(p.currentTime, 500);142    tick(1400);143    assert.equal(p.startTime, 400);144    assert.equal(p.currentTime, 1000);145    tick(1500);146    assert.equal(p.startTime, 400);147    assert.equal(p.currentTime, 1000);148    p.reverse();149    assert.equal(p.playbackRate, -1);150    assert.equal(p.currentTime, 1000);151    assert.equal(p._startTime, null);152    tick(2000);153    assert.equal(p.currentTime, 1000);154    assert.equal(p.startTime, 3000);155    tick(2200);156    assert.equal(p.currentTime, 800);157    assert.equal(p.startTime, 3000);158    tick(3200);159    assert.equal(p.currentTime, 0);160    assert.equal(p.startTime, 3000);161    tick(3500);162    assert.equal(p.currentTime, 0);163    assert.equal(p.startTime, 3000);164  });165  test('play after limit works as expected', function() {166    tick(490);167    var p = document.body.animate([], 2000);168    tick(500);169    tick(2600);170    assert.equal(p.currentTime, 2000);171    assert.equal(p.startTime, 500);172    assert.equal(p.finished, true);173    assert.equal(p.playbackRate, 1);174    setTicking(true);175    p.play();176    tick(2700);177    assert.equal(p.startTime, 2700);178    assert.equal(p.currentTime, 0);179    assert.equal(p.finished, false);180    assert.equal(p.playbackRate, 1);181  });182  test('play after limit works as expected (reversed)', function() {183    tick(590);184    var p = document.body.animate([], 3000);185    tick(600);186    tick(700);187    p.reverse();188    tick(701);189    tick(900);190    assert.equal(p.startTime, 801);191    assert.equal(p.currentTime, 0);192    assert.equal(p.finished, true);193    assert.equal(p.playbackRate, -1);194    setTicking(true);195    p.play();196    tick(1000);197    assert.equal(p.startTime, 4000);198    assert.equal(p.currentTime, 3000);199    assert.equal(p.finished, false);200    assert.equal(p.playbackRate, -1);201  });202  test('seeking works as expected', function() {203    tick(690);204    var p = document.body.animate([], 2000);205    tick(700);206    tick(900);207    assert.equal(p.currentTime, 200);208    p.currentTime = 600;209    assert.equal(p.currentTime, 600);210    assert.equal(p.startTime, 300);211    p.reverse();212    tick(1000);213    assert.equal(p.startTime, 1600);214    p.currentTime = 300;215    assert.equal(p.currentTime, 300);216    assert.equal(p.startTime, 1300);217  });218  test('seeking while paused works as expected', function() {219    tick(790);220    var p = document.body.animate([], 1000);221    tick(800);222    tick(1000);223    p.pause();224    assert.equal(p.currentTime, null);225    assert.equal(p.startTime, null);226    assert.equal(p.paused, true);227    p.currentTime = 500;228    assert.equal(p.startTime, null);229    assert.equal(p.paused, true);230  });231  test('setting start time while paused is ignored', function() {232    tick(900);233    var p = document.body.animate([], 1234);234    p.pause();235    assert.equal(p.startTime, null);236    assert.equal(p.currentTime, null);237    p.startTime = 2232;238    assert.equal(p.startTime, null);239    assert.equal(p.currentTime, null);240  });241  test('finishing works as expected', function() {242    tick(1000);243    var p = document.body.animate([], 2000);244    p.finish();245    assert.equal(p.startTime, 0);246    assert.equal(p.currentTime, 2000);247    p.reverse();248    p.finish();249    assert.equal(p.currentTime, 0);250    assert.equal(p.startTime, 2000);251    tick(2000);252  });253  test('cancelling clears all effects', function() {254    tick(0);255    var target = document.createElement('div');256    document.documentElement.appendChild(target);257    var player = target.animate([{marginLeft: '50px'}, {marginLeft: '50px'}], 1000);258    tick(10);259    tick(110);260    assert.equal(getComputedStyle(target).marginLeft, '50px');261    player.cancel();262    // getComputedStyle forces a tick.263    assert.equal(getComputedStyle(target).marginLeft, '0px');264    assert.deepEqual(webAnimationsMinifill.timeline._players, []);265    tick(120);266    assert.equal(getComputedStyle(target).marginLeft, '0px');267    assert.deepEqual(webAnimationsMinifill.timeline._players, []);268    document.documentElement.removeChild(target);269  });270  test('startTime is set on first tick if timeline hasn\'t started', function() {271    webAnimationsMinifill.timeline.currentTime = undefined;272    var p = document.body.animate([], 1000);273    tick(0);274    tick(100);275    assert.equal(p.startTime, 0);276  });277  test('players which are finished and not filling get discarded', function() {278    tick(90);279    var nofill = document.body.animate([], 100);280    var fill = document.body.animate([], {duration: 100, fill: 'forwards'});281    assert.deepEqual(webAnimationsMinifill.timeline._players, [nofill._player || nofill, fill._player || fill]);282    tick(100);283    assert.deepEqual(webAnimationsMinifill.timeline._players, [nofill._player || nofill, fill._player || fill]);284    tick(400);285    assert.deepEqual(webAnimationsMinifill.timeline._players, [fill._player || fill]);286  });287  test('discarded players get re-added on modification', function() {288    tick(90);289    var player = document.body.animate([], 100);290    tick(100);291    tick(400);292    assert.deepEqual(webAnimationsMinifill.timeline._players, []);293    player.currentTime = 0;294    assert.deepEqual(webAnimationsMinifill.timeline._players, [player._player || player]);295  });296  test('players in the before phase are not discarded', function() {297    tick(100);298    var player = document.body.animate([], 100);299    player.currentTime = -50;300    tick(110);301    assert.deepEqual(webAnimationsMinifill.timeline._players, [player._player || player]);302  });303  test('players that go out of effect should not clear the effect of players that are in effect', function() {304    var target = document.createElement('div');305    document.body.appendChild(target);306    tick(0);307    var playerBehind = target.animate([{marginLeft: '200px'}, {marginLeft: '200px'}], 200);308    var playerInfront = target.animate([{marginLeft: '100px'}, {marginLeft: '100px'}], 100);309    tick(50);310    assert.equal(getComputedStyle(target).marginLeft, '100px', 't = 50');311    tick(150);312    assert.equal(getComputedStyle(target).marginLeft, '200px', 't = 150');313    tick(250);314    assert.equal(getComputedStyle(target).marginLeft, '0px', 't = 250');315    document.body.removeChild(target);316  });317  test('player modifications should update CSS effects immediately', function() {318    var target = document.createElement('div');319    document.body.appendChild(target);320    tick(0);321    var playerBehind = target.animate([{width: '1234px'}, {width: '1234px'}], {duration: 1, fill: 'both'});322    var playerInfront = target.animate([{width: '0px'}, {width: '100px'}], 100);323    assert.equal(getComputedStyle(target).width, '0px');324    playerInfront.currentTime = 50;325    assert.equal(getComputedStyle(target).width, '50px');326    playerInfront.currentTime = 100;327    assert.equal(getComputedStyle(target).width, '1234px');328    playerInfront.play();329    assert.equal(getComputedStyle(target).width, '0px');330    playerInfront.startTime = -50;331    assert.equal(getComputedStyle(target).width, '50px');332    document.body.removeChild(target);333  });334  test('Player that hasn\'t been played has playState \'idle\'', function() {335    var source = new minifillAnimation(document.body, [], 1000);336    var p = new Player(source);337    assert.equal(p.playState, 'idle');338  });339  test('playState works for a simple animation', function() {340    var p = document.body.animate([], 1000);341    tick(0);342    assert.equal(p.playState, 'running');343    tick(100);344    assert.equal(p.playState, 'running');345    p.pause();346    assert.equal(p.playState, 'pending');347    tick(101);348    assert.equal(p.playState, 'paused');349    p.play();350    assert.equal(p.playState, 'pending');351    tick(102);352    assert.equal(p.playState, 'running');353    tick(1002);354    assert.equal(p.playState, 'finished');355  });356  test('Play after cancel', function() {357    var p = document.body.animate([], 1000);358    assert.equal(p.playState, 'pending');359    tick(0);360    p.cancel();361    assert.equal(p.playState, 'idle');362    assert.equal(p.currentTime, null);363    assert.equal(p.startTime, null);364    tick(1);365    assert.equal(p.playState, 'idle');366    assert.equal(p.currentTime, null);367    assert.equal(p.startTime, null);368    p.play();369    assert.equal(p.playState, 'pending');370    assert.equal(p.currentTime, 0);371    assert.equal(p.startTime, null);372    tick(10);373    assert.equal(p.playState, 'running');374    assert.equal(p.currentTime, 0);375    assert.equal(p.startTime, 10);376  });377  test('Reverse after cancel', function() {378    var p = document.body.animate([], 300);379    tick(0);380    p.cancel();381    assert.equal(p.playState, 'idle');382    assert.equal(p.currentTime, null);383    assert.equal(p.startTime, null);384    tick(1);385    p.reverse();386    assert.equal(p.playState, 'pending');387    assert.equal(p.currentTime, 300);388    assert.equal(p.startTime, null);389    tick(100);390    assert.equal(p.playState, 'running');391    assert.equal(p.currentTime, 300);392    assert.equal(p.startTime, 400);393    tick(300);394    assert.equal(p.playState, 'running');395    assert.equal(p.currentTime, 100);396    assert.equal(p.startTime, 400);397    tick(400);398    assert.equal(p.playState, 'finished');399    assert.equal(p.currentTime, 0);400    assert.equal(p.startTime, 400);401  });402  test('Finish after cancel', function() {403    var p = document.body.animate([], 300);404    tick(0);405    p.cancel();406    assert.equal(p.playState, 'idle');407    assert.equal(p.currentTime, null);408    assert.equal(p.startTime, null);409    tick(1);410    p.finish();411    assert.equal(p.playState, 'idle');412    assert.equal(p.currentTime, null);413    assert.equal(p.startTime, null);414    tick(2);415    assert.equal(p.playState, 'idle');416    assert.equal(p.currentTime, null);417    assert.equal(p.startTime, null);418  });419  test('Pause after cancel', function() {420    var p = document.body.animate([], 300);421    tick(0);422    p.cancel();423    assert.equal(p.playState, 'idle');424    assert.equal(p.currentTime, null);425    assert.equal(p.startTime, null);426    tick(1);427    p.pause();428    assert.equal(p.playState, 'idle');429    assert.equal(p.currentTime, null);430    assert.equal(p.startTime, null);431  });432  test('Players ignore NaN times', function() {433    var p = document.body.animate([], 300);434    p.startTime = 100;435    tick(110);436    assert.equal(p.currentTime, 10);437    p.startTime = NaN;438    assert.equal(p.startTime, 100);439    p.currentTime = undefined;440    assert.equal(p.currentTime, 10);441  });442  test('play() should not set a start time', function() {443    var p = document.body.animate([], 1000);444    p.cancel();445    assert.equal(p.startTime, null);446    assert.equal(p.playState, 'idle');447    p.play();448    assert.equal(p.startTime, null);449    assert.equal(p.playState, 'pending');...grouped-categories.js
Source:grouped-categories.js  
1/**2 * Grouped Categories v1.0.13 (2016-01-13)3 *4 * (c) 2012-2016 Black Label5 *6 * License: Creative Commons Attribution (CC)7 */8(function(HC){9/*jshint expr:true, boss:true */10var UNDEFINED = void 0,11    mathRound = Math.round,12    mathMin   = Math.min,13    mathMax   = Math.max,14    merge     = HC.merge,15    pick      = HC.pick,16    // #74, since Highcharts 4.1.10 HighchartsAdapter is only provided by the Highcharts Standalone Framework17    inArray   = (window.HighchartsAdapter && window.HighchartsAdapter.inArray) || HC.inArray,18    // cache prototypes19    axisProto  = HC.Axis.prototype,20    tickProto  = HC.Tick.prototype,21    // cache original methods22    _axisInit          = axisProto.init,23    _axisRender        = axisProto.render,24    _axisSetCategories = axisProto.setCategories,25    _tickGetLabelSize  = tickProto.getLabelSize,26    _tickAddLabel      = tickProto.addLabel,27    _tickDestroy       = tickProto.destroy,28    _tickRender        = tickProto.render;29function Category(obj, parent) {30  this.userOptions = deepClone(obj);31  this.name = obj.name || obj;32  this.parent = parent;33  return this;34}35Category.prototype.toString = function () {36  var parts = [],37      cat = this;38  while (cat) {39    parts.push(cat.name);40    cat = cat.parent;41  }42  return parts.join(', ');43};44// Highcharts methods45function defined(obj) {46  return obj !== UNDEFINED && obj !== null;47}48// returns sum of an array49function sum(arr) {50  var l = arr.length,51      x = 0;52  while (l--)53    x += arr[l];54  return x;55}56// Builds reverse category tree57function buildTree(cats, out, options, parent, depth) {58  var len = cats.length,59      cat;60  depth || (depth = 0);61  options.depth || (options.depth = 0);62  while (len--) {63    cat = cats[len];64    if (parent)65      cat.parent = parent;66    if (cat.categories)67      buildTree(cat.categories, out, options, cat, depth + 1);68    else69      addLeaf(out, cat, parent);70  }71  options.depth = mathMax(options.depth, depth);72}73// Adds category leaf to array74function addLeaf(out, cat, parent) {75  out.unshift(new Category(cat, parent));76  while (parent) {77    parent.leaves++ || (parent.leaves = 1);78    parent = parent.parent;79  }80}81// Pushes part of grid to path82function addGridPart(path, d, width) {83  // Based on crispLine from HC (#65)84  if (d[0] === d[2]) {85    d[0] = d[2] = mathRound(d[0]) - (width % 2 / 2);86  }87  if (d[1] === d[3]) {88    d[1] = d[3] = mathRound(d[1]) + (width % 2 / 2);89  }90    91  path.push(92    'M',93      d[0], d[1],94    'L',95      d[2], d[3]96  );97}98// Destroys category groups99function cleanCategory(category) {100  var tmp;101  while (category) {102    tmp = category.parent;103    if (category.label)104      category.label.destroy();105    delete category.parent;106    delete category.label;107    category = tmp;108  }109}110// Returns tick position111function tickPosition(tick, pos) {112  return tick.getPosition(tick.axis.horiz, pos, tick.axis.tickmarkOffset);113}114function walk(arr, key, fn) {115  var l = arr.length,116      children;117  while (l--) {118    children = arr[l][key];119    if (children)120      walk(children, key, fn);121    fn(arr[l]);122  }123}124function deepClone(thing) {125    return JSON.parse(JSON.stringify(thing));126}127//128// Axis prototype129//130axisProto.init = function (chart, options) {131  // default behaviour132  _axisInit.call(this, chart, options);133  if (typeof options === 'object' && options.categories)134    this.setupGroups(options);135};136// setup required axis options137axisProto.setupGroups = function (options) {138  var categories,139      reverseTree = [],140      stats       = {};141 142  categories = deepClone(options.categories);;143  // build categories tree144  buildTree(categories, reverseTree, stats);145  // set axis properties146  this.categoriesTree   = categories;147  this.categories       = reverseTree;148  this.isGrouped        = stats.depth !== 0;149  this.labelsDepth      = stats.depth;150  this.labelsSizes      = [];151  this.labelsGridPath   = [];152  this.tickLength       = options.tickLength || this.tickLength || null;153  // #66: tickWidth for x axis defaults to 1, for y to 0154  this.tickWidth        = pick(options.tickWidth, this.isXAxis ? 1 : 0);155  this.directionFactor  = [-1, 1, 1, -1][this.side];156  this.options.lineWidth = pick(options.lineWidth, 1);157};158axisProto.render = function () {159  // clear grid path160  if (this.isGrouped)161    this.labelsGridPath = [];162  // cache original tick length163  if (this.originalTickLength === UNDEFINED)164    this.originalTickLength = this.options.tickLength;165  // use default tickLength for not-grouped axis166  // and generate grid on grouped axes,167  // use tiny number to force highcharts to hide tick168  this.options.tickLength = this.isGrouped ? 0.001 : this.originalTickLength;169  _axisRender.call(this);170  if (!this.isGrouped) {171    if (this.labelsGrid)172      this.labelsGrid.attr({visibility: 'hidden'});173    return;174  }175  var axis    = this,176      options = axis.options,177      top     = axis.top,178      left    = axis.left,179      right   = left + axis.width,180      bottom  = top + axis.height,181      visible = axis.hasVisibleSeries || axis.hasData,182      depth   = axis.labelsDepth,183      grid    = axis.labelsGrid,184      horiz   = axis.horiz,185      d       = axis.labelsGridPath,186      i       = options.drawHorizontalBorders === false ? depth+1 : 0,187      offset  = axis.opposite ? (horiz ? top : right) : (horiz ? bottom : left),188      tickWidth = axis.tickWidth,189      part;190  if (axis.userTickLength)191    depth -= 1;192  // render grid path for the first time193  if (!grid) {194    grid = axis.labelsGrid = axis.chart.renderer.path()195      .attr({196        // #58: use tickWidth/tickColor instead of lineWidth/lineColor: 197        strokeWidth: tickWidth, // < 4.0.3198        'stroke-width': tickWidth, // 4.0.3+ #30199        stroke: options.tickColor200      })201      .add(axis.axisGroup);202  }203  // go through every level and draw horizontal grid line204  while (i <= depth) {205    offset += axis.groupSize(i);206    part = horiz ?207      [left, offset, right, offset] :208      [offset, top, offset, bottom];209    addGridPart(d, part, tickWidth);210    i++;211  }212  // draw grid path213  grid.attr({214    d: d,215    visibility: visible ? 'visible' : 'hidden'216  });217  axis.labelGroup.attr({218    visibility: visible ? 'visible' : 'hidden'219  });220  walk(axis.categoriesTree, 'categories', function (group) {221    var tick = group.tick;222    if (!tick)223      return;224    if (tick.startAt + tick.leaves - 1 < axis.min || tick.startAt > axis.max) {225      tick.label.hide();226      tick.destroyed = 0;227    }228    else229	  tick.label.attr({230		visibility: visible ? 'visible' : 'hidden'231	  });232  });233};234axisProto.setCategories = function (newCategories, doRedraw) {235  if (this.categories)236    this.cleanGroups();237  this.setupGroups({238    categories: newCategories239  });240  this.categories = this.userOptions.categories = newCategories;241  _axisSetCategories.call(this, this.categories, doRedraw);242};243// cleans old categories244axisProto.cleanGroups = function () {245  var ticks = this.ticks,246      n;247  for (n in ticks)248    if (ticks[n].parent)249      delete ticks[n].parent;250  walk(this.categoriesTree, 'categories', function (group) {251    var tick = group.tick,252        n;253    if (!tick)254      return;255    tick.label.destroy();256    for (n in tick)257      delete tick[n];258    delete group.tick;259  });260  this.labelsGrid = null;261};262// keeps size of each categories level263axisProto.groupSize = function (level, position) {264  var positions = this.labelsSizes,265      direction = this.directionFactor,266      groupedOptions = this.options.labels.groupedOptions ? this.options.labels.groupedOptions[level-1] : false,267      userXY = 0;268      269  if(groupedOptions) {270  	if(direction == -1) {271  		userXY = groupedOptions.x ? groupedOptions.x : 0;272  	} else {273  		userXY = groupedOptions.y ? groupedOptions.y : 0;274  	}275  }  	276  277  if (position !== UNDEFINED)278    positions[level] = mathMax(positions[level] || 0, position + 10 + Math.abs(userXY));279  280  if (level === true)281    return sum(positions) * direction;282  else if (positions[level])283    return positions[level] * direction;284  return 0;285};286//287// Tick prototype288//289// Override methods prototypes290tickProto.addLabel = function () {291  var category;292  _tickAddLabel.call(this);293  if (!this.axis.categories ||294      !(category = this.axis.categories[this.pos]))295    return;296  // set label text - but applied after formatter #46297  if (this.label)298    this.label.attr('text', this.axis.labelFormatter.call({299			axis: this.axis,300			chart: this.axis.chart,301			isFirst: this.isFirst,302			isLast: this.isLast,303			value: category.name304		}));305  // create elements for parent categories306  if (this.axis.isGrouped && this.axis.options.labels.enabled)307    this.addGroupedLabels(category);308};309// render ancestor label310tickProto.addGroupedLabels = function (category) {311  var tick    = this,312      axis    = this.axis,313      chart   = axis.chart,314      options = axis.options.labels,315      useHTML = options.useHTML,316      css     = options.style,317      userAttr= options.groupedOptions,318      attr    = { align: 'center' , rotation: options.rotation, x: 0, y: 0 },319      size    = axis.horiz ? 'height' : 'width',320      depth   = 0,321      label;322  while (tick) {323    if (depth > 0 && !category.tick) {324      // render label element325      this.value = category.name;326      var name = options.formatter ? options.formatter.call(this, category) : category.name,327      	  hasOptions = userAttr && userAttr[depth-1],328     	  mergedAttrs =  hasOptions ? merge(attr, userAttr[depth-1] ) : attr,329     	  mergedCSS = hasOptions && userAttr[depth-1].style ? merge(css, userAttr[depth-1].style ) : css;330      //#63: style is passed in CSS and not as an attribute331      delete mergedAttrs.style;332     	  333      label = chart.renderer.text(name, 0, 0, useHTML)334        .attr(mergedAttrs)335        .css(mergedCSS)336        .add(axis.labelGroup);337      // tick properties338      tick.startAt = this.pos;339      tick.childCount = category.categories.length;340      tick.leaves = category.leaves;341      tick.visible = this.childCount;342      tick.label = label;343      tick.labelOffsets = {344      		x: mergedAttrs.x,345      		y: mergedAttrs.y346      };347      // link tick with category348      category.tick = tick;349    }350    // set level size351    axis.groupSize(depth, tick.label.getBBox()[size]);352    // go up to the parent category353    category = category.parent;354    if (category)355      tick = tick.parent = category.tick || {};356    else357      tick = null;358    depth++;359  }360};361// set labels position & render categories grid362tickProto.render = function (index, old, opacity) {363  _tickRender.call(this, index, old, opacity);364  var treeCat = this.axis.categories[this.pos];365  366  if (!this.axis.isGrouped || !treeCat || this.pos > this.axis.max)367    return;368  var tick    = this,369      group   = tick,370      axis    = tick.axis,371      tickPos = tick.pos,372      isFirst = tick.isFirst,373      max     = axis.max,374      min     = axis.min,375      horiz   = axis.horiz,376      cat     = axis.categories[tickPos],377      grid    = axis.labelsGridPath,378      size    = axis.groupSize(0),379      tickLen = axis.tickLength || size,380      tickWidth = axis.tickWidth,381      factor  = axis.directionFactor,382      xy      = tickPosition(tick, tickPos),383      start   = horiz ? xy.y : xy.x,384      baseLine= axis.chart.renderer.fontMetrics(axis.options.labels.style.fontSize).b,385      depth   = 1,386	  // adjust grid lines for edges387      reverseCrisp = ((horiz && xy.x === axis.pos + axis.len) || (!horiz && xy.y === axis.pos)) ? -1 : 0,388      gridAttrs,389      lvlSize,390      minPos,391      maxPos,392      attrs,393      bBox;394  // render grid for "normal" categories (first-level), render left grid line only for the first category395  if (isFirst) {396  	397    gridAttrs = horiz ?398      [axis.left, xy.y, axis.left, xy.y + axis.groupSize(true)] :399      axis.isXAxis ?400        [xy.x, axis.top, xy.x + axis.groupSize(true), axis.top] :401        [xy.x, axis.top + axis.len, xy.x + axis.groupSize(true), axis.top + axis.len];402    addGridPart(grid, gridAttrs, tickWidth);403  }404    405	if(horiz && axis.left < xy.x) {406			addGridPart(grid, [xy.x - reverseCrisp, xy.y, xy.x - reverseCrisp, xy.y + size], tickWidth);407	} else if(!horiz && axis.top <= xy.y){408			addGridPart(grid, [xy.x, xy.y + reverseCrisp, xy.x + size, xy.y + reverseCrisp], tickWidth);409	}410  size = start + size;411  function fixOffset(group, treeCat, tick){412  		var ret = 0;413			if(isFirst) {414					ret = inArray(treeCat.name, treeCat.parent.categories);415					ret = ret < 0 ? 0 : ret;416					return ret;417			} 418			return ret;419  }420  while (group = group.parent) {421  	var fix = fixOffset(group, treeCat, tick),422  			userX = group.labelOffsets.x,423  			userY = group.labelOffsets.y;424  	425    minPos  = tickPosition(tick, mathMax(group.startAt - 1, min - 1));426    maxPos  = tickPosition(tick, mathMin(group.startAt + group.leaves - 1 - fix, max));427    bBox    = group.label.getBBox(true);428    lvlSize = axis.groupSize(depth);429    // check if on the edge to adjust430    reverseCrisp = ((horiz && maxPos.x === axis.pos + axis.len) || (!horiz && maxPos.y === axis.pos)) ? -1 : 0;431	432    attrs = horiz ? {433      x: (minPos.x + maxPos.x) / 2 + userX,434      y: size + lvlSize / 2 + baseLine - bBox.height / 2 - 4 + userY / 2435    } : {436			x: size + lvlSize / 2 + userX,437			y: (minPos.y + maxPos.y - bBox.height) / 2  + baseLine + userY438		};439		if(!isNaN(attrs.x) && !isNaN(attrs.y)){ 440			group.label.attr(attrs);441	442			if (grid) {443				if(horiz && axis.left < maxPos.x) {444						addGridPart(grid, [maxPos.x - reverseCrisp, size, maxPos.x - reverseCrisp, size + lvlSize], tickWidth);445				} else if(!horiz && axis.top <= maxPos.y){446						addGridPart(grid, [size, maxPos.y + reverseCrisp, size + lvlSize, maxPos.y + reverseCrisp], tickWidth);447				}448			}449    }450    size += lvlSize;451    depth++;452  }453};454tickProto.destroy = function () {455  var group = this;456  while (group = group.parent)457    group.destroyed++ || (group.destroyed = 1);458  _tickDestroy.call(this);459};460// return size of the label (height for horizontal, width for vertical axes)461tickProto.getLabelSize = function () {462  if (this.axis.isGrouped === true) {463    // #72, getBBox might need recalculating when chart is tall464    var size = _tickGetLabelSize.call(this) + 10,465        topLabelSize = this.axis.labelsSizes[0];466    if (topLabelSize < size) {467        this.axis.labelsSizes[0] = size;468    }469    return sum(this.axis.labelsSizes);470  } else471    return _tickGetLabelSize.call(this);472};...c3.axis.js
Source:c3.axis.js  
1// Features:2// 1. category axis3// 2. ceil values of translate/x/y to int for half pixel antialiasing4// 3. multiline tick text5var tickTextCharSize;6function c3_axis(d3, params) {7    var scale = d3.scale.linear(), orient = "bottom", innerTickSize = 6, outerTickSize, tickPadding = 3, tickValues = null, tickFormat, tickArguments;8    var tickOffset = 0, tickCulling = true, tickCentered;9    params = params || {};10    outerTickSize = params.withOuterTick ? 6 : 0;11    function axisX(selection, x) {12        selection.attr("transform", function (d) {13            return "translate(" + Math.ceil(x(d) + tickOffset) + ", 0)";14        });15    }16    function axisY(selection, y) {17        selection.attr("transform", function (d) {18            return "translate(0," + Math.ceil(y(d)) + ")";19        });20    }21    function scaleExtent(domain) {22        var start = domain[0], stop = domain[domain.length - 1];23        return start < stop ? [ start, stop ] : [ stop, start ];24    }25    function generateTicks(scale) {26        var i, domain, ticks = [];27        if (scale.ticks) {28            return scale.ticks.apply(scale, tickArguments);29        }30        domain = scale.domain();31        for (i = Math.ceil(domain[0]); i < domain[1]; i++) {32            ticks.push(i);33        }34        if (ticks.length > 0 && ticks[0] > 0) {35            ticks.unshift(ticks[0] - (ticks[1] - ticks[0]));36        }37        return ticks;38    }39    function copyScale() {40        var newScale = scale.copy(), domain;41        if (params.isCategory) {42            domain = scale.domain();43            newScale.domain([domain[0], domain[1] - 1]);44        }45        return newScale;46    }47    function textFormatted(v) {48        var formatted = tickFormat ? tickFormat(v) : v;49        return typeof formatted !== 'undefined' ? formatted : '';50    }51    function getSizeFor1Char(tick) {52        if (tickTextCharSize) {53            return tickTextCharSize;54        }55        var size = {56            h: 11.5,57            w: 5.558        };59        tick.select('text').text(textFormatted).each(function (d) {60            var box = this.getBoundingClientRect(),61                text = textFormatted(d),62                h = box.height,63                w = text ? (box.width / text.length) : undefined;64            if (h && w) {65                size.h = h;66                size.w = w;67            }68        }).text('');69        tickTextCharSize = size;70        return size;71    }72    function transitionise(selection) {73        return params.withoutTransition ? selection : d3.transition(selection);74    }75    function axis(g) {76        g.each(function () {77            var g = axis.g = d3.select(this);78            var scale0 = this.__chart__ || scale, scale1 = this.__chart__ = copyScale();79            var ticks = tickValues ? tickValues : generateTicks(scale1),80                tick = g.selectAll(".tick").data(ticks, scale1),81                tickEnter = tick.enter().insert("g", ".domain").attr("class", "tick").style("opacity", 1e-6),82                // MEMO: No exit transition. The reason is this transition affects max tick width calculation because old tick will be included in the ticks.83                tickExit = tick.exit().remove(),84                tickUpdate = transitionise(tick).style("opacity", 1),85                tickTransform, tickX, tickY;86            var range = scale.rangeExtent ? scale.rangeExtent() : scaleExtent(scale.range()),87                path = g.selectAll(".domain").data([ 0 ]),88                pathUpdate = (path.enter().append("path").attr("class", "domain"), transitionise(path));89            tickEnter.append("line");90            tickEnter.append("text");91            var lineEnter = tickEnter.select("line"),92                lineUpdate = tickUpdate.select("line"),93                textEnter = tickEnter.select("text"),94                textUpdate = tickUpdate.select("text");95            if (params.isCategory) {96                tickOffset = Math.ceil((scale1(1) - scale1(0)) / 2);97                tickX = tickCentered ? 0 : tickOffset;98                tickY = tickCentered ? tickOffset : 0;99            } else {100                tickOffset = tickX = 0;101            }102            var text, tspan, sizeFor1Char = getSizeFor1Char(g.select('.tick')), counts = [];103            var tickLength = Math.max(innerTickSize, 0) + tickPadding,104                isVertical = orient === 'left' || orient === 'right';105            // this should be called only when category axis106            function splitTickText(d, maxWidth) {107                var tickText = textFormatted(d),108                    subtext, spaceIndex, textWidth, splitted = [];109                if (Object.prototype.toString.call(tickText) === "[object Array]") {110                    return tickText;111                }112                if (!maxWidth || maxWidth <= 0) {113                    maxWidth = isVertical ? 95 : params.isCategory ? (Math.ceil(scale1(ticks[1]) - scale1(ticks[0])) - 12) : 110;114                }115                function split(splitted, text) {116                    spaceIndex = undefined;117                    for (var i = 1; i < text.length; i++) {118                        if (text.charAt(i) === ' ') {119                            spaceIndex = i;120                        }121                        subtext = text.substr(0, i + 1);122                        textWidth = sizeFor1Char.w * subtext.length;123                        // if text width gets over tick width, split by space index or crrent index124                        if (maxWidth < textWidth) {125                            return split(126                                splitted.concat(text.substr(0, spaceIndex ? spaceIndex : i)),127                                text.slice(spaceIndex ? spaceIndex + 1 : i)128                            );129                        }130                    }131                    return splitted.concat(text);132                }133                return split(splitted, tickText + "");134            }135            function tspanDy(d, i) {136                var dy = sizeFor1Char.h;137                if (i === 0) {138                    if (orient === 'left' || orient === 'right') {139                        dy = -((counts[d.index] - 1) * (sizeFor1Char.h / 2) - 3);140                    } else {141                        dy = ".71em";142                    }143                }144                return dy;145            }146            function tickSize(d) {147                var tickPosition = scale(d) + (tickCentered ? 0 : tickOffset);148                return range[0] < tickPosition && tickPosition < range[1] ? innerTickSize : 0;149            }150            text = tick.select("text");151            tspan = text.selectAll('tspan')152                .data(function (d, i) {153                    var splitted = params.tickMultiline ? splitTickText(d, params.tickWidth) : [].concat(textFormatted(d));154                    counts[i] = splitted.length;155                    return splitted.map(function (s) {156                        return { index: i, splitted: s };157                    });158                });159            tspan.enter().append('tspan');160            tspan.exit().remove();161            tspan.text(function (d) { return d.splitted; });162            var rotate = params.tickTextRotate;163            function textAnchorForText(rotate) {164                if (!rotate) {165                    return 'middle';166                }167                return rotate > 0 ? "start" : "end";168            }169            function textTransform(rotate) {170                if (!rotate) {171                    return '';172                }173                return "rotate(" + rotate + ")";174            }175            function dxForText(rotate) {176                if (!rotate) {177                    return 0;178                }179                return 8 * Math.sin(Math.PI * (rotate / 180));180            }181            function yForText(rotate) {182                if (!rotate) {183                    return tickLength;184                }185                return 11.5 - 2.5 * (rotate / 15) * (rotate > 0 ? 1 : -1);186            }187            switch (orient) {188            case "bottom":189                {190                    tickTransform = axisX;191                    lineEnter.attr("y2", innerTickSize);192                    textEnter.attr("y", tickLength);193                    lineUpdate.attr("x1", tickX).attr("x2", tickX).attr("y2", tickSize);194                    textUpdate.attr("x", 0).attr("y", yForText(rotate))195                        .style("text-anchor", textAnchorForText(rotate))196                        .attr("transform", textTransform(rotate));197                    tspan.attr('x', 0).attr("dy", tspanDy).attr('dx', dxForText(rotate));198                    pathUpdate.attr("d", "M" + range[0] + "," + outerTickSize + "V0H" + range[1] + "V" + outerTickSize);199                    break;200                }201            case "top":202                {203                    // TODO: rotated tick text204                    tickTransform = axisX;205                    lineEnter.attr("y2", -innerTickSize);206                    textEnter.attr("y", -tickLength);207                    lineUpdate.attr("x2", 0).attr("y2", -innerTickSize);208                    textUpdate.attr("x", 0).attr("y", -tickLength);209                    text.style("text-anchor", "middle");210                    tspan.attr('x', 0).attr("dy", "0em");211                    pathUpdate.attr("d", "M" + range[0] + "," + -outerTickSize + "V0H" + range[1] + "V" + -outerTickSize);212                    break;213                }214            case "left":215                {216                    tickTransform = axisY;217                    lineEnter.attr("x2", -innerTickSize);218                    textEnter.attr("x", -tickLength);219                    lineUpdate.attr("x2", -innerTickSize).attr("y1", tickY).attr("y2", tickY);220                    textUpdate.attr("x", -tickLength).attr("y", tickOffset);221                    text.style("text-anchor", "end");222                    tspan.attr('x', -tickLength).attr("dy", tspanDy);223                    pathUpdate.attr("d", "M" + -outerTickSize + "," + range[0] + "H0V" + range[1] + "H" + -outerTickSize);224                    break;225                }226            case "right":227                {228                    tickTransform = axisY;229                    lineEnter.attr("x2", innerTickSize);230                    textEnter.attr("x", tickLength);231                    lineUpdate.attr("x2", innerTickSize).attr("y2", 0);232                    textUpdate.attr("x", tickLength).attr("y", 0);233                    text.style("text-anchor", "start");234                    tspan.attr('x', tickLength).attr("dy", tspanDy);235                    pathUpdate.attr("d", "M" + outerTickSize + "," + range[0] + "H0V" + range[1] + "H" + outerTickSize);236                    break;237                }238            }239            if (scale1.rangeBand) {240                var x = scale1, dx = x.rangeBand() / 2;241                scale0 = scale1 = function (d) {242                    return x(d) + dx;243                };244            } else if (scale0.rangeBand) {245                scale0 = scale1;246            } else {247                tickExit.call(tickTransform, scale1);248            }249            tickEnter.call(tickTransform, scale0);250            tickUpdate.call(tickTransform, scale1);251        });252    }253    axis.scale = function (x) {254        if (!arguments.length) { return scale; }255        scale = x;256        return axis;257    };258    axis.orient = function (x) {259        if (!arguments.length) { return orient; }260        orient = x in {top: 1, right: 1, bottom: 1, left: 1} ? x + "" : "bottom";261        return axis;262    };263    axis.tickFormat = function (format) {264        if (!arguments.length) { return tickFormat; }265        tickFormat = format;266        return axis;267    };268    axis.tickCentered = function (isCentered) {269        if (!arguments.length) { return tickCentered; }270        tickCentered = isCentered;271        return axis;272    };273    axis.tickOffset = function () {274        return tickOffset;275    };276    axis.tickInterval = function () {277        var interval, length;278        if (params.isCategory) {279            interval = tickOffset * 2;280        }281        else {282            length = axis.g.select('path.domain').node().getTotalLength() - outerTickSize * 2;283            interval = length / axis.g.selectAll('line').size();284        }285        return interval === Infinity ? 0 : interval;286    };287    axis.ticks = function () {288        if (!arguments.length) { return tickArguments; }289        tickArguments = arguments;290        return axis;291    };292    axis.tickCulling = function (culling) {293        if (!arguments.length) { return tickCulling; }294        tickCulling = culling;295        return axis;296    };297    axis.tickValues = function (x) {298        if (typeof x === 'function') {299            tickValues = function () {300                return x(scale.domain());301            };302        }303        else {304            if (!arguments.length) { return tickValues; }305            tickValues = x;306        }307        return axis;308    };309    return axis;...Learn to execute automation testing from scratch with LambdaTest Learning Hub. Right from setting up the prerequisites to run your first automation test, to following best practices and diving deeper into advanced test scenarios. LambdaTest Learning Hubs compile a list of step-by-step guides to help you be proficient with different test automation frameworks i.e. Selenium, Cypress, TestNG etc.
You could also refer to video tutorials over LambdaTest YouTube channel to get step by step demonstration from industry experts.
Get 100 minutes of automation test minutes FREE!!
