Greasy Fork is available in English.
Lib для графа комментариев Shikimori
当前为 
        此脚本不应直接安装。它是供其他脚本使用的外部库,要使用该库请加入元指令 // @require https://update.greasyfork.icu/scripts/552715/1678737/shikiCommentsGraphLib.js
      
// ==UserScript== // @name shikiCommentsGraphLib // @namespace https://shikimori.one/ // @version 3.0 // @description Lib для графа комментариев Shikimori // @license MIT // @require https://cdn.jsdelivr.net/npm/[email protected]/lodash.min.js // @require https://cdn.jsdelivr.net/npm/[email protected]/dist/axios.min.js // ==/UserScript== ; (function () { 'use strict'; // === Базовая математика (оставляем из оригинала) === class ShikiMath { static is_above(x, y, x1, y1, x2, y2) { const dx = x2 - x1; const dy = y2 - y1; return ((((dy * x) - (dx * y)) + (dx * y1)) - (dy * x1)) <= 0; } static sector(x1, y1, x2, y2, rx, ry) { const lb_to_rt = this.is_above(x2, y2, x1 - rx, y1 - ry, x1, y1); const lt_to_rb = this.is_above(x2, y2, x1 - rx, y1 + ry, x1, y1); if (lb_to_rt && lt_to_rb) return 'top'; if (!lb_to_rt && lt_to_rb) return 'right'; if (!lb_to_rt && !lt_to_rb) return 'bottom'; return 'left'; } static square_cutted_line(x1, y1, x2, y2, rx1, ry1, rx2, ry2) { let f_x1, f_x2, f_y1, f_y2; const dx = x2 - x1, dy = y2 - y1; const y = x => (((dy * x) + (dx * y1)) - (dy * x1)) / dx; const x = y => (((dx * y) - (dx * y1)) + (dy * x1)) / dy; const target = this.sector(x1, y1, x2, y2, rx1, ry1); if (target === 'right') { f_x1 = x1 + rx1; f_y1 = y(f_x1); f_x2 = x2 - rx2; f_y2 = y(f_x2); } if (target === 'left') { f_x1 = x1 - rx1; f_y1 = y(f_x1); f_x2 = x2 + rx2; f_y2 = y(f_x2); } if (target === 'top') { f_y1 = y1 + ry1; f_x1 = x(f_y1); f_y2 = y2 - ry2; f_x2 = x(f_y2); } if (target === 'bottom') { f_y1 = y1 - ry1; f_x1 = x(f_y1); f_y2 = y2 + ry2; f_x2 = x(f_y2); } return { x1: f_x1, y1: f_y1, x2: f_x2, y2: f_y2 }; } } // === Класс CommentNode === const SELECT_SCALE = 1.5; const BORDER_OFFSET = 3; class CommentNode { constructor(data, size = 64) { Object.assign(this, data); this.width = this.height = size; this.rx = this.ry = size / 2; this.selected = false; } get d3Node() { return this._d3Node ||= d3.select(`.node#${this.id}`); } get d3Image() { return this._d3Image ||= this.d3Node.select('image'); } deselect(boundX, boundY, tick) { this.selected = false; this._animate(this.width, this.height, boundX, boundY, tick); this._hideTooltip(); } select(boundX, boundY, tick) { this.selected = true; this._animate(this.width * SELECT_SCALE, this.height * SELECT_SCALE, boundX, boundY, tick); this._loadTooltip(); } _animate(newW, newH, boundX, boundY, tick) { const iw = d3.interpolate(this.width, newW); const ih = d3.interpolate(this.height, newH); this.d3Node.transition().duration(300).tween('resize', () => t => { this.width = iw(t); this.height = ih(t); this.rx = this.width / 2; this.ry = this.height / 2; this.d3Node.attr('transform', `translate(${boundX(this) - this.rx}, ${boundY(this) - this.ry})`); this.d3Image.attr({ width: this.width, height: this.height }); tick(); }); } async _loadTooltip() { try { const tooltip = document.querySelector('.sticky-tooltip'); tooltip.style.display = 'block'; tooltip.querySelector('.inner').innerHTML = '<i>Загрузка...</i>'; const { data } = await axios.get(`https://shikimori.one/api/comments/${this.id}`); const html = data.html_body || '(нет данных)'; tooltip.querySelector('.inner').innerHTML = html; } catch (e) { console.error('Tooltip error:', e); } } _hideTooltip() { const tooltip = document.querySelector('.sticky-tooltip'); if (tooltip) tooltip.style.display = 'none'; } } // === Граф комментариев === class CommentsGraph extends window.FranchiseGraph { constructor(data) { super(data); this.image_w = this.image_h = 64; this.nodes_data = data.nodes.map(node => new CommentNode(node, 64)); } _append_nodes() { this.d3_node = this.d3_svg.append('g').selectAll('.node') .data(this.nodes_data) .enter().append('g') .attr({ class: 'node', id: d => d.id }) .call(this.d3_force.drag()) .on('click', d => { if (d3.event?.defaultPrevented) return; this._node_selected(d); }); this.d3_node.append('path').attr({ class: 'border_outer', d: '' }); this.d3_image_container = this.d3_node.append('g').attr({ class: 'image-container' }); this.d3_image_container.append('image') .attr({ width: d => d.width, height: d => d.height, 'xlink:href': d => d.avatar }); this.d3_image_container.append('path') .attr({ class: 'border_inner', d: d => `M 0,0 ${d.width},0 ${d.width},${d.height} 0,${d.height} 0,0` }); } } // === Экспорт === window.ShikiMath = ShikiMath; window.CommentNode = CommentNode; window.CommentsGraph = CommentsGraph; })();