AtcoderDevotionGraph

Overlay your devoiting graph on your rating graph

Устаревшая версия за 24.11.2020. Перейдите к последней версии.

Чтобы установить этот скрипт, вы сначала должны установить расширение браузера, например Tampermonkey, Greasemonkey или Violentmonkey.

Чтобы установить этот скрипт, вы сначала должны установить расширение браузера, например Tampermonkey или Violentmonkey.

Чтобы установить этот скрипт, вы сначала должны установить расширение браузера, например Tampermonkey или Violentmonkey.

Чтобы установить этот скрипт, вы сначала должны установить расширение браузера, например Tampermonkey или Userscripts.

Чтобы установить этот скрипт, сначала вы должны установить расширение браузера, например Tampermonkey.

Чтобы установить этот скрипт, вы должны установить расширение — менеджер скриптов.

(у меня уже есть менеджер скриптов, дайте мне установить скрипт!)

Чтобы установить этот стиль, сначала вы должны установить расширение браузера, например Stylus.

Чтобы установить этот стиль, сначала вы должны установить расширение браузера, например Stylus.

Чтобы установить этот стиль, сначала вы должны установить расширение браузера, например Stylus.

Чтобы установить этот стиль, сначала вы должны установить расширение — менеджер стилей.

Чтобы установить этот стиль, сначала вы должны установить расширение — менеджер стилей.

Чтобы установить этот стиль, сначала вы должны установить расширение — менеджер стилей.

(у меня уже есть менеджер стилей, дайте мне установить скрипт!)

// ==UserScript==
// @name         AtcoderDevotionGraph
// @namespace    http://atcoder.jp/
// @version      0.3.0β
// @description  Overlay your devoiting graph on your rating graph
// @author       kemkemG0
// @include      *://atcoder.jp/users*
// @exclude      *://atcoder.jp/users/*?graph=rank
// @exclude      *://atcoder.jp/users/*/history*
// @grant        none
// @require      https://code.jquery.com/jquery-1.8.0.min.js
//@run-at        document-end

// ==/UserScript==

"use strict";

(async () => {

    //##//##//##//##//##//##//##//##//##//##//##//##//##//##//##//##//##//##//##//##//##//##//##//##//##//##//##//##//##//##

    let scriptsArray = $('script');//<script>タグのものを配列に突っ込む
    scriptsArray[14].remove(); // 対象のタグを消す記述 x[14]がグラフを読み込むjs

    //なんでこれ必要??  -->>一度読み込んだscriptタグはDOMから消しても効果は残るからそれを消すため
    let copyPage = $("html").clone().html(); // 対象のタグが消えたページをコピー
    $("html").remove(); // ページをまるごと削除
    document.write(copyPage); // コピーしてあったページ内容をペースト

    const element = document.getElementsByClassName('btn-text-group')[document.getElementsByClassName('btn-text-group').length - 1];
    const insertButton = Object.assign(document.createElement('button'), {
        className: '',
        id: 'shoujinButtonID',
        style: '\
    margin-left:50px;\
    appearance: none;\
    border: 0;\
    border-radius: 5px;\
    background: #20b2aa;\
    color: #fff;\
    padding: 8px 16px;\
    font-size: 16px;\
    '
    }
    );
    insertButton.textContent = "精進グラフの表示を切り替える"
    element.appendChild(insertButton)
    console.log(element)


    //##//##//##//##//##//##//##//##//##//##//##//##//##//##//##//##//##//##//##//##//##//##//##//##//##//##//##//##//##//##

    // const
    const MARGIN_VAL_X = 86400 * 30;
    const MARGIN_VAL_Y_LOW = 100;//
    const MARGIN_VAL_Y_HIGH = 300;//自分の最高レート+表示する領域
    const OFFSET_X = 50;//グラフの位置?
    const OFFSET_Y = 5;
    const DEFAULT_WIDTH = 640;
    let canvas_status = document.getElementById("ratingStatus");
    // <canvas id="ratingStatus" width="1280" height="160"
    // style="max-width: 640px; max-height: 80px; height: 100%; width: 100%;"></canvas>
    const STATUS_WIDTH = canvas_status.width - OFFSET_X - 10;
    const STATUS_HEIGHT = canvas_status.height - OFFSET_Y - 5;
    let canvas_graph = document.getElementById("ratingGraph");
    // <canvas id="ratingGraph"
    // width="1280" height="720"
    // style="max-width: 640px; max-height: 360px; height: 100%; width: 100%;"></canvas>
    const PANEL_WIDTH = canvas_graph.width - OFFSET_X - 10;
    const PANEL_HEIGHT = canvas_graph.height - OFFSET_Y - 30;
    //HIGHEST:932 とかの吹き出しのサイズ
    const HIGHEST_WIDTH = 80;
    const HIGHEST_HEIGHT = 20;
    const LABEL_FONT = "12px Lato";
    const START_YEAR = 2010;
    const MONTH_NAMES = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
    const YEAR_SEC = 86400 * 365;
    const STEP_SIZE = 400;//グラフのy軸のステップ数
    const COLORS = [
        [0, "#808080", 0.15],
        [400, "#804000", 0.15],
        [800, "#008000", 0.15],
        [1200, "#00C0C0", 0.2],
        [1600, "#0000FF", 0.1],
        [2000, "#C0C000", 0.25],
        [2400, "#FF8000", 0.2],
        [2800, "#FF0000", 0.1]
    ];

    //??????????????????
    const STAR_MIN = 3200;
    const PARTICLE_MIN = 3;
    const PARTICLE_MAX = 20;
    const LIFE_MAX = 30;
    const EPS = 1e-9;

    let cj = createjs;
    let stage_graph, stage_status;

    // graph
    let panel_shape, border_shape;
    let chart_container, line_shape, vertex_shapes, highest_shape;
    let n, x_min, x_max, y_min, y_max;

    //devoting graph
    let devoting_panel_shape, devoting_border_shape;
    let devoting_chart_container, devoting_line_shape, devoting_vertex_shapes, devoting_highest_shape;
    let devoting_n, devoting_x_min, devoting_x_max, devoting_y_min, devoting_y_max;
    let devoting_rating_history = []

    // status
    let border_status_shape;
    let rating_text, place_text, diff_text, date_text, contest_name_text;
    let particles;
    let standings_url;
    const username = document.getElementsByClassName("username")[0].textContent;
    let allJson;
    try {
        const res = await fetch("https://kenkoooo.com/atcoder/atcoder-api/results?user=" + username);
        allJson = await res.json()
    } catch (reaseon) { console.log('try失敗') }



    console.log(111)
    {
        let isExist={};
        for (let i = 0; i < allJson.length; i++) {
            if (allJson[i].result == 'AC' && allJson[i].point <= 3000 && isExist[allJson[i].problem_id]==undefined) {
                devoting_rating_history.push({ ...allJson[i] });
                isExist[allJson[i].problem_id]=0;
            }
        }
    }
    function compare(a, b) { return a.epoch_second - b.epoch_second; }//比較関数
    devoting_rating_history.sort(compare);//時間順にソート

    for (let i = 0; i < devoting_rating_history.length - 1; i++) {//合計の累積和的な
        devoting_rating_history[i + 1].point += devoting_rating_history[i].point;
        devoting_rating_history[i].point /= 100;
    }
    devoting_rating_history[devoting_rating_history.length - 1].point /= 100;
    //今までの累積和/100 が高さ

    console.log(222)
    console.log("before init()")
    console.log("This should be excuted after init()")

    let shoujinButtonID = document.getElementById('shoujinButtonID');
    shoujinButtonID.addEventListener('click', function () {
        devoting_chart_container.visible = !devoting_chart_container.visible;
        stage_graph.update();
    });


    console.log(devoting_rating_history.length);
    //クリエイトjsとやらをつかっている
    console.log(333)


    //いい感じにキャンバスの大きさを設定してマウスオーバーもONにする
    function initStage(stage, canvas) {
        let width = canvas.getAttribute('width');// <canvas width="">を取得
        let height = canvas.getAttribute('height');


        //最悪、なくても画質悪くなったが動いた よくわからん
        if (window.devicePixelRatio) {//ピクセル比 によって解像度を変える 本来は2のときに1にしたらぼやけた
            //縦横の設定
            canvas.setAttribute('width', Math.round(width * window.devicePixelRatio));//Math.round()は四捨五入
            canvas.setAttribute('height', Math.round(height * window.devicePixelRatio));
            stage.scaleX = stage.scaleY = window.devicePixelRatio;
        }
        //最大のキャンパスサイズ=もとのキャンバスサイズにする
        canvas.style.maxWidth = width + "px";
        canvas.style.maxHeight = height + "px";
        canvas.style.width = canvas.style.height = "100%";
        stage.enableMouseOver();
    }

    //parent===stageに図形を追加し、その図形をreturnで参照渡し
    function newShape(parent) {
        let s = new cj.Shape();
        parent.addChild(s);
        return s;
    }
    //上のテキストバージョン
    function newText(parent, x, y, font) {
        let t = new cj.Text("", font, "#000");
        t.x = x;
        t.y = y;
        t.textAlign = "center";
        t.textBaseline = "middle";
        parent.addChild(t);
        return t;
    }



    //多分一番の大元
    function init() {

        // window.alert('33333');

        //rating_history はHTML内で取得してある
        //rating_history=[{"EndTime":時間(単位不明),"NewRating":11,"OldRating":0,"Place":5200,"ContestName":"コンテスト名","StandingsUrl":"/contests/m-solutions2020/standings?watching=kemkemG0"}];
        n = rating_history.length;
        devoting_n = devoting_rating_history.length;
        if (n == 0) return;

        //console.log("initの中")

        //土台のステージ これに図形とかを追加していくイメージ
        stage_graph = new cj.Stage("ratingGraph");// Stage("canvasのID");
        stage_status = new cj.Stage("ratingStatus");
        initStage(stage_graph, canvas_graph);
        initStage(stage_status, canvas_status);

        //グラフのサイズ決定
        x_min = 100000000000;
        x_max = 0;
        y_min = 10000;
        y_max = 0;
        for (let i = 0; i < n; i++) {
            x_min = Math.min(x_min, rating_history[i].EndTime);
            x_max = Math.max(x_max, rating_history[i].EndTime);
            y_min = Math.min(y_min, rating_history[i].NewRating);
            y_max = Math.max(y_max, rating_history[i].NewRating);
        }
        x_min -= MARGIN_VAL_X;//最初にコンテストに参加した日ー1ヶ月
        x_max += MARGIN_VAL_X;//最後にコンテストに参加した日+1ヶ月
        y_min = Math.min(1500, Math.max(0, y_min - MARGIN_VAL_Y_LOW));//いい感じに高さも設定
        y_max += MARGIN_VAL_Y_HIGH;

        //精進グラフのサイズ決定
        devoting_x_min = 100000000000;
        devoting_x_max = 0;
        devoting_y_min = 10000;
        devoting_y_max = 0;
        for (let i = 0; i < devoting_rating_history.length; i++) {
            devoting_x_min = Math.min(devoting_x_min, devoting_rating_history[i].epoch_second);
            devoting_x_max = Math.max(devoting_x_max, devoting_rating_history[i].epoch_second);
            devoting_y_min = Math.min(devoting_y_min, devoting_rating_history[i].point);
            devoting_y_max = Math.max(devoting_y_max, devoting_rating_history[i].point);
        }
        devoting_x_min -= MARGIN_VAL_X;//最初にコンテストに参加した日ー1ヶ月
        devoting_x_max += MARGIN_VAL_X;//最後にコンテストに参加した日+1ヶ月
        devoting_y_min = Math.min(1500, Math.max(0, devoting_y_min - MARGIN_VAL_Y_LOW));//いい感じに高さも設定
        devoting_y_max += MARGIN_VAL_Y_HIGH;

        //形を決める
        y_min = Math.min(y_min, devoting_y_min);
        y_max = Math.max(y_max, devoting_y_max);
        x_min = Math.min(x_min, devoting_x_min);
        x_max = Math.max(x_max, devoting_x_max);

        console.log("I'm in inside of init 111")

        initBackground();//背景の描画
        initChart();//プロットと直線の描画

        console.log("I'm in inside of init 222")

        initDevotingChart()



        stage_graph.update();
        console.log("I'm in inside of init 333")

        initStatus();//グラフの上のコンテスト情報とかの描画
        stage_status.update();



        //window.alert('44444');  アラート→描画の順番なのはなぜなのか


        //マウスオーバー時のほわほわの管理
        cj.Ticker.setFPS(60);
        cj.Ticker.addEventListener("tick", handleTick);
        function handleTick(event) {
            updateParticles();
            stage_status.update();
        }
    }

    function getPer(x, l, r) {
        return (x - l) / (r - l);
    }

    function getColor(x) {
        for (let i = COLORS.length - 1; i >= 0; i--) {
            if (x >= COLORS[i][0]) return COLORS[i];
        }
        return [-1, "#000000", 0.1];
    }

    function initBackground() {

        panel_shape = newShape(stage_graph);//stage_graphに図形を追加、また panel_shapeはstage_graphの内部とつながってる(オブジェクトは参照渡し)
        panel_shape.x = OFFSET_X;
        panel_shape.y = OFFSET_Y;
        panel_shape.alpha = 0.3;

        border_shape = newShape(stage_graph);
        border_shape.x = OFFSET_X;
        border_shape.y = OFFSET_Y;

        // testtest = newShape(stage_graph);
        // testtest.graphics.beginFill("DarkRed"); // 赤色で描画するように設定
        // testtest.graphics.drawCircle(0, 0, 10); //半径 100px の円を描画
        // testtest.x = OFFSET_X; //OFFSET_XとOFFSET_Yにしたらグラフの中の四角形の左端になった
        // testtest.y = OFFSET_Y;

        //左の軸のレートの設定
        function newLabelY(s, y) {
            let t = new cj.Text(s, LABEL_FONT, "#000");
            t.x = OFFSET_X - 10;//理解
            t.y = OFFSET_Y + y;
            t.textAlign = "right";
            t.textBaseline = "middle";
            stage_graph.addChild(t);
        }
        //上と同様にX軸のラベルの設定
        function newLabelX(s, x, y) {
            let t = new cj.Text(s, LABEL_FONT, "#000");
            t.x = OFFSET_X + x;
            t.y = OFFSET_Y + PANEL_HEIGHT + 2 + y;
            t.textAlign = "center";
            t.textBaseline = "top";
            stage_graph.addChild(t);
        }

        //https://createjs.com/docs/easeljs/classes/Graphics.html Graphics Classのドキュメント
        let y1 = 0;



        // グラフの中の正方形のパネルを色を設定
        for (let i = COLORS.length - 1; i >= 0; i--) {
            let y2 = PANEL_HEIGHT - PANEL_HEIGHT * getPer(COLORS[i][0], y_min, y_max);
            if (y2 > 0 && y1 < PANEL_HEIGHT) {
                y1 = Math.max(y1, 0);                           //rect ( x, y, w , h )
                panel_shape.graphics.beginFill(COLORS[i][1]).rect(0, y1, PANEL_WIDTH, Math.min(y2, PANEL_HEIGHT) - y1);
            }
            y1 = y2;
        }
        console.log('after FOR')

        //Y軸ラベルの設定
        for (let i = 0; i <= y_max; i += STEP_SIZE) {
            if (i >= y_min) {
                let y = PANEL_HEIGHT - PANEL_HEIGHT * getPer(i, y_min, y_max);
                newLabelY(String(i), y);
                border_shape.graphics.beginStroke("#FFF").setStrokeStyle(0.5);
                if (i == 2000) border_shape.graphics.beginStroke("#000");
                border_shape.graphics.moveTo(0, y).lineTo(PANEL_WIDTH, y);
            }
        }
        border_shape.graphics.beginStroke("#FFF").setStrokeStyle(0.5);

        let month_step = 6;
        for (let i = 3; i >= 1; i--) {
            if (x_max - x_min <= YEAR_SEC * i + MARGIN_VAL_X * 2) month_step = i;//初めてすぐの人は短めに
        }

        //X軸ラベルの設定
        let first_flag = true;
        for (let i = START_YEAR; i < 3000; i++) {
            let break_flag = false;
            for (let j = 0; j < 12; j += month_step) {
                let month = ('00' + (j + 1)).slice(-2);
                let unix = Date.parse(String(i) + "-" + month + "-01T00:00:00") / 1000;
                if (x_min < unix && unix < x_max) {
                    let x = PANEL_WIDTH * getPer(unix, x_min, x_max);
                    if (j == 0 || first_flag) {
                        newLabelX(MONTH_NAMES[j], x, 0);
                        newLabelX(String(i), x, 13);
                        first_flag = false;
                    } else {
                        newLabelX(MONTH_NAMES[j], x, 0);
                    }
                    border_shape.graphics.mt(x, 0).lt(x, PANEL_HEIGHT)
                }
                if (unix > x_max) { break_flag = true; break; }
            }
            if (break_flag) break;
        }
        border_shape.graphics.s("#888").ss(1.5).rr(0, 0, PANEL_WIDTH, PANEL_HEIGHT, 2);
    }


    function initChart() {
        chart_container = new cj.Container();//コンテナでまとめると、同時に動かせたりして良い
        stage_graph.addChild(chart_container);
        chart_container.shadow = new cj.Shadow("rgba(0,0,0,0.3)", 1, 2, 3);//チャートの下に影

        line_shape = newShape(chart_container);
        highest_shape = newShape(chart_container);
        vertex_shapes = new Array();

        //マウスおいたら丸が大きくなるやつ
        function mouseoverVertex(e) {
            vertex_shapes[e.target.i].scaleX = vertex_shapes[e.target.i].scaleY = 1.2;
            stage_graph.update();
            setStatus(rating_history[e.target.i], true);
        };
        function mouseoutVertex(e) {
            vertex_shapes[e.target.i].scaleX = vertex_shapes[e.target.i].scaleY = 1;
            stage_graph.update();
        };

        let highest_i = 0;
        for (let i = 0; i < n; i++) {
            if (rating_history[highest_i].NewRating < rating_history[i].NewRating) {
                highest_i = i;
            }
        }
        //historyの数だけ配列にpushしてイベントリスナーも設定
        for (let i = 0; i < n; i++) {
            vertex_shapes.push(newShape(chart_container));
            vertex_shapes[i].graphics.beginStroke("#FFF");
            if (i == highest_i) vertex_shapes[i].graphics.s("#000");//Highestなら外枠を黒に
            vertex_shapes[i].graphics.setStrokeStyle(0.5).beginFill(getColor(rating_history[i].NewRating)[1]).dc(0, 0, 3.5);

            vertex_shapes[i].x = OFFSET_X + PANEL_WIDTH * getPer(rating_history[i].EndTime, x_min, x_max);
            vertex_shapes[i].y = OFFSET_Y + (PANEL_HEIGHT - PANEL_HEIGHT * getPer(rating_history[i].NewRating, y_min, y_max));

            vertex_shapes[i].i = i;//なにこれ??

            let hitArea = new cj.Shape();
            hitArea.graphics.f("#000").dc(1.5, 1.5, 6);
            vertex_shapes[i].hitArea = hitArea;
            vertex_shapes[i].addEventListener("mouseover", mouseoverVertex);
            vertex_shapes[i].addEventListener("mouseout", mouseoutVertex);
        }

        {//highest 関連
            let dx = 80;
            if ((x_min + x_max) / 2 < rating_history[highest_i].EndTime) dx = -80;
            let x = vertex_shapes[highest_i].x + dx;
            let y = vertex_shapes[highest_i].y - 16;
            highest_shape.graphics.s("#FFF").mt(vertex_shapes[highest_i].x, vertex_shapes[highest_i].y).lt(x, y);
            highest_shape.graphics.s("#888").f("#FFF").rr(x - HIGHEST_WIDTH / 2, y - HIGHEST_HEIGHT / 2, HIGHEST_WIDTH, HIGHEST_HEIGHT, 2);
            highest_shape.i = highest_i;
            let highest_text = newText(stage_graph, x, y, "12px Lato");
            highest_text.text = "Highest: " + rating_history[highest_i].NewRating;
            highest_shape.addEventListener("mouseover", mouseoverVertex);
            highest_shape.addEventListener("mouseout", mouseoutVertex);
        }


        for (let j = 0; j < 2; j++) {
            if (j == 0) line_shape.graphics.s("#AAA").ss(2);
            else line_shape.graphics.s("#FFF").ss(0.5);//線の種類を変えてる? よくわからん

            line_shape.graphics.mt(vertex_shapes[0].x, vertex_shapes[0].y);
            for (let i = 0; i < n; i++) {
                line_shape.graphics.lt(vertex_shapes[i].x, vertex_shapes[i].y);
            }
        }
    }


    function initDevotingChart() {
        devoting_chart_container = new cj.Container();//コンテナでまとめると、同時に動かせたりして良い
        stage_graph.addChild(devoting_chart_container);//これは devoting_じゃない
        devoting_chart_container.shadow = new cj.Shadow("rgba(0,0,0,0.3)", 1, 2, 3);//チャートの下に影

        devoting_line_shape = newShape(devoting_chart_container);
        devoting_highest_shape = newShape(devoting_chart_container);
        devoting_vertex_shapes = new Array();

        // //マウスおいたら丸が大きくなるやつ
        // function devoting_mouseoverVertex(e) {
        //     devoting_vertex_shapes[e.target.i].scaleX = devoting_vertex_shapes[e.target.i].scaleY = 2;
        //     stage_graph.update();
        // };
        // function devoting_mouseoutVertex(e) {
        //     devoting_vertex_shapes[e.target.i].scaleX = devoting_vertex_shapes[e.target.i].scaleY = 1;
        //     stage_graph.update();
        // };


        //historyの数だけ配列にpushしてイベントリスナーも設定
        for (let i = 0; i < devoting_n; i++) {
            devoting_vertex_shapes.push(newShape(devoting_chart_container));
            devoting_vertex_shapes[i].graphics.beginStroke("#FFF");
            if (i == devoting_n - 1) {
                devoting_vertex_shapes[i].graphics.s("#000");
                devoting_vertex_shapes[i].graphics.setStrokeStyle(1).beginFill(getColor(devoting_rating_history[i].point)[1]).dc(0, 0, 2.5);
            }
            else {
                devoting_vertex_shapes[i].graphics.setStrokeStyle(0.5).beginFill(getColor(devoting_rating_history[i].point)[1]).dc(0, 0, 2);
            }
            devoting_vertex_shapes[i].x = OFFSET_X + PANEL_WIDTH * getPer(devoting_rating_history[i].epoch_second, x_min, x_max);//devotingじゃないほうに合わせる?
            devoting_vertex_shapes[i].y = OFFSET_Y + (PANEL_HEIGHT - PANEL_HEIGHT * getPer(devoting_rating_history[i].point, y_min, y_max));
            devoting_vertex_shapes[i].i = i;
            let hitArea = new cj.Shape();
            hitArea.graphics.f("#000").dc(1.5, 1.5, 6);
            devoting_vertex_shapes[i].hitArea = hitArea;
            // devoting_vertex_shapes[i].addEventListener("mouseover", devoting_mouseoverVertex);
            // devoting_vertex_shapes[i].addEventListener("mouseout", devoting_mouseoutVertex);
        }

        //チャートの線関連
        for (let index = 0; index < 2; index++) {
            if (index == 0) devoting_line_shape.graphics.s("#AAA").ss(2);
            else devoting_line_shape.graphics.s("#FFF").ss(0.5);
            devoting_line_shape.graphics.mt(devoting_vertex_shapes[0].x, devoting_vertex_shapes[0].y);
            for (let i = 0; i < devoting_rating_history.length; i++) {
                devoting_line_shape.graphics.lt(devoting_vertex_shapes[i].x, devoting_vertex_shapes[i].y);
            }
        }


    }

    function initStatus() {
        border_status_shape = newShape(stage_status);
        rating_text = newText(stage_status, OFFSET_X + 75, OFFSET_Y + STATUS_HEIGHT / 2, "48px 'Squada One'");
        place_text = newText(stage_status, OFFSET_X + 160, OFFSET_Y + STATUS_HEIGHT / 2.7, "16px Lato");
        diff_text = newText(stage_status, OFFSET_X + 160, OFFSET_Y + STATUS_HEIGHT / 1.5, "11px Lato");
        diff_text.color = '#888';
        date_text = newText(stage_status, OFFSET_X + 200, OFFSET_Y + STATUS_HEIGHT / 4, "14px Lato");
        contest_name_text = newText(stage_status, OFFSET_X + 200, OFFSET_Y + STATUS_HEIGHT / 1.6, "20px Lato");
        date_text.textAlign = contest_name_text.textAlign = "left";
        contest_name_text.maxWidth = STATUS_WIDTH - 200 - 10;
        {
            let hitArea = new cj.Shape(); hitArea.graphics.f("#000").r(0, -12, contest_name_text.maxWidth, 24);
            contest_name_text.hitArea = hitArea;
            contest_name_text.cursor = "pointer";
            contest_name_text.addEventListener("click", function () {
                location.href = standings_url;
            });
        }
        particles = new Array();
        for (let i = 0; i < PARTICLE_MAX; i++) {
            particles.push(newText(stage_status, 0, 0, "64px Lato"));
            particles[i].visible = false;
        }
        setStatus(rating_history[rating_history.length - 1], false);
    }

    function getRatingPer(x) {
        let pre = COLORS[COLORS.length - 1][0] + STEP_SIZE;
        for (let i = COLORS.length - 1; i >= 0; i--) {
            if (x >= COLORS[i][0]) return (x - COLORS[i][0]) / (pre - COLORS[i][0]);
            pre = COLORS[i][0];
        }
        return 0;
    }

    //@#//@#//@#//@#//@#//@#//@#//  関係ない  //@#//@#//@#//@#//@#//@#//@#//@#//@#//@#
    function getOrdinal(x) {
        let s = ["th", "st", "nd", "rd"], v = x % 100;
        return x + (s[(v - 20) % 10] || s[v] || s[0]);
    }
    function getDiff(x) {
        let sign = x == 0 ? 'ツア' : (x < 0 ? '-' : '+');
        return sign + Math.abs(x);
    }
    function setStatus(data, particle_flag) {
        let date = new Date(data.EndTime * 1000);
        let rating = data.NewRating, old_rating = data.OldRating;
        let place = data.Place;
        let contest_name = data.ContestName;
        let tmp = getColor(rating); let color = tmp[1], alpha = tmp[2];
        border_status_shape.graphics.c().s(color).ss(1).rr(OFFSET_X, OFFSET_Y, STATUS_WIDTH, STATUS_HEIGHT, 2);
        rating_text.text = rating;
        rating_text.color = color;
        place_text.text = getOrdinal(place);
        diff_text.text = getDiff(rating - old_rating);
        date_text.text = date.toLocaleDateString();
        contest_name_text.text = contest_name;
        if (particle_flag) {
            let particle_num = parseInt(Math.pow(getRatingPer(rating), 2) * (PARTICLE_MAX - PARTICLE_MIN) + PARTICLE_MIN);
            setParticles(particle_num, color, alpha, rating);
        }
        standings_url = data.StandingsUrl;
    }
    //Particle は マウスオーバー時のくるくるのやつw
    function setParticle(particle, x, y, color, alpha, star_flag) {
        particle.x = x;
        particle.y = y;
        let ang = Math.random() * Math.PI * 2;
        let speed = Math.random() * 4 + 4;
        particle.vx = Math.cos(ang) * speed;
        particle.vy = Math.sin(ang) * speed;
        particle.rot_speed = Math.random() * 20 + 10;
        particle.life = LIFE_MAX;
        particle.visible = true;
        particle.color = color;
        if (star_flag) {
            particle.text = "★";
        } else {
            particle.text = "@";
        }
        particle.alpha = alpha;
    }
    function setParticles(num, color, alpha, rating) {
        for (let i = 0; i < PARTICLE_MAX; i++) {
            if (i < num) {
                setParticle(particles[i], rating_text.x, rating_text.y, color, alpha, rating >= STAR_MIN);
            } else {
                particles[i].life = 0;
                particles[i].visible = false;
            }
        }
    }
    function updateParticle(particle) {
        if (particle.life <= 0) {
            particle.visible = false;
            return;
        }
        particle.x += particle.vx;
        particle.vx *= 0.9;
        particle.y += particle.vy;
        particle.vy *= 0.9;
        particle.life--;
        particle.scaleX = particle.scaleY = particle.life / LIFE_MAX;
        particle.rotation += particle.rot_speed;
    }

    function updateParticles() {
        for (let i = 0; i < PARTICLE_MAX; i++) {
            if (particles[i].life > 0) {
                updateParticle(particles[i]);
            }
        }
    }
    //@#//@#//@#//@#//@#//@#//@#//  関係ない  //@#//@#//@#//@#//@#//@#//@#//@#//@#//@#




    init();


})()