bgm-wiki-rev-diff

show diff between bgm.tv wiki versions

当前为 2021-11-10 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name bgm-wiki-rev-diff
  3. // @name:zh 显示条目信息版本差异
  4. // @namespace https://trim21.me/
  5. // @version 0.1.9
  6. // @author Trim21 <i@trim21.me>
  7. // @source https://github.com/Trim21/bgm-wiki-rev-diff
  8. // @supportURL https://github.com/Trim21/bgm-wiki-rev-diff/issues
  9. // @license MIT
  10. // @match https://bgm.tv/subject/*/edit*
  11. // @match https://bangumi.tv/subject/*/edit*
  12. // @match https://chii.in/subject/*/edit*
  13. // @require https://cdn.jsdelivr.net/npm/jquery@3.6.0/dist/jquery.min.js
  14. // @require https://cdn.jsdelivr.net/npm/diff2html@3.4.12/bundles/js/diff2html.min.js
  15. // @require https://cdn.jsdelivr.net/npm/diff@5.0.0/dist/diff.min.js
  16. // @require https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.min.js
  17. // @resource diff2html https://cdn.jsdelivr.net/npm/diff2html@3.4.12/bundles/css/diff2html.min.css
  18. // @grant GM.getResourceUrl
  19. // @run-at document-end
  20. // @description show diff between bgm.tv wiki versions
  21. // ==/UserScript==
  22.  
  23.  
  24. /******/ (() => { // webpackBootstrap
  25. /******/ "use strict";
  26. var __webpack_exports__ = {};
  27.  
  28. ;// CONCATENATED MODULE: external "$"
  29. const external_$_namespaceObject = $;
  30. ;// CONCATENATED MODULE: external "_"
  31. const external_namespaceObject = _;
  32. ;// CONCATENATED MODULE: ./src/parser.ts
  33.  
  34. function parseRevDetails(html) {
  35. var _jq$find$val, _jq$find$val2, _jq$find$val3;
  36.  
  37. const jq = external_$_namespaceObject(html);
  38. const rawInfo = ((_jq$find$val = jq.find('#subject_infobox').val()) === null || _jq$find$val === void 0 ? void 0 : _jq$find$val.toString()) ?? '';
  39. const title = ((_jq$find$val2 = jq.find('input[name="subject_title"]').val()) === null || _jq$find$val2 === void 0 ? void 0 : _jq$find$val2.toString()) ?? '';
  40. const description = ((_jq$find$val3 = jq.find('textarea#subject_summary').val()) === null || _jq$find$val3 === void 0 ? void 0 : _jq$find$val3.toString()) ?? '';
  41. return {
  42. title,
  43. rawInfo,
  44. description
  45. };
  46. }
  47. function parseRevEl(el) {
  48. const date = el.find('a:not(.compare-previous-trim21-cn)').first().html();
  49. const revEL = el.find('a.l:contains("恢复")');
  50. const revCommentEl = el.find('span.comment');
  51. let comment = '';
  52.  
  53. if (revCommentEl.length > 0) {
  54. comment = revCommentEl.html();
  55. comment = comment.substring(1, comment.length - 1);
  56. }
  57.  
  58. const revHref = revEL.attr('href');
  59.  
  60. if (!revHref) {
  61. // this is a merge commit, can't know what's really info
  62. return undefined;
  63. }
  64.  
  65. const revID = revHref.split('/').pop();
  66.  
  67. if (!revID) {
  68. throw new Error(`can't parse rev id from ${revHref}`);
  69. }
  70.  
  71. return {
  72. id: revID,
  73. comment,
  74. date,
  75. url: revHref
  76. };
  77. }
  78.  
  79. function getRevs() {
  80. const revs = [];
  81. external_$_namespaceObject('#pagehistory li').each(function () {
  82. const rev = parseRevEl(external_$_namespaceObject(this));
  83.  
  84. if (rev) {
  85. revs.push(rev);
  86. }
  87. });
  88. return revs;
  89. }
  90.  
  91. function getRevInfo(revID) {
  92. for (const rev of getRevs()) {
  93. if (rev.id === revID) {
  94. return rev;
  95. }
  96. }
  97. }
  98. ;// CONCATENATED MODULE: external "Diff"
  99. const external_Diff_namespaceObject = Diff;
  100. ;// CONCATENATED MODULE: ./src/differ.ts
  101.  
  102. function diff(revOld, revNew) {
  103. return [titleDiff(revOld, revNew), infoDiff(revOld, revNew), descriptionDiff(revOld, revNew)].join('\n');
  104. }
  105.  
  106. function titleDiff(rev1, rev2) {
  107. if (rev1.details.title === rev2.details.title) {
  108. return '';
  109. }
  110.  
  111. return external_Diff_namespaceObject.createPatch('条目名', rev1.details.title, rev2.details.title, rev1.rev.date, rev2.rev.date);
  112. }
  113.  
  114. function infoDiff(rev1, rev2) {
  115. if (rev1.details.rawInfo === rev2.details.rawInfo) {
  116. return '';
  117. }
  118.  
  119. return external_Diff_namespaceObject.createPatch('相关信息', rev1.details.rawInfo, rev2.details.rawInfo, rev1.rev.date, rev2.rev.date);
  120. }
  121.  
  122. function descriptionDiff(rev1, rev2) {
  123. if (rev1.details.description === rev2.details.description) {
  124. return '';
  125. }
  126.  
  127. return external_Diff_namespaceObject.createPatch('简介', rev1.details.description, rev2.details.description, rev1.rev.date, rev2.rev.date);
  128. }
  129. ;// CONCATENATED MODULE: external "Diff2Html"
  130. const external_Diff2Html_namespaceObject = Diff2Html;
  131. ;// CONCATENATED MODULE: ./src/ui.ts
  132.  
  133.  
  134. function render(diff) {
  135. return external_Diff2Html_namespaceObject.html(diff);
  136. }
  137. function show(html) {
  138. external_$_namespaceObject('#show-trim21-cn').html(html);
  139. }
  140. function clear() {
  141. external_$_namespaceObject('#show-trim21-cn').html('');
  142. }
  143. ;// CONCATENATED MODULE: ./src/model.ts
  144. class Commit {
  145. constructor(rev, detail) {
  146. this.rev = rev;
  147. this.details = detail;
  148. }
  149.  
  150. }
  151. ;// CONCATENATED MODULE: ./src/compare.ts
  152.  
  153.  
  154.  
  155.  
  156. function compare(revID1, revID2) {
  157. clear();
  158. show('<h2>loading versions...</h2>');
  159. const rev1 = getRevInfo(revID1);
  160. const rev2 = getRevInfo(revID2);
  161.  
  162. if (!rev1) {
  163. throw new Error(`error finding ${revID1}`);
  164. }
  165.  
  166. const ps = [fetchRev(rev1), fetchRev(rev2)];
  167. Promise.all(ps).then(values => {
  168. const d = diff(values[1], values[0]);
  169. const rendered = render(d);
  170. return show(rendered);
  171. }).catch(e => {
  172. console.log(e);
  173. show('<h2 style="color:red">loading versions error</h2>');
  174. }).finally(() => {
  175. var _document$getElementB;
  176.  
  177. (_document$getElementB = document.getElementById('show-trim21-cn')) === null || _document$getElementB === void 0 ? void 0 : _document$getElementB.scrollIntoView({
  178. behavior: 'smooth'
  179. });
  180. });
  181. }
  182. const _cache = {};
  183.  
  184. async function fetchRev(rev) {
  185. if (!rev) {
  186. return new Commit({
  187. id: '0',
  188. comment: '',
  189. date: '',
  190. url: ''
  191. }, {
  192. title: '',
  193. rawInfo: '',
  194. description: ''
  195. });
  196. }
  197.  
  198. if (!_cache[rev.id]) {
  199. const res = await fetch(rev.url);
  200. _cache[rev.id] = new Commit(rev, parseRevDetails(await res.text()));
  201. }
  202.  
  203. return _cache[rev.id];
  204. }
  205. ;// CONCATENATED MODULE: ./src/index.ts
  206.  
  207.  
  208.  
  209.  
  210.  
  211. async function main() {
  212. console.log('start bgm-wiki-rev-diff UserScript');
  213. await initUI();
  214. }
  215.  
  216. const style = `
  217. <style>
  218. #show-trim21-cn .d2h-code-line {
  219. width: calc(100% - 8em);
  220. padding-right: 0;
  221. }
  222.  
  223. #show-trim21-cn .d2h-code-line-ctn {
  224. width: calc(100% - 8em);
  225. }
  226.  
  227. #columnInSubjectA .rev-trim21-cn {
  228. margin: 0 0.2em;
  229. }
  230.  
  231. ul#pagehistory > li > * {
  232. vertical-align: middle;
  233. }
  234. </style>
  235. `;
  236.  
  237. async function initUI() {
  238. external_$_namespaceObject('#columnInSubjectA > hr.board').after(style + '<div id="show-trim21-cn"></div>');
  239. const diff2htmlStyle = await GM.getResourceUrl('diff2html');
  240. external_$_namespaceObject('head').append(`<link rel='stylesheet' type='text/css' href='${diff2htmlStyle}' />`);
  241. const s = external_$_namespaceObject('#pagehistory li');
  242. const revs = Array.from(s).map(function (e) {
  243. var _parseRevEl;
  244.  
  245. return (_parseRevEl = parseRevEl(external_$_namespaceObject(e))) === null || _parseRevEl === void 0 ? void 0 : _parseRevEl.id;
  246. });
  247. s.each(function (index) {
  248. const el = external_$_namespaceObject(this);
  249. const id = revs[index];
  250.  
  251. if (!id) {
  252. el.prepend('<span style="padding-right: 1.4em"> 无法参与比较 </span>');
  253. return;
  254. }
  255.  
  256. el.prepend(`<input type='radio' class='rev-trim21-cn' name='rev-right' label='select to compare' value='${id}'>`);
  257. el.prepend(`<input type='radio' class='rev-trim21-cn' name='rev-left' label='select to compare' value='${id}'>`);
  258. const previous = external_namespaceObject.find(revs, Boolean, index + 1) ?? '';
  259. el.prepend(`(<a href='#' data-rev='${id}' data-previous='${previous}' class='l compare-previous-trim21-cn'>显示修改</a>) `);
  260. });
  261. const typeRevert = {
  262. 'rev-left': 'rev-right',
  263. 'rev-right': 'rev-left'
  264. };
  265. external_$_namespaceObject('input[type="radio"]').on('change', function (e) {
  266. const name = e.target.getAttribute('name');
  267.  
  268. if (!name) {
  269. return;
  270. }
  271.  
  272. const selectName = typeRevert[name];
  273. const rev = e.target.getAttribute('value');
  274.  
  275. if (rev) {
  276. external_$_namespaceObject(`input[name="${selectName}"][value="${rev}"]`).css('visibility', 'hidden');
  277. external_$_namespaceObject(`input[name="${selectName}"][value!="${rev}"]`).css('visibility', 'visible');
  278. }
  279. });
  280. external_$_namespaceObject('.compare-previous-trim21-cn').on('click', function () {
  281. const el = external_$_namespaceObject(this);
  282. const left = String(el.data('rev'));
  283. const right = String(el.data('previous'));
  284. compare(left, right);
  285. external_$_namespaceObject(`input[name="rev-left"][value="${left}"]`).prop('checked', true);
  286. external_$_namespaceObject(`input[name="rev-left"][value!="${left}"]`).prop('checked', null);
  287. external_$_namespaceObject(`input[name="rev-right"][value="${left}"]`).css('visibility', 'hidden');
  288. external_$_namespaceObject(`input[name="rev-right"][value!="${left}"]`).css('visibility', 'visible');
  289. external_$_namespaceObject('input[name="rev-left"]').css('visibility', 'visible');
  290. external_$_namespaceObject('input[name="rev-right"]').prop('checked', null);
  291.  
  292. if (right) {
  293. external_$_namespaceObject(`input[name="rev-right"][value="${right}"]`).prop('checked', true);
  294. external_$_namespaceObject(`input[name="rev-left"][value="${right}"]`).css('visibility', 'hidden');
  295. }
  296. });
  297. external_$_namespaceObject('#columnInSubjectA span.text').append('<a href="#" id="compare-trim21-cn" class="l"> > 比较选中的版本</a>');
  298. external_$_namespaceObject('#compare-trim21-cn').on('click', function () {
  299. const selectedRevs = getSelectedVersion();
  300. compare(selectedRevs[0], selectedRevs[1]);
  301. });
  302. }
  303.  
  304. function getSelectedVersion() {
  305. const selectedVersion = [];
  306. const selectedRev = external_$_namespaceObject('.rev-trim21-cn:checked');
  307.  
  308. if (selectedRev.length < 2) {
  309. window.alert('请选中两个版本进行比较');
  310. throw new Error();
  311. }
  312.  
  313. selectedRev.each(function () {
  314. const val = external_$_namespaceObject(this).val();
  315. selectedVersion.push(val);
  316. });
  317. selectedVersion.sort((a, b) => parseInt(b) - parseInt(a));
  318. return selectedVersion;
  319. }
  320.  
  321. main().catch(console.error);
  322. /******/ })()
  323. ;