Raw Source
myso / 네이버 블로그 통계 어드밴스드

// ==UserScript==
// @namespace    https://tampermonkey.myso.kr/
// @name         네이버 블로그 통계 어드밴스드
// @description  네이버 블로그 통계 그래프에 분석 및 예측에 필요한 다양한 요소를 추가해줍니다.
// @copyright    2021, myso (https://tampermonkey.myso.kr)
// @license      Apache-2.0
// @version      1.1.5
// @updateURL    https://github.com/myso-kr/kr.myso.tampermonkey/raw/master/service/com.naver.blog-analytics.advanced.user.js
// @downloadURL  https://github.com/myso-kr/kr.myso.tampermonkey/raw/master/service/com.naver.blog-analytics.advanced.user.js
// @author       Won Choi
// @connect      naver.com
// @match        https://blog.stat.naver.com/blog/*
// @match        https://blog.stat.naver.com/m/blog/*
// @grant        GM_addStyle
// @grant        GM_xmlhttpRequest
// @require      https://cdn.jsdelivr.net/npm/kr.myso.tampermonkey@1.0.25/assets/vendor/gm-app.js
// @require      https://cdn.jsdelivr.net/npm/kr.myso.tampermonkey@1.0.25/assets/vendor/gm-add-style.js
// @require      https://cdn.jsdelivr.net/npm/kr.myso.tampermonkey@1.0.25/assets/vendor/gm-add-script.js
// @require      https://cdn.jsdelivr.net/npm/kr.myso.tampermonkey@1.0.25/assets/vendor/gm-xmlhttp-request-async.js
// @require      https://cdn.jsdelivr.net/npm/kr.myso.tampermonkey@1.0.25/assets/vendor/gm-xmlhttp-request-cors.js
// @require      https://cdn.jsdelivr.net/npm/kr.myso.tampermonkey@1.0.25/assets/vendor/gm-xmlhttp-request-hook.js
// @require      https://cdn.jsdelivr.net/npm/kr.myso.tampermonkey@1.0.25/assets/donation.js
// @require      https://cdn.amcharts.com/lib/4/core.js
// @require      https://cdn.amcharts.com/lib/4/charts.js
// @require      https://cdn.amcharts.com/lib/4/themes/animated.js
// @require      https://cdnjs.cloudflare.com/ajax/libs/bluebird/3.7.2/bluebird.min.js
// @require      https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js
// @require      https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.1/moment-with-locales.min.js
// @require      https://cdnjs.cloudflare.com/ajax/libs/regression/2.0.1/regression.min.js
// ==/UserScript==

// ==OpenUserJS==
// @author myso
// ==/OpenUserJS==
GM_App(async function main() {
    const root = document.querySelector('#_root'); if(!root) return;
    GM_donation('#_root');
    GM_addStyle(`html, body { height: 100%; overflow:hidden; } #_root { overflow-y: scroll; height: 100%; }`);
    GM_addStyle(`.u_ni_chart_amchart + * { display: none; }`);
    GM_addStyle(`.u_ni_chart_amchart { width: 100%; height: 480px; }`);
    GM_xmlhttpRequestHook((event, res) => {
        // parse data
        function NB_blogStatObject(resp) {
            resp && resp.map((item)=>{
                const data = item.data || {};
                const rows = data.rows || {};
                const cols = Object.keys(rows);
                const head = cols[0], headdata = rows[head];
                resp[item.dataId] = headdata ? headdata.map((nil, idx) => cols.reduce((r, key)=>(r[key] = rows[key][idx], r), {})) : data;
            });
            return resp;
        }
        const dataset = NB_blogStatObject(res.result.statDataList);
        const head = ['total', 'cv', 'uv', 'visit', 'averageVisit', 'revisit', 'averageDuration', 'relationVisit', 'relationDelta', 'play_cnt'];
        const temp = head.reduce((r, k)=>_.get(dataset, k, r), ''); if(!temp) return res;
        const poly = regression.linear(_.map(temp, (item, offset, dataset) => {
            const name = head.reduce((r, k)=>_.has(item, k) ? k : r, '');
            const curr = item[name], prev = (dataset[offset - 1] || {})[name] || curr;
            return [prev, curr];
        }));
        const data = _.map(temp, (item, offset)=>(item.polynomial = poly.points[offset][1], item));
        const keys = Object.keys(data[0] || {}).filter(k=>!['date'].includes(k));
        // create chart
        const component = document.querySelector('.u_ni_chart_component'); if(!component) return res;
        const canvas = component.querySelector('.u_ni_chart_amchart') || document.createElement('div');
        if(!canvas.className) { canvas.className = 'u_ni_chart_amchart'; component.append(canvas); }
        am4core.useTheme(am4themes_animated);
        const chart = am4core.create(canvas, am4charts.XYChart);
        chart.data = _.map(data, (item)=>(item.date = moment(item.date).toDate(), item));
        const categoryAxis = chart.xAxes.push(new am4charts.DateAxis());
        categoryAxis.renderer.grid.template.location = 0;
        categoryAxis.renderer.minGridDistance = 30;

        const valueAxis = chart.yAxes.push(new am4charts.ValueAxis());
        function createSeriesAndAxis(field, name, topMargin, bottomMargin) {
            const series = chart.series.push(new am4charts.LineSeries());
            series.dataFields.valueY = field;
            series.dataFields.dateX = "date";
            series.name = name;
            series.tooltipText = "{dateX}: [b]{valueY}[/]";
            series.strokeWidth = 2;
            series.yAxis = valueAxis;

            valueAxis.renderer.line.strokeOpacity = 1;
            valueAxis.renderer.line.stroke = series.stroke;
            valueAxis.renderer.grid.template.stroke = series.stroke;
            valueAxis.renderer.grid.template.strokeOpacity = 0.1;
            valueAxis.renderer.labels.template.fill = series.stroke;
            valueAxis.renderer.minGridDistance = 20;
            valueAxis.align = "right";

            if (topMargin && bottomMargin) {
                valueAxis.marginTop = 10;
                valueAxis.marginBottom = 10;
            }
            else {
                if (topMargin) {
                    valueAxis.marginTop = 20;
                }
                if (bottomMargin) {
                    valueAxis.marginBottom = 20;
                }
            }

            var bullet = series.bullets.push(new am4charts.CircleBullet());
            bullet.circle.stroke = am4core.color("#fff");
            bullet.circle.strokeWidth = 2;
        }
        keys.map((key)=>createSeriesAndAxis(key, key, false, false));

        chart.legend = new am4charts.Legend();
        chart.cursor = new am4charts.XYCursor();

        chart.leftAxesContainer.layout = "vertical";
        return res;
    });
});