您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Greasy Fork is available in English.
Better visualization of upcoming Bunpro Reviews
当前为
// ==UserScript== // @name bunpro-planner // @namespace http://patrickayoup.com/ // @version 1.0.5 // @description Better visualization of upcoming Bunpro Reviews // @author Patrick Ayoup // @include *bunpro.jp/* // @exclude *community.bunpro.jp* // @grant none // @require https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.22.2/moment.min.js // @require https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.2/Chart.min.js // @require https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.min.js // @require http://greasyfork.icu/scripts/5392-waitforkeyelements/code/WaitForKeyElements.js?version=115012 // ==/UserScript== const bunproPlanner = { toAbsoluteTime: function (relativeTime) { let absoluteTime = null; relativeTime = relativeTime.toLowerCase(); if (relativeTime === 'now') { absoluteTime = moment(); } else { let tokens = relativeTime.split(' '); // When bunpro "burns" items, it uses the string // "almost 20 years", we want to get the last 2 tokens // which will cover both cases. let value = tokens[tokens.length - 2]; let unit = tokens[tokens.length - 1]; absoluteTime = moment().add(parseInt(value), unit); } return absoluteTime.format(); }, getReviews: function () { const self = this; let cards = jQuery('div .srs-info .row'); const reviews = cards.map(function () { let kids = jQuery(this).children(); let rv = { timesCorrect: parseInt(jQuery(kids[0]).text().trim().replace("Times Correct: ", "")), nextReview: self.toAbsoluteTime( jQuery(kids[1]).text().trim().replace("Next Review: ", "").replace("about ", "") ) }; return rv; }); return reviews.get(); }, insertChartNode: function () { let showUpcomingGrammarButton = jQuery('div.show-upcoming-grammar').first(); let container = jQuery('<div id="bunpro-planner-chart-container"></div>'); let chart = jQuery('<canvas id="bunpro-planner-chart"></canvas>'); container.append(chart); container.insertBefore(showUpcomingGrammarButton); return chart; }, getCurrentHour: function () { return moment().hour(); }, generateChartLabels: function () { let thisHour = this.getCurrentHour(); let hours = []; for (i = thisHour; i < thisHour + 24; ++i) { hours.push(i % 24); } return hours; }, getReviewsPerHour: function (reviews, labels) { let now = moment(); // Filter for cards scheduled in the next day. let filteredReviews = reviews.filter(function (card) { return moment(card.nextReview).diff(now, 'hours') < 23; }); let groups = _.groupBy(filteredReviews, function (card) { let reviewTime = moment(card.nextReview); return reviewTime.hour(); }); return labels.map(function(hour) { if (groups[hour]) { return groups[hour].length; } else { return 0; } }); }, getAccumulatedReviewsPerHour: function (reviewsPerHour) { const accumulatedReviews = []; for (i = 0; i < reviewsPerHour.length; ++i) { let sum = 0; for (j = 0; j <= i; ++j) { sum += reviewsPerHour[j]; } accumulatedReviews.push(sum); } return accumulatedReviews; }, buildChart: function (chart, reviews) { var ctx = chart.get(0).getContext('2d'); let labels = this.generateChartLabels(); const reviewsPerHour = this.getReviewsPerHour(reviews, labels); const accumulatedReviewsPerHour = this.getAccumulatedReviewsPerHour(reviewsPerHour); new Chart(ctx, { type: 'bar', data: { labels: labels, datasets: [ { label: 'Reviews', data: reviewsPerHour, backgroundColor: 'rgba(255, 99, 132, 0.2)', borderColor: 'rgba(255,99,132,1)', borderWidth: 1 }, { label: 'Accumulated Reviews', data: accumulatedReviewsPerHour, type: 'line' } ] }, options: { scales: { yAxes: [{ ticks: { beginAtZero:true } }] }, legend: { display: false } } }); } }; waitForKeyElements('div.show-upcoming-grammar', function() { let reviews = bunproPlanner.getReviews(); let chart = bunproPlanner.insertChartNode(); bunproPlanner.buildChart(chart, reviews); });