User:Cacycle/diff.js: Difference between revisions

Content deleted Content added
1.0.11 fix ShortenOutput omitting changes/html, improve bubble up
1.0.12 (September 10, 2014) skin/sky/cloud colored theme, new inline chunk split, new custom split, IE8 compatibility mouse/click handler, fix bubbling, fix ShortenOutput (paragraphs, headings)
Line 3:
// ==UserScript==
// @name wDiff
// @version 1.0.1112
// @date September 0810, 2014
// @description improved word-based diff library with block move detection
// @homepage https://en.wikipedia.org/wiki/User:Cacycle/diff
Line 136:
if (wDiff.regExpSplit === undefined) {
wDiff.regExpSplit = {
 
// paragraphs: after newline
paragraph: new RegExp('/(.|\\n)+?(\?=(\n|$)', 'g')/g,
 
// sentences: after .spaces or before newline
sentence: new RegExp('\/\n|.*?\\.( +|(?=\\n))|.+?(?=\\n)', '/g'),
 
// inline chunks
// [[wiki link]] | {{template}} | [ext. link] |<html> | [[wiki link| | {{template| | url
chunk: /\[\[[^\[\]\n]+\]\]|\{\{[^\{\}\n]+\}\}|\[[^\[\]\n]+\]|<[^<>\[\]\{\}\n]+>|\[\[[^\[\]\|\n]+\]\]\||\{\{[^\{\}\|\n]+\||\b((https?:|)\/\/)[^\x00-\x20\s"\[\]\x7f]+/g,
 
// words, multi-char markup, and chars
word: new RegExp('([' + wDiff.letters + '])+|\\[\\[|\\]\\]|\\{\\{|\\}\\}|&\\w+;|\'\'\'|\'\'|==+|\\{\\||\\|\\}|\\|-|.', 'g'),
 
// chars
character: new RegExp('/.', '/g')
};
}
Line 203 ⟶ 207:
'.wDiffMarkRight:before { content: "▶"; }' +
'.wDiffMarkLeft:before { content: "◀"; }' +
'.wDiffDelete { font-weight: normalbold; textbackground-decoration: none; color: #fffffe49c; background-color: #c33222; border-radius: 0.25em; padding: 0.2em 1px; }' +
'.wDiffInsert { font-weight: normalbold; textbackground-decoration: none; color: #fffbbddff; background-color: #07e222; border-radius: 0.25em; padding: 0.2em 1px; }' +
'.wDiffBlockLeft { font-weight: bold; background-color: #d8d8d8e8e8e8; border-radius: 0.25em; padding: 0.25em2em 1px; margin: 0 1px; }' +
'.wDiffBlockRight { font-weight: bold; background-color: #d8d8d8e8e8e8; border-radius: 0.25em; padding: 0.25em2em 1px; margin: 0 1px; }' +
'.wDiffMarkLeft { colorfont-weight: #d8d8d8bold; background-color: #c33ffe49c; color: #666; border-radius: 0.25em; padding: 0.2em 0.2em; margin: 0 1px; }' +
'.wDiffMarkRight { colorfont-weight: #d8d8d8bold; background-color: #c33ffe49c; color: #666; border-radius: 0.25em; padding: 0.2em 0.2em; margin: 0 1px; }' +
'.wDiffFragment { white-space: pre-wrap; background: #fcfcfcfff; border: #bbb solid; border-width: 1px 1px 1px 0.5em; border-radius: 0.5em; font-family: inheritsans-serif; font-size: 88%; line-height: 1.6; box-shadow: 2px 2px 2px #ddd; padding: 1em; margin: 0; }' +
'.wDiffContainer { }' +
'.wDiffNoChange { white-space: pre-wrap; background: #f0f0f0; border: #bbb solid; border-width: 1px 1px 1px 0.5em; border-radius: 0.5em; font-family: inheritsans-serif; font-size: 88%; line-height: 1.6; box-shadow: 2px 2px 2px #ddd; padding: 0.5em; margin: 1em 0; }' +
'.wDiffSeparator { margin-bottom: 1em; }' +
'.wDiffBlock { }' +
Line 232 ⟶ 237:
'.wDiffMark7 { color: #ff9999; }' +
'.wDiffMark8 { color: #90d090; }' +
'.wDiffBlockHighlight { background-color: #333777; color: #fff; border: solid #777; border-width: 1px 0; }' +
'.wDiffMarkHighlight { background-color: #333777; color: #fff; }';
}
 
Line 263 ⟶ 268:
// dynamic replacements: {block}: block number style, {mark}: mark number style, {class}: class number, {number}: block number, {title}: title attribute (popup)
// class plus html comment are required indicators for wDiff.ShortenOutput()
if (wDiff.blockEvent === undefined) { wDiff.blockEvent = ' onmouseover="wDiff.BlockHandler(nullundefined, this, \'mouseover\');"'; }
 
if (wDiff.htmlContainerStart === undefined) { wDiff.htmlContainerStart = '<div class="wDiffContainer" style="' + wDiff.styleContainer + '">'; }
Line 299 ⟶ 304:
 
//
// javascript handler for output code, compatible with IE 8
//
 
// wDiff.BlockHandler: event handler for block and mark elements
if (wDiff.BlockHandler === undefined) { wDiff.BlockHandler = function (event, element, type) {
 
// getIE event datacompatibility
if ( (event === undefined) && (window.event !== undefined) ) {
var type;
if ( event !== null) {window.event;
element = event.currentTarget;
type = event.type;
event.stopPropagation();
}
 
Line 319 ⟶ 321:
 
// highlight corresponding mark/block pairs
if ( (type === undefined) || (type == 'mouseover') ) {
if (element.addEventListeneronmouseover !== undefined) {null;
element.addEventListeneronmouseout = function ('mouseout',event) { wDiff.BlockHandler(event, falseelement, 'mouseout'); };
element.addEventListeneronclick = function ('click',event) { wDiff.BlockHandler(event, falseelement, 'click'); };
}
else if (element.attachEvent !== undefined) {
element.attachEvent('onmouseout', wDiff.BlockHandler);
element.attachEvent('onclick', wDiff.BlockHandler);
}
else {
return;
}
block.className += ' wDiffBlockHighlight';
mark.className += ' wDiffMarkHighlight';
Line 336 ⟶ 330:
 
// remove mark/block highlighting
else if ( (type == 'mouseout') || (type == 'click') ) {
element.onmouseout = null;
element.onmouseover = function (event) { wDiff.BlockHandler(event, element, 'mouseover'); };
 
// getElementsByClassName
Line 355 ⟶ 351:
// scroll to corresponding mark/block element
if (type == 'click') {
 
// remove element click handler
if (element.removeEventListener !== undefined) {
element.removeEventListener('mouseout', wDiff.BlockHandler, false);
}
else {
element.detachEvent('onmouseout', wDiff.BlockHandler);
}
 
// get corresponding element
Line 381 ⟶ 369:
 
// scroll element under mouse cursor
var top = window.pageYOffset;
if (elementwindow.removeEventListenerpageXOffset !== undefined) {
var cursor = event.pageY;
top = window.pageXOffset;
var line = parseInt(window.getComputedStyle(corrElement).getPropertyValue('line-height'));
}
else {
document.documentElement.scrollTop;
}
 
var cursor;
if (event.pageY !== undefined) {
type cursor = event.typepageY;
}
else if (elementevent.attachEventclientY !== undefined) {
var cursor = event.pageYclientY + top;
}
 
var line = 12;
if (window.getComputedStyle !== undefined) {
var line = parseInt(window.getComputedStyle(corrElement).getPropertyValue('line-height'));
}
window.scroll(0, corrElementPos + top - cursor + line / 2);
}
Line 478 ⟶ 483:
return text.diff;
}
 
// new symbols object
var symbols = {
Line 498 ⟶ 503:
// calculate refined diff
wDiff.CalculateDiff(text, symbols, 'sentence');
 
// refine different paragraphs into chunks
wDiff.SplitRefine(text.newText, 'chunk');
wDiff.SplitRefine(text.oldText, 'chunk');
 
// calculate refined diff
wDiff.CalculateDiff(text, symbols, 'chunk');
 
// refine different sentences into words
Line 550 ⟶ 562:
var first = current;
var string = '';
 
// split full text or specified token
if (token === undefined) {
Line 561 ⟶ 573:
}
 
// split text into tokens, regExp match as separator
var number = 0;
var split = [];
var regExpMatch;
var lastIndex = 0;
while ( (regExpMatch = wDiff.regExpSplit[level].exec(string)) !== null) {
if (regExpMatch.index > lastIndex) {
split.push(string.substring(lastIndex, regExpMatch.index));
}
split.push(regExpMatch[0]);
lastIndex = wDiff.regExpSplit[level].lastIndex;
}
if (lastIndex < string.length) {
split.push(string.substring(lastIndex));
}
 
// cycle trough new tokens
for (var i = 0; i < split.length; i ++) {
 
// insert current item, link to previous
text.tokens[current] = {
token: regExpMatchsplit[0i],
prev: prev,
next: null,
Line 900 ⟶ 926:
 
// bubble down as deep as possible
var front = text.tokens[gapStart].next;
var back = text.tokens[i].next;
while (
(front !== null) && (back !== null) &&
Line 916 ⟶ 942:
// test baloon up, remember last line break or closing text
var frontStop = null;
front = text.tokens[front].prev;
back = text.tokens[back].prev;
var frontTest = front;
var backTest = back;
while (
(frontTest !== null) && (backTest !== null) &&
(text.tokens[frontTest].link !== null) && (text.tokens[backTest].link === null) &&
(text.tokens[frontTest].token == text.tokens[backTest].token)
) {
Line 926 ⟶ 954:
backTest = text.tokens[backTest].prev;
if (wDiff.regExpBubbleStop.test(text.tokens[frontTest].token) === true) {
frontStop = frontfrontTest;
break;
}
Line 1,220 ⟶ 1,248:
// recursively diff the unresolved sequence
if ( (iLength > 1) || (jLength > 1) ) {
 
// new symbols object for sub-region
var symbolsRecurse = {
Line 1,272 ⟶ 1,300:
iNext = text.newText.tokens[iNext].prev;
}
 
// determine the limits of of the unresolved old sequence
var jStart = null;
Line 1,289 ⟶ 1,317:
// recursively diff the unresolved sequence
if ( (iLength > 1) || (jLength > 1) ) {
 
// new symbols object for sub-region
var symbolsRecurse = {
Line 1,354 ⟶ 1,382:
// sort blocks by new text token number and update groups
wDiff.SortBlocks(blocks, groups);
 
// convert groups to insertions/deletions if maximal block length is too short
if (wDiff.blockMinLength > 0) {
Line 1,840 ⟶ 1,868:
var blockStart = groups[group].blockStart;
var blockEnd = groups[group].blockEnd;
 
// no block in group is at least blockMinLength words long
if (groups[group].maxWords < wDiff.blockMinLength) {
Line 1,860 ⟶ 1,888:
for (var block = blockStart; block <= blockEnd; block ++) {
if ( (block > 0) && (blocks[block - 1].type == 'del') && (blocks[block].type == 'same') ) {
 
// stop unlinking if more than one word or a unique word
if ( (blocks[block].words > 1) || ( (blocks[block].words == 1) && (blocks[block].unique === true) ) ) {
Line 1,874 ⟶ 1,902:
for (var block = blockEnd; block > blockStart; block --) {
if ( (blockEnd < blocks.length - 1) && (blocks[block + 1].type == 'del') && (blocks[block].type == 'same') ) {
 
// stop unlinking if more than one word or a unique word
if ( (blocks[block].words > 1) || ( (blocks[block].words == 1) && (blocks[block].unique === true) ) ) {
Line 2,464 ⟶ 2,492:
lines.push(regExpMatch.index);
}
 
// get heading positions
var headings = [];