Bangumi-History-Diff

compare two versions of subject history

目前为 2015-08-20 提交的版本。查看 最新版本

// ==UserScript==
// @name        Bangumi-History-Diff
// @namespace   BHD
// @include     /https?:\/\/(bgm|bangumi|chii)\.(tv|in)\/subject/\d+\/edit/
// @include     /https?:\/\/(bgm|bangumi|chii)\.(tv|in)\/subject/\d+\/edit_detail/diff\/\d+\.\.\.\d+/
// @version     0.0.2
// @grant       none
// @require     https://code.jquery.com/jquery-2.1.1.min.js
// @description compare two versions of subject history
// ==/UserScript==

var domain, func, uri, params = {};
var version1, version2;

//----------------------------------------
//---- Edit ------------------------------
//----------------------------------------
editController = function() {
  $('<div id="diff-launcher"><input type="number" name="diff-left" class="inputtext" placeholder="左对比(通常是老版本)"><input type="number" name="diff-right" class="inputtext" placeholder="右对比(通常是新版本)"><input type="button" class="inputBtn" value="对比" name="diff-launch"></div>').insertBefore($('#pagehistory'));

  $('#pagehistory li a').each(function() {
    var hrefMatch = $(this).attr('href').match(/undo\/(\d+)/);
    if(hrefMatch == null) return;
    var version = hrefMatch[1];
  
    $('<span>| <a href="#" class="l diff-add2left" data-version="' + version + '">加到左边对比</a> | <a href="#" class="l diff-add2right" data-version="' + version + '">加到右边对比</a></span>').insertAfter(this);
  });
  
  //binding events
  $('input[name="diff-launch"]').click(function() {
    window.location.href = '/subject/' + params.subject + '/edit_detail/diff/' + $('input[name="diff-left"]').val() + '...' + $('input[name="diff-right"').val();
  });
  
  $('.diff-add2left').click(function() {
    $('input[name="diff-left"]').val($(this).attr('data-version'));
  });

  $('.diff-add2right').click(function() {
    $('input[name="diff-right"]').val($(this).attr('data-version'));
  });
}

//----------------------------------------
//---- Diff ------------------------------
//----------------------------------------

diffController = function() {
//Request the first version and load insert it into document.
$.get('/subject/' + params.subject + '/edit_detail/undo/' + params.ver1, function(data) {
  $('body').html(data);
  
  //Change the links in navigation bar.
  $('.navSubTabs .focus').removeClass('focus');
  $('.navSubTabs').append('<li><a class="focus" href="#">对比</a></li>');
  
  //Get all infomations we need.
  var info = {ver1: {}, ver2: {}};
  //条目标题
  info.ver1.title = $('input[name="subject_title"]').val();
  //Infobox
  info.ver1.infobox = $('#subject_infobox').val();
  //简介
  info.ver1.summary = $('#subject_summary').val();
  
  //Clean workspace
  $('#columnInSubjectA').html('<h2>/ 正在对比版本 <a href="/subject/' + params.subject + '/edit_detail/undo/' + params.ver1 + '">' + params.ver1 + '</a> 与 <a href="/subject/' + params.subject + '/edit_detail/undo/' + params.ver2 + '">' + params.ver2 + '</a>' +
                              '    <a class="chiiBtn" href="/subject/' + params.subject + '/edit_detail/diff/' + params.ver2 + '...' + params.ver1 + '"><span>交换方向</span></a></small></h2>' +
                              '<div id="diff-workspace">' +
                              '<style>#diff-workspace h1 { margin: 6px; padding: 3px; font-size: 18px; border-bottom: 1px solid #777; } .diff-added { background: #e9efe9; } .diff-deleted { background: #feebeb; } .diff-modified { background: #faffd9; } </style>' +
                              '<h1>标题</h1> <table id="diff-title" class="settings" width="100%" cellpadding="5"><thead><tr><td>版本 #' + params.ver1 + '</td><td>版本 #' + params.ver2 + '</td></tr></thead><tbody></tbody></table>' + 
                              '<h1>Infobox</h1> <table id="diff-infobox" class="settings" width="100%" cellpadding="5"><thead><tr><td></td><td>版本 #' + params.ver1 + '</td><td>版本 #' + params.ver2 + '</td></tr></thead><tbody></tbody></table>' + 
                              '<h1>简介</h1> <table id="diff-summary" class="settings" width="100%" cellpadding="5"><thead><tr><td>版本 #' + params.ver1 + '</td><td>版本 #' + params.ver2 + '</td></tr></thead><tbody></tbody></table>' +
                              '</div>');

  //Loading another version and then diff them.
  $.get('/subject/' + params.subject + '/edit_detail/undo/' + params.ver2, function(data) {
    //Remove the input box, and add the table element for diff view.
    //[\S\s]* => See https://stackoverflow.com/questions/26929891/regex-to-match-a-multi-line-string
    info.ver2.title = data.match(/subject_title" class="inputtext" type="text" value="(.+?)" \/>/)[1];
    info.ver2.infobox = data.match(/subject_infobox"[^>]+>([\S\s]*?)<\/textarea>/m)[1];
    info.ver2.summary = data.match(/subject_summary"[^>]+>([\S\s]*?)<\/textarea>/m)[1];

    //Diff - Title
    $('#diff-title tbody').append('<tr><td>' + info.ver1.title + '</td><td>' + info.ver2.title + '</td></tr>');

    //Diff - Infobox
    var infobox = {ver1: {}, ver2: {}};
    infobox.ver1 = info.ver1.infobox.split(/\n/);
    infobox.ver2 = info.ver2.infobox.split(/\n/);

    infoboxCompare = compare(infobox.ver1, infobox.ver2);

    for(i in infoboxCompare) {
      switch(infoboxCompare[i].act) {
        case 'match':
          $('#diff-infobox tbody').append('<tr class="diff-match"><th></th><td>' + infoboxCompare[i].left + '</td><td>' + infoboxCompare[i].right + '</td></tr>');
          break;
        case 'added':
          $('#diff-infobox tbody').append('<tr class="diff-added"><th>++</th><td></td><td>' + infoboxCompare[i].right + '</td></tr>');
          break;
        case 'deleted':
          $('#diff-infobox tbody').append('<tr class="diff-deleted"><th>--</th><td>' + infoboxCompare[i].left + '</td><td></td></tr>');
          break;
        case 'modified':
          $('#diff-infobox tbody').append('<tr class="diff-modified"><th>MM</th><td>' + infoboxCompare[i].left + '</td><td>' + infoboxCompare[i].right + '</td></tr>');
          break;
      }
    }
    
    //Diff - Summary
    $('#diff-summary tbody').append('<tr><td>' + info.ver1.summary + '</td><td>' + info.ver2.summary + '</td></tr>');

  }); //$.get('/subj...ver2...
}); //$.get('/subj...ver1...
}
  
//----------------------------------------
//---- Functions -------------------------
//----------------------------------------
compare = function(left, right) {
  var leftPointer = 0, rightPointer = 0;
  var retval = [];

  while(leftPointer < left.length) {
    //May be they are the same...
    if(left[leftPointer] == right[rightPointer]) {
      retval.push({act: 'match', left: left[leftPointer], right: right[rightPointer]});
      leftPointer++;
      rightPointer++;
      continue;
    }

    var matchShift = 0;
    var leftShift = 0, rightShift = 0;
    var leftTitleMatch = false, rightTitleMatch = false;
      
    while((rightPointer + rightShift) < right.length) {
      //for the line start with "|", we just compare their title("|title=", "|title =")
      if((left[leftPointer].length > 0 && left[leftPointer][0] == '|') &&
         (right[rightPointer + rightShift].length > 0 && right[rightPointer + rightShift][0] == '|')) {
        var leftTitle = left[leftPointer].match(/\|(.+?)=/)[1];
        var rightTitle = right[rightPointer + rightShift].match(/\|(.+?)=/)[1];
        if(leftTitle == rightTitle) {
          rightTitleMatch = true;
          break;
        }
      } else if(left[leftPointer] == right[rightPointer + rightShift]) break;
      rightShift++;
    } //while rightShift...
    matchShift = rightShift;

    var leftShift = 0, rightShift = 0;
    while((leftShift + leftPointer) <= left.length - 1) {
      //for the line start with "|", we just compare their title("|title=", "|title =")
      if((left[leftPointer + leftShift].length > 0 && left[leftPointer + leftShift][0] == '|') &&
         (right[rightPointer].length > 0 && right[rightPointer][0] == '|')) {
        var leftTitle = left[leftPointer + leftShift].match(/\|(.+?)=/)[1];
        var rightTitle = right[rightPointer].match(/\|(.+?)=/)[1];
        if(leftTitle == rightTitle) {
          leftTitleMatch = true;
          break;
        }
      } else if(left[leftPointer + leftShift] == right[rightPointer]) break;
      leftShift++;
    } //while leftShift...
    rightShift = matchShift;

    //Comparing...
      
    //Modified
    if(((leftPointer + leftShift) >= left.length - 1) && ((rightPointer + rightShift) >= right.length - 1)) {
      retval.push({act: 'deleted', left: left[leftPointer]});
      retval.push({act: 'added', right: right[rightPointer]});
      leftPointer++;
      rightPointer++;
      continue;
    }
      
    //Delete
    if(leftShift < rightShift) {
      for(var i = 0; i < leftShift; i++) {
        retval.push({act: 'deleted', left: left[leftPointer + i]});
      }
      var leftContent = left[leftPointer + leftShift].match(/=(.+)/);
      var rightContent = right[rightPointer].match(/=(.+)/);
      retval.push({act: (leftContent == rightContent ? 'match' : 'modified'), left: left[leftPointer + leftShift], right: right[rightPointer]});
      leftPointer += leftShift + 1;
      rightPointer++;
      continue;
    }

    //Add
    if(leftShift > rightShift) {
      for(var i = 0; i < rightShift; i++) {
        retval.push({act: 'added', right: right[rightPointer + i]});
      }
      var leftContent = left[leftPointer].match(/=(.+)/);
      var rightContent = right[rightPointer + rightShift].match(/=(.+)/);
      retval.push({act: (leftContent == rightContent ? 'match' : 'modified'), left: left[leftPointer], right: right[rightPointer + rightShift]});
      leftPointer++;
      rightPointer += rightShift + 1;
      continue;
    }
    
    //Else... => the title of two lines are the same
    var leftContent = left[leftPointer].match(/=(.+)/);
    var rightContent = right[rightPointer].match(/=(.+)/);
    retval.push({act: (leftContent == rightContent ? 'match' : 'modified'), left: left[leftPointer], right: right[rightPointer]});
    leftPointer++;
    rightPointer++;
    continue;
  } //while leftPointer...
  return retval;
}


//----------------------------------------
//---- Routing & Bootstrap ---------------
//----------------------------------------

$(function() {
  var urlMatch = window.location.href.match(/\/\/(bgm|bangumi|chii).(tv|in)(\/.+)/);
  uri = urlMatch[3];

  //Domain
  switch(urlMatch[1]) {
    case 'bgm':
      domain = 'bgm.tv';
      break;
    case 'bangumi':
      domain = 'bangumi.tv';
      break;
    case 'chii':
      domain = 'chii.in';
      break;
  }
  
  //URI & Params
  switch(true) {
    case (urlMatch[3].search('diff') >= 0):
      func = 'diff';

      var matchParams = urlMatch[3].match(/\/subject\/(\d+)\/edit_detail\/diff\/(\d+)...(\d+)/);
      params = {
        subject: matchParams[1],
        ver1: matchParams[2],
        ver2: matchParams[3]
      };
      diffController();
      break;
    case (urlMatch[3].search('edit') >= 0):
      func = 'edit';

      var matchParams = urlMatch[3].match(/\/subject\/(\d+)\/edit/);
      params = {
        subject: matchParams[1]
      };
      editController();
      break;
  }
});

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址