您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
restores legacy "subway" style tag chart on new last.fm report pages
当前为
// ==UserScript== // @name Last.fm Original Tag Chart // @namespace http://thlayli.detrave.net // @require https://cdnjs.cloudflare.com/ajax/libs/d3/4.13.0/d3.min.js // @require http://code.jquery.com/jquery-1.9.0.min.js // @require https://gf.qytechs.cn/scripts/383527-wait-for-key-elements/code/Wait_for_key_elements.js?version=701631 // @require https://cdn.jsdelivr.net/npm/[email protected]/src/jquery.address.min.js // @description restores legacy "subway" style tag chart on new last.fm report pages // @include https://www.last.fm/user/* // @version 1.3.2 // @license MIT // @grant none // ==/UserScript== String.prototype.toProperCase = function () { return this.replace(/\w\S*/g, function(txt){return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();}); }; //var ran_once = false; function main_func(){ // don't run on old style pages if($("section[data-require='stats/top-tags-v2']").length == 0){ // Create a div to put the chart into $('<div class="listening-report-row"><div class="report-box-container report-box-container--charts report-box-container--top-tags-over-time"><div class="report-box-header"><h3 class="report-box-title">Tag timeline</h3></div><div class="report-box-content legacy-tag-chart" style="text-align: center;"></div></div></div>').insertBefore($('.listening-report-row--2-wide').first()); // get the tag data from the html table d3.selection.prototype.mapNested = function (f) { var arr = d3.range(this.size()).map(function () { return []; }); this.each(function (d, i) { arr[i].push(f.call(this, d, i)); }); return arr; }; var tagData = d3.select(".js-top-tags-over-time-table").selectAll("thead,tbody").selectAll("tr").selectAll("th,td"); var vals = tagData.mapNested(function(d, i, j){ return d3.select(this).text().replace(/\n\s+/g,"") }).filter(v => (v.length > 0)); // set chart size var scale = (vals.length > 7) ? 2 : 1; // scale lines slightly less than layout var lineScale = (vals.length > 7) ? 1.5 : 1; var margin = 25*scale; var chartHeight = 350*scale; var chartWidth = (vals.length > 7) ? 2000 : 850; // override some styles $('head').append("<style>.legacy-tag-chart svg {paint-order: stroke;} .legacy-tag-chart .tick text {fill: #a7acb7; stroke: none} .legacy-tag-chart .report-box-container--top-tags-over-time { fill: none; } text.lc-tag.lc-tag-white {fill: white; stroke: black} .lc-no-dot {display: none;}</style>"); // set styles for week/month and year layouts if(vals.length > 7) $('head').append("<style>.axis-circles .tick text {fill: #313131; font-size: 64px;} .legacy-tag-chart .y-axis {font-size: 36px; font-weight: bold} .legacy-tag-chart .x-axis {font-size: 22px; font-weight: normal}</style>"); else $('head').append("<style>.axis-circles .tick text {fill: #313131; font-size: 48px;} .legacy-tag-chart .y-axis {font-size: 20px; font-weight: bold} .legacy-tag-chart .x-axis {font-size: 11px; font-weight: normal}</style>"); // scale chart if full year if(vals.length > 7) $('head').append("<style>.legacy-tag-chart svg {transform: scale(0.52) translate(-44%, -60%);} @media (max-width: 1260px) {.legacy-tag-chart svg {transform: scale(0.45) translate(-64%, -75%);}}</style>"); else $('head').append("<style>.legacy-tag-chart svg {transform: scale(1) translate(0, 0);}</style>"); var tags = vals[0].slice(1); valsByTag = d3.transpose(vals); var dates = valsByTag[0].map(v => v); dates[0] = ''; // collect top 5 tags for each week var weekData = vals.map(r => r.slice(1).map(function(element, index, array){ return {tag: tags[index], plays: element }}).sort(function(a, b){ return parseInt(b.plays) - parseInt(a.plays); }).slice(0,5)); weekData.shift(); // find first appearance of tag for dots var firstTag = [] tags.map(function(element, index, array){ var weekSummary = weekData.map(w => w.map(x => (x.tag == element) ? 1 : 0)); var weekBoolean = weekSummary.map(w => (w.indexOf(1) != -1) ? 1 : 0); firstTag[element] = weekBoolean.indexOf(1); }); // build d3 data object var d3data = []; weekData.map(function(element1, index1, array1){ element1.map(function(element2, index2, array2){ console.log(element2.tag + ' '+ firstTag[element2.tag] + ' ' + index1); d3data.push({ date: index1+1, rank: index2+1, tag: element2.tag, dot: (firstTag[element2.tag] == index1) }) })}); // svg scalers const x = d3.scaleLinear().domain([0, 6*scale]).range([margin - 40, chartWidth - margin - 60]) const y = d3.scaleLinear().domain([7, 1]).range([chartHeight - margin + 40, 10 + margin + ((scale ==2) ? 150 : 0)]) var svgContainer = d3.select(".legacy-tag-chart").append("svg") .attr("width", chartWidth) .attr("height", chartHeight); var g = svgContainer.append("svg:g"); // x axis var xaxis = d3.axisBottom(x) .ticks(vals.length-1) .tickFormat(function (d) { return dates[d]; }); svgContainer.append("g") .attr("fill","white") .attr("stroke","white") .attr("ticks",vals.length-1) .attr("class", "x-axis") .attr("stroke-width","0.5") .attr("transform", "translate("+(25*lineScale)+"," + (chartHeight - margin ) + ")") .call(xaxis); // y axis var yaxis = d3.axisLeft(y) .ticks(6) .tickFormat(function (d) { return (d < 6) ? d : ''; }); // y axis circles var yaxisc = d3.axisLeft(y) .ticks(6) .tickFormat(function (d) { return (d < 6) ? "⬤" : ''; }); // draw y axis circles svgContainer.append("g") .attr("transform","translate(" + (((scale == 2) ? 38 : 25) + margin) + ",0)") .call(yaxisc) .attr("class", "axis-circles") .append("text") .attr("y", 6) .attr("dy", 0.6*scale+"em"); // draw y axis text svgContainer.append("g") .attr("fill","white") .attr("stroke","none") .attr("class", "y-axis") .attr("transform","translate(" + (10*scale + margin) + ",0)") .call(yaxis) .attr("r", 12) .append("text") .attr("y", 6) .attr("transform","translate("+-10*scale+", 0)") .attr("dy", (0.71*scale)+"em") .style("text-anchor", "end"); // delete axis lines d3.selectAll(".domain,.tick>line").remove(); // draws circles (first tag appearance only) g.selectAll("tag-nodes") .data(d3data) .enter() .append("svg:circle") .attr("cx", function(d) { return x(d.date); }) .attr("cy", function(d) { return y(d.rank); }) .attr("r", 8*lineScale) .attr("class", function(d) { return ((d.dot) ? "" : "lc-no-dot ") + "lc-tag lc-tag-" + tags.indexOf(d.tag) + " top-tags-over-time-colour-" + tags.indexOf(d.tag) % 12}) .append("svg:title") .text(function(d){ return d.tag.toProperCase(); }); // draws lines g.selectAll("tag-lines") .data(d3data) .enter() .append('line') .attr('x1', function(d) { return x(d.date); }) .attr('y1', function(d) { return y(d.rank); }) .attr('x2', function(d) { return x(d.date) + ((scale == 2) ? 70 : 50); }) .attr('y2', function(d) { return y(d.rank); }) .attr("class", function(d) { return "lc-tag lc-tag-" + tags.indexOf(d.tag) + " top-tags-over-time-colour-" + tags.indexOf(d.tag) % 12}) .style("stroke-width", 5*lineScale) .append("svg:title") .text(function(d){ return d.tag.toProperCase(); }); // calculate links and draw curves // provide source column, source row, destination row function curveMaker(xsrc,xdest,ysrc,ydest){ return [{ x: x(xsrc)+((scale == 2) ? 69 : 49), y: y(ysrc)}, { x: x(xsrc)+((scale == 2) ? 99 : 79), y: y(ysrc)}, { x: x(xdest)-29, y: y(ydest)}, { x: x(xdest)+1, y: y(ydest)}]; } var curve = d3.line(i) .x((d) => d.x) .y((d) => d.y) .curve(d3.curveBasis); // iterate over tags jQuery.each(d3data, function(i,d) { // check to see if tag appears again var nextWeekToAppear = 0; for(n=i+1; n<d3data.length; n++){ if(d3data[n].tag == d.tag){ nextWeekToAppear = d3data[n].date; nextRow = d3data[n].rank; break; } } // draw curves to tags that appear in the next week if(nextWeekToAppear == d.date+1){ // check for the tag in the next date set if(d.date < vals.length-1 && weekData[d.date]){ var weeklyTags = weekData[d.date].map(r => r.tag); var row = (weeklyTags.indexOf(d.tag) < 0) ? 6 : weeklyTags.indexOf(d.tag) + 1; svgContainer.select("g") .append("path") .attr("d", curve(curveMaker(d.date,d.date+1,d.rank,row))) .attr("fill", "none") .attr("stroke", "white") .style("stroke-width", 5*lineScale) .attr("class", function(e) { return "lc-tag lc-tag-" + tags.indexOf(d.tag) + " report-box-container--top-tags-over-time top-tags-over-time-colour-" + tags.indexOf(d.tag) % 12}); } } // draw a curve to row 6 for artists who don'tappear next week if(nextWeekToAppear > d.date+1){ svgContainer.select("g") .append("path") .attr("d", curve(curveMaker(d.date,d.date+1,d.rank,6))) .attr("fill", "none") .attr("opacity", 0.5) .attr("stroke", "white") .style("stroke-width", 5*lineScale) .attr("class", function(e) { return "lc-dim lc-tag lc-tag-" + tags.indexOf(d.tag) + " report-box-container--top-tags-over-time top-tags-over-time-colour-" + tags.indexOf(d.tag) % 12}) .append("svg:title") .text(function(e){ return d.tag.toProperCase(); }); } if(nextWeekToAppear > 2 && 1 < d.date < 5 && d.date < nextWeekToAppear-1){ // draw a line on row 6 from the next week to nextWeekToAppear-1 var scaleDifference = -0.07; svgContainer.select("g") .append("path") .attr("d", curve(curveMaker(parseInt(d.date)+0.63+((scale==2) ? scaleDifference : 0), parseInt(nextWeekToAppear)-0.63-((scale==2) ? scaleDifference : 0), 6, 6))) .attr("fill", "none") .attr("stroke", "white") .attr("opacity", 0.5) .style("stroke-width", 5*lineScale) .attr("class", function(e) { return "lc-dim lc-tag lc-tag-" + tags.indexOf(d.tag) + " report-box-container--top-tags-over-time top-tags-over-time-colour-" + tags.indexOf(d.tag) % 12}) .append("svg:title") .text(function(e){ return d.tag.toProperCase(); }); } // add curves (behind) from row 6 for returning tags if(d.date > 2){ var lastWeek = weekData[d.date-2].map(r => r.tag); var found = lastWeek.indexOf(d.tag); //if(found > -1) // else // // if no dot and not in last week, draw curve from row 6 if(d.dot == false && found == -1){ svgContainer.select("g") .append("path") .attr("d", curve(curveMaker(d.date-1,d.date,6,d.rank))) .attr("fill", "none") .attr("stroke", "white") .attr("opacity", 0.5) .style("stroke-width", 5*lineScale) .attr("class", function(e) { return "lc-dim lc-tag lc-tag-" + tags.indexOf(d.tag) + " report-box-container--top-tags-over-time top-tags-over-time-colour-" + tags.indexOf(d.tag) % 12}) .append("svg:title") .text(function(e){ return d.tag.toProperCase(); }); } } }); // add tag labels (first time only) g.selectAll("legacy-text") .data(d3data) .enter() .append('text') .attr('x', function(d) { return x(d.date); }) .attr('y', function(d) { return y(d.rank) - 20*lineScale; }) .attr("fill", "white") .attr("stroke","black") .attr("font-size", ((scale == 2) ? 20 : 12)) .attr("font-family", "sans-serif") .attr("class", function(d) { return ((d.dot) ? "" : "lc-no-dot ") + "lc-tag lc-tag-white lc-tag-" + tags.indexOf(d.tag) + " report-box-container--top-tags-over-time top-tags-over-time-colour-" + tags.indexOf(d.tag) % 12}) .attr("text-anchor", "middle") .text((d) => (d.tag.length > 11) ? d.tag.slice(0,11).toProperCase() + "..." : d.tag.toProperCase()); // set up mouseover event listeners tags.forEach(function(t) { g.selectAll(".lc-tag-"+tags.indexOf(t)) .on('mouseover', function (d, i) { // dim all tags d3.selectAll(".lc-tag").transition().duration('100').attr('opacity', '.25'); // undim selected d3.selectAll(".lc-tag-"+tags.indexOf(t)).transition().duration('100').attr('opacity', '1'); // dim selected lc-dim tags to 50% d3.selectAll(".lc-tag-"+tags.indexOf(t)).filter(function() { return this.classList.contains('lc-dim') }).transition().duration('100').attr('opacity', '0.5'); }) .on('mouseout', function (d, i) { setTimeout(() => { // undim all tags d3.selectAll(".lc-tag").transition().duration('100').attr('opacity', '1'); // dim lc-dim tags to 50% d3.selectAll(".lc-dim").transition().duration('100').attr('opacity', '0.5'); }, "50"); }); }); } } function repair_func(){ // old style page - fix duplicate label glitch setTimeout(() => { var extraLabels = d3.selectAll(".highcharts-label text").filter(function() { return d3.select(this).attr("x") == 0 }); extraLabels.remove(); }, "2000"); } var fireOnHashChangesToo = true; var pageURLCheckTimer = setInterval ( function () { if ( this.lastPathStr !== location.pathname || this.lastQueryStr !== location.search || (fireOnHashChangesToo && this.lastHashStr !== location.hash) ) { this.lastPathStr = location.pathname; this.lastQueryStr = location.search; this.lastHashStr = location.hash; //var ran_once = false; waitForKeyElements(".js-top-tags-over-time-target > .highcharts-container", main_func); } } , 111 ); waitForKeyElements(".js-top-tags-over-time-target > .highcharts-container", main_func); waitForKeyElements(".tube-tags-graph svg.highcharts-root", repair_func);
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址