flyink13 / BtUtils

// ==UserScript==
// @name         BtUtils
// @namespace    http://tampermonkey.net/
// @version      2.2
// @description  Добавляет фичи в vk.com/bugtracker
// @updateURL https://openuserjs.org/meta/flyink13/BtUtils.meta.js
// @author       Flyink13
// @match        https://vk.com/bugtracker*
// @license MIT
// @copyright 2017, flyink13 (https://openuserjs.org/users/flyink13)
// @grant        none
// ==/UserScript==
/* global vk, cur, extend, ce, ajax, ge, geByClass, geByClass1, q2ajx, ajx2q, stManager */

function BtUtils() {
    var on = {},
        default_rules = [
            'Пожалуйста, ознакомьтесь с:',
            '1) Правилами составления отчетов https://vk.cc/71Wc3i',
            '2) Поиском по отчётам, во избежание дублирования отчетов.',
            '3) Описанием тестируемого продукта %productDescriptionLink%.'
        ].join('\n'),
        blank_report = '1) \n2) \n\nРезультат: \nОжидание:\n',
        bt_links = [];

    function qce(name, d, c) {
        var el, i;
        if (Array.isArray(d)) {
            c = d;
            d = {};
        }
        if (typeof d == 'string') {
            d = {
                textContent: d
            };
        }
        if (/^(.+?)(?:#(.+?))?(?:\.(.+))?$/.test(name)) {
            name = name.match(/^(.+?)(?:#(.+?))?(?:\.(.+))?$/);
            extend(d, {
                tagName: name[1],
                id: name[2] || '',
                className: name[3] || ''
            });
            name = name[1];
        }

        el = ce(name, d);
        extend(el.style, d.style);
        if (c) {
            for (i = 0; i < c.length; i += 1) {
                el.appendChild(c[i] instanceof HTMLElement ? c[i] : qce.apply(this, c[i]));
            }
        }
        return el;
    }

    function createBtSettingsRow(name, childs) {
        return qce('div.bt_hform_block clear_fix', [
            ['div.bt_hform_label', name],
            ['div.bt_hform_control', childs]
        ]);
    }

    function createCounter(label, q) {
        var count = qce('div.count', '---');
        var page_counter = qce('a.page_counter', {
            href: '/bugtracker?' + ajx2q(q)
        }, [
            count, ['div.label', label]
        ]);
        ajax.plainpost('/bugtracker', {
            al: 1,
            load: 1,
            status: q.status,
            mid: q.mid,
            severity: q.severity
        }, function onDone(r) {
            r = r.match(/bt_reports_found.+?(\d+)/);
            if (!r) {
                count.textContent = '???';
            } else {
                count.textContent = r[1];
            }
        });
        return page_counter;
    }

    function drawBtUtilsBlock(title) {
        var bt_utils_content;
        if (ge('bt_utils')) {
            ge('bt_utils_title').textContent = title;
            return ge('bt_utils_content');
        }
        bt_utils_content = qce('div.bt_utils_content', '');
        ge('narrow_column').appendChild(qce('div#bt_utils.page_block', [
            ['div.bt_sb_search_wrap', [
                ['div#bt_utils_title.bt_sb_search_filter_label', title],
                bt_utils_content
            ]]
        ]));
        return bt_utils_content;
    }

    function showBtVideoPlayer(href) {
        document.body.appendChild(
            qce('div.bt_video', {
                onclick: function closeVideo(event) {
                    if (this !== event.target) return 1;
                    this.outerHTML = '';
                }
            }, [
                ['video', {
                    src: href,
                    controls: 1,
                    onclick: function toggleVideoState() {
                        if (this.paused) {
                            this.play();
                        } else {
                            this.pause();
                        }
                    }
                }]
            ]));
        return false;
    }

    on.settings = function btOnSettings() {
        ge('wide_column').appendChild(
            qce('div.page_block bt_page', [
                ['h2.page_block_h2', [
                    ['div.page_block_header clear_fix', [
                        ['div.page_block_header_inner _header_inner', [
                            ['div.ui_crumb', 'Настройки BtUtils']
                        ]]
                    ]]
                ]],
                ['div.bt_page_content', [
                    ['div.bt_settings', [
                        createBtSettingsRow('Шаблон отчета', [qce('textarea.text dark bt_form_text', {
                            style: {
                                width: '315px',
                                height: '105px'
                            },
                            value: localStorage.blank_report || blank_report,
                            onkeyup: function onKeyUp() {
                                localStorage.blank_report = this.value;
                                console.log('blank_report changed', this.value);
                            }
                        })]),
                        createBtSettingsRow('Шаблон комментария', [qce('textarea.text dark bt_form_text', {
                            style: {
                                width: '315px',
                                height: '105px'
                            },
                            value: localStorage.btRules || default_rules,
                            onkeyup: function onKeyUp() {
                                localStorage.btRules = this.value;
                                console.log('btRules changed', this.value);
                            }
                        })])
                    ]]
                ]]
            ])
        );
    };

    on.reporters = function btOnReporters(query) {
        var d_all, info, BtUtilsBlock, c;
        if (!query.product) query.product = 0;
        if (!geByClass('bt_reporter_row')) return;
        d_all = 0;
        info = JSON.parse(localStorage.getItem('reporters_info_' + query.product) || '{}');
        BtUtilsBlock = drawBtUtilsBlock('Обновления');
        c = geByClass('bt_reporter_row').map(function drawRowCounter(e) {
            var owner = geByClass1('bt_reporter_row_name', e);
            var id = 'reporter_' + owner.href.substr(42);
            var reports = geByClass1('bt_reporter_row_count', e).nextElementSibling;
            var d = reports.textContent - (info[id] || 0);
            if (id == 'reporter_' + vk.id) owner.parentNode.style.fontWeight = 'bold';
            if (d !== 0) {
                d_all += d;
                info[id] = reports.textContent;
                reports.textContent += ' (' + (d > 0 ? '+' + d : d) + ')';
                reports.style.fontWeight = 'bold';
                reports.style.color = d > 0 ? 'green' : 'red';
            }
            return [owner.textContent, d];
        }).filter(function filterEmpty(x) {
            return !!x[1];
        }).sort(function sortTop(a, b) {
            return b[1] - a[1];
        }).map(function toUserFormat(r) {
            return r[0] + (r[1] > 0 ? ('\t+' + r[1]) : ('\t-' + -r[1]));
        });
        localStorage.setItem('reporters_info_' + query.product, JSON.stringify(info));
        if (geByClass1('bt_reporter_header_row__pos')) {
            geByClass1('bt_reporter_header_row__pos').style.color = 'black';
        }
        if (d_all) {
            geByClass1('bt_reporter_header_row__count').style.color = d_all > 0 ? 'green' : 'red';
            BtUtilsBlock.innerHTML = '<ol><li>' + c.join('<li>') + '</ol>';
        } else {
            BtUtilsBlock.innerHTML = 'Пусто :(';
        }

        setTimeout(function expandList() {
            var btFilter;
            if (!cur || !cur.btProducts) return;

            btFilter = geByClass('page_block')[1];
            btFilter.className = 'page_block ui_rmenu ui_rmenu_pr bt_hide_products';
            btFilter.role = 'list';
            btFilter.innerHTML = '';
            cur.btProducts.forEach(function addListItem(p) {
                btFilter.appendChild(ce('a', {
                    className: 'ui_rmenu_item _ui_item_p' + p[0] + (p[0] == cur.btProduct ? ' ui_rmenu_item_sel' : ''),
                    href: 'bugtracker?act=reporters&product=' + p[0],
                    onclick: 'return uiRightMenu.go(this, event);',
                    id: 'ui_rmenu_p' + p[0]
                })).appendChild(ce('span', {
                    className: 'bt_hide_products_placeholder',
                    title: (p[0] == '0' ? 'Все продукты' : p[1]),
                    textContent: (p[0] == '0' ? 'Все продукты' : 'Продукт #' + p[0])
                }));
            });
            btFilter.appendChild(ce('div', {
                className: 'ui_rmenu_slider _ui_rmenu_slider'
            }));
        }, 100);

        console.log('BtUtils', 'reporters ok', query.product);
    };

    function btSaveReportsLink() {
        bt_links = geByClass('bt_report_title_link').map(function getLink(x) {
            return x.href;
        });
        console.log('BtUtils', 'bt_links saved');
    }
    on.reports = btSaveReportsLink;
    on.my = btSaveReportsLink;

    on.reporter = function btOnReporter(q) {
        geByClass1('bt_reporter_block').appendChild(
            qce('div#btu_counters.counts_module', [
                createCounter('актуально', {
                    mid: q.id
                }),
                createCounter('отклонено', {
                    mid: q.id,
                    status: 3
                }),
                createCounter('исправлено', {
                    mid: q.id,
                    status: 2
                }),
                createCounter('выше среднего', {
                    mid: q.id,
                    status: 100,
                    severity: '4,3'
                }),
                createCounter('низких', {
                    mid: q.id,
                    status: 100,
                    severity: '1'
                })
            ]));
        stManager.add('module.css');
    };

    on.add = function btOnAdd() {
        var descr = ge('bt_form_descr');
        descr.value = localStorage.blank_report || blank_report;
        descr.style.height = descr.scrollHeight + 'px';
    };

    on.show = function btOnShow() {
        var BtUtilsBlock, nextHref, i;
        geByClass('page_doc_icon6').forEach(function attachVideoIconClick(video_doc) {
            video_doc.onclick = showBtVideoPlayer.bind(video_doc, video_doc.href);
        });

        ge('bt_comment_form_submit').parentNode.insertBefore(ce('div', {
            className: 'flat_button secondary',
            textContent: 'Правила',
            onclick: function onRulesClick() {
                var productDescriptionLink = document.querySelector('.bt_report_one_info_row a').href;
                var el = ge('bt_comment_form_text');
                el.value = (localStorage.btRules || default_rules).replace(/%productDescriptionLink%/gi, productDescriptionLink);
                el.style.height = el.scrollHeight + 'px';
            }
        }, {
            marginRight: '6px'
        }), ge('bt_comment_form_submit'));

        if (!bt_links.length) return;
        i = bt_links.indexOf(window.location.href);
        if (i == -1 || bt_links.length < i) return;
        BtUtilsBlock = drawBtUtilsBlock('Разное');
        nextHref = ce('a', {
            className: 'flat_button secondary button_light button_wide',
            href: bt_links[i + 1],
            textContent: 'Следующий отчет'
        });
        BtUtilsBlock.appendChild(nextHref);
    };

    function loaded(act, query) {
        if (!act) act = 'reports';
        if (!Object.prototype.hasOwnProperty.call(on, act)) return;
        on[act](query);
    }

    function replaceFunction(org, editor) {
        return function fakeFunction() {
            var args = editor.apply(this, arguments);
            return org.apply(this, args || arguments);
        };
    }

    ajax.post = replaceFunction(ajax.post, function fakeAjaxPost(url, query, options) {
        if (url !== 'al_bugtracker.php' || !query || !options) return;
        options.onDone = replaceFunction(options.onDone, function fakeOnDone() {
            setTimeout(loaded.bind(this, query.act, query), 100);
        });
        return [url, query, options];
    });

    (function onLoaded() {
        var query;
        if (window.location.pathname !== '/bugtracker') return;
        query = q2ajx(window.location.search.substr(1));
        loaded(query.act, query);
    })();


    document.head.appendChild(ce('style', {
        innerHTML: '.bt_hide_products:hover .bt_hide_products_placeholder{font-size:0}' +
            '#btu_counters{margin: 20px -20px -20px -20px}' +
            '.bt_hide_products:hover .bt_hide_products_placeholder:after{font-size:13px; content: attr(title)}' +
            '.bt_video{position: fixed; top: 0; left: 0; background: rgba(0,0,0,.8);width: 100%; height: 100%; z-index: 1000;}' +
            '.bt_video video{position: absolute; max-height: 100%;max-width: 100%;top: 0;left: 0;bottom: 0;right: 0;margin: auto;}'
    }));
}

(function injectScript() {
    var script = document.createElement('script');
    script.appendChild(document.createTextNode('(' + BtUtils + ')();'));
    (document.body || document.head || document.documentElement).appendChild(script);
})();