User:Cacycle/diff.js: Difference between revisions

Content deleted Content added
1.0.7 (September 01, 2014) jshint, split wDiff.DetectBlocks, unlinking too short groups, Greasemonkey header
1.0.8 (September 02, 2014) css styles to classes, disable colors, add scrolling between block and its mark, add bloack and mark highlighting on hovering
Line 3:
// ==UserScript==
// @name wDiff
// @version 1.0.78
// @date September 0102, 2014
// @description improved word-based diff library with block move detection
// @homepage https://en.wikipedia.org/wiki/User:Cacycle/diff
Line 101:
var wDiff; if (wDiff === undefined) { wDiff = {}; }
var WED;
 
//
// css for core diff
//
 
if (wDiff.styleContainer === undefined) { wDiff.styleContainer = ''; }
if (wDiff.StyleDelete === undefined) { wDiff.styleDelete = 'font-weight: normal; text-decoration: none; color: #fff; background-color: #c33; border-radius: 0.25em; padding: 0.2em 1px;'; }
if (wDiff.styleInsert === undefined) { wDiff.styleInsert = 'font-weight: normal; text-decoration: none; color: #fff; background-color: #07e; border-radius: 0.25em; padding: 0.2em 1px;'; }
if (wDiff.styleBlockLeft === undefined) { wDiff.styleBlockLeft = 'background-color: #d0d0d0; border-radius: 0.25em; padding: 0.25em 1px; margin: 0 1px;'; }
if (wDiff.styleBlockRight === undefined) { wDiff.styleBlockRight = 'background-color: #d0d0d0; border-radius: 0.25em; padding: 0.25em 1px; margin: 0 1px;'; }
if (wDiff.styleBlockColor === undefined) { wDiff.styleBlockColor = [
'background-color: #ffff60;',
'background-color: #c0ff60;',
'background-color: #ffd8ff;',
'background-color: #a0ffff;',
'background-color: #ffe840;',
'background-color: #bbccff;',
'background-color: #ffaaff;',
'background-color: #ffbbbb;',
'background-color: #a0e8a0;'
]; }
if (wDiff.styleMarkLeft === undefined) { wDiff.styleMarkLeft = 'color: #d0d0d0; background-color: #c33; border-radius: 0.25em; padding: 0.2em 0.2em; margin: 0 1px;'; }
if (wDiff.styleMarkRight === undefined) { wDiff.styleMarkRight = 'color: #d0d0d0; background-color: #c33; border-radius: 0.25em; padding: 0.2em 0.2em; margin: 0 1px;'; }
if (wDiff.styleMarkColor === undefined) { wDiff.styleMarkColor = [
'color: #ffff60;',
'color: #c0ff60;',
'color: #ffd8ff;',
'color: #a0ffff;',
'color: #ffd840;',
'color: #bbccff;',
'color: #ff99ff;',
'color: #ff9999;',
'color: #90d090;'
]; }
if (wDiff.styleNewline === undefined) { wDiff.styleNewline = ''; }
if (wDiff.styleTab === undefined) { wDiff.styleTab = ''; }
if (wDiff.stylesheet === undefined) { wDiff.stylesheet = '.wDiffTab:before { content: "→"; color: #bbb; font-size: smaller; } .wDiffNewline:before { content: "¶"; color: #ccc; padding: 0 0.2em 0 1px; } .wDiffMarkRight:before { content: "▶"; } .wDiffMarkLeft:before { content: "◀"; }'; }
 
//
// css for shorten output
//
 
if (wDiff.styleFragment === undefined) { wDiff.styleFragment = 'white-space: pre-wrap; background: #fcfcfc; border: #bbb solid; border-width: 1px 1px 1px 0.5em; border-radius: 0.5em; font-family: inherit; font-size: 88%; line-height: 1.6; box-shadow: 2px 2px 2px #ddd; padding: 1em; margin: 0;'; }
if (wDiff.styleNoChange === undefined) { wDiff.styleNoChange = 'white-space: pre-wrap; background: #f0f0f0; border: #bbb solid; border-width: 1px 1px 1px 0.5em; border-radius: 0.5em; font-family: inherit; font-size: 88%; line-height: 1.6; box-shadow: 2px 2px 2px #ddd; padding: 0.5em; margin: 1em 0;'; }
if (wDiff.styleSeparator === undefined) { wDiff.styleSeparator = 'margin-bottom: 1em;'; }
if (wDiff.styleOmittedChars === undefined) { wDiff.styleOmittedChars = ''; }
 
//
// html for core diff,
//
 
// {block} and {mark} are replaced by block number color style, {title} is replaced by title attribute (popup)
// class plus html comment are required indicators for wDiff.ShortenOutput()
if (wDiff.htmlContainerStart === undefined) { wDiff.htmlContainerStart = '<div class="wDiffContainer" style="' + wDiff.styleContainer + '">'; }
if (wDiff.htmlContainerEnd === undefined) { wDiff.htmlContainerEnd = '</div>'; }
 
if (wDiff.htmlDeleteStart === undefined) { wDiff.htmlDeleteStart = '<span class="wDiffDelete" style="' + wDiff.styleDelete + '" title="−">'; }
if (wDiff.htmlDeleteEnd === undefined) { wDiff.htmlDeleteEnd = '</span><!--wDiffDelete-->'; }
 
if (wDiff.htmlInsertStart === undefined) { wDiff.htmlInsertStart = '<span class="wDiffInsert" style="' + wDiff.styleInsert + '" title="+">'; }
if (wDiff.htmlInsertEnd === undefined) { wDiff.htmlInsertEnd = '</span><!--wDiffInsert-->'; }
 
if (wDiff.htmlBlockLeftStart === undefined) { wDiff.htmlBlockLeftStart = '<span class="wDiffBlockLeft" style="' + wDiff.styleBlockLeft + ' {block}" title="▶ ▢">'; }
if (wDiff.htmlBlockLeftEnd === undefined) { wDiff.htmlBlockLeftEnd = '</span><!--wDiffBlockLeft-->'; }
 
if (wDiff.htmlBlockRightStart === undefined) { wDiff.htmlBlockRightStart = '<span class="wDiffBlockRight" style="' + wDiff.styleBlockRight + ' {block}" title="▭ ◀">'; }
if (wDiff.htmlBlockRightEnd === undefined) { wDiff.htmlBlockRightEnd = '</span><!--wDiffBlockRight-->'; }
 
if (wDiff.htmlMarkRight === undefined) { wDiff.htmlMarkRight = '<span class="wDiffMarkRight" style="' + wDiff.styleMarkRight + ' {mark}"{title}></span><!--wDiffMarkRight-->'; }
if (wDiff.htmlMarkLeft === undefined) { wDiff.htmlMarkLeft = '<span class="wDiffMarkLeft" style="' + wDiff.styleMarkLeft + ' {mark}"{title}></span><!--wDiffMarkLeft-->'; }
 
if (wDiff.htmlNewline === undefined) { wDiff.htmlNewline = '<span class="wDiffNewline" style="' + wDiff.styleNewline + '"></span>\n'; }
if (wDiff.htmlTab === undefined) { wDiff.htmlTab = '<span class="wDiffTab" style="' + wDiff.styleTab + '">\t</span>'; }
 
//
// html for shorten output
//
 
if (wDiff.htmlFragmentStart === undefined) { wDiff.htmlFragmentStart = '<pre class="wDiffFragment" style="' + wDiff.styleFragment + '">'; }
if (wDiff.htmlFragmentEnd === undefined) { wDiff.htmlFragmentEnd = '</pre>'; }
 
if (wDiff.htmlNoChange === undefined) { wDiff.htmlNoChange = '<pre class="wDiffFragment" style="' + wDiff.styleNoChange + '" title="="></pre>'; }
if (wDiff.htmlSeparator === undefined) { wDiff.htmlSeparator = '<div class="wDiffStyleSeparator" style="' + wDiff.styleSeparator + '"></div>'; }
if (wDiff.htmlOmittedChars === undefined) { wDiff.htmlOmittedChars = '<span class="wDiffOmittedChars" style="' + wDiff.styleOmittedChars + '">…</span>'; }
 
//
Line 201 ⟶ 117:
// enable recursive diff to resolve problematic sequences
if (wDiff.recursiveDiff === undefined) { wDiff.recursiveDiff = true; }
 
// display blocks in different colors
if (wDiff.coloredBlocks === undefined) { wDiff.coloredBlocks = false; }
 
// UniCode letter support for regexps, from http://xregexp.com/addons/unicode/unicode-base.js v1.0.0
Line 223 ⟶ 142:
// regExp for counting words
if (wDiff.regExpWordCount === undefined) { wDiff.regExpWordCount = new RegExp('(^|[^' + wDiff.letters + '])[' + wDiff.letters + '][' + wDiff.letters + '_\'’]*', 'g'); }
 
 
//
Line 254 ⟶ 172:
if (wDiff.fragmentJoinLines === undefined) { wDiff.fragmentJoinLines = 10; }
if (wDiff.fragmentJoinChars === undefined) { wDiff.fragmentJoinChars = 1000; }
 
//
// css classes
//
 
if (wDiff.stylesheet === undefined) {
wDiff.stylesheet =
'.wDiffTab:before { content: "→"; color: #bbb; font-size: smaller; }' +
'.wDiffNewline:before { content: "¶"; color: #ccc; padding: 0 0.2em 0 1px; }' +
'.wDiffMarkRight:before { content: "▶"; }' +
'.wDiffMarkLeft:before { content: "◀"; }' +
'.wDiffDelete { font-weight: normal; text-decoration: none; color: #fff; background-color: #c33; border-radius: 0.25em; padding: 0.2em 1px; }' +
'.wDiffInsert { font-weight: normal; text-decoration: none; color: #fff; background-color: #07e; border-radius: 0.25em; padding: 0.2em 1px; }' +
'.wDiffBlockLeft { background-color: #ddd; border-radius: 0.25em; padding: 0.25em 1px; margin: 0 1px; }' +
'.wDiffBlockRight { background-color: #ddd; border-radius: 0.25em; padding: 0.25em 1px; margin: 0 1px; }' +
'.wDiffMarkLeft { color: #ddd; background-color: #c33; border-radius: 0.25em; padding: 0.2em 0.2em; margin: 0 1px; }' +
'.wDiffMarkRight { color: #ddd; background-color: #c33; border-radius: 0.25em; padding: 0.2em 0.2em; margin: 0 1px; }' +
'.wDiffFragment { white-space: pre-wrap; background: #fcfcfc; border: #bbb solid; border-width: 1px 1px 1px 0.5em; border-radius: 0.5em; font-family: inherit; font-size: 88%; line-height: 1.6; box-shadow: 2px 2px 2px #ddd; padding: 1em; margin: 0; }' +
'.wDiffNoChange { white-space: pre-wrap; background: #f0f0f0; border: #bbb solid; border-width: 1px 1px 1px 0.5em; border-radius: 0.5em; font-family: inherit; font-size: 88%; line-height: 1.6; box-shadow: 2px 2px 2px #ddd; padding: 0.5em; margin: 1em 0; }' +
'.wDiffSeparator { margin-bottom: 1em; }' +
'.wDiffSeparator { }' +
'.wDiffBlock { background-color: #ddd; }' +
'.wDiffBlock0 { background-color: #ffff60; }' +
'.wDiffBlock1 { background-color: #c0ff60; }' +
'.wDiffBlock2 { background-color: #ffd8ff; }' +
'.wDiffBlock3 { background-color: #a0ffff; }' +
'.wDiffBlock4 { background-color: #ffe840; }' +
'.wDiffBlock5 { background-color: #bbccff; }' +
'.wDiffBlock6 { background-color: #ffaaff; }' +
'.wDiffBlock7 { background-color: #ffbbbb; }' +
'.wDiffBlock8 { background-color: #a0e8a0; }' +
'.wDiffMark { color: #ddd; }' +
'.wDiffMark0 { color: #ffff60; }' +
'.wDiffMark1 { color: #c0ff60; }' +
'.wDiffMark2 { color: #ffd8ff; }' +
'.wDiffMark3 { color: #a0ffff; }' +
'.wDiffMark4 { color: #ffd840; }' +
'.wDiffMark5 { color: #bbccff; }' +
'.wDiffMark6 { color: #ff99ff; }' +
'.wDiffMark7 { color: #ff9999; }' +
'.wDiffMark8 { color: #90d090; }' +
'.wDiffBlockHighlight { background-color: #333; color: #fff; }' +
'.wDiffMarkHighlight { background-color: #333; color: #fff; }';
}
 
//
// css styles
//
 
if (wDiff.styleContainer === undefined) { wDiff.styleContainer = ''; }
if (wDiff.StyleDelete === undefined) { wDiff.styleDelete = ''; }
if (wDiff.styleInsert === undefined) { wDiff.styleInsert = ''; }
if (wDiff.styleBlockLeft === undefined) { wDiff.styleBlockLeft = ''; }
if (wDiff.styleBlockRight === undefined) { wDiff.styleBlockRight = ''; }
if (wDiff.styleBlockHighlight === undefined) { wDiff.styleBlockHighlight = ''; }
if (wDiff.styleBlockColor === undefined) { wDiff.styleBlockColor = []; }
if (wDiff.styleMarkLeft === undefined) { wDiff.styleMarkLeft = ''; }
if (wDiff.styleMarkRight === undefined) { wDiff.styleMarkRight = ''; }
if (wDiff.styleMarkColor === undefined) { wDiff.styleMarkColor = []; }
if (wDiff.styleNewline === undefined) { wDiff.styleNewline = ''; }
if (wDiff.styleTab === undefined) { wDiff.styleTab = ''; }
if (wDiff.styleFragment === undefined) { wDiff.styleFragment = ''; }
if (wDiff.styleNoChange === undefined) { wDiff.styleNoChange = ''; }
if (wDiff.styleSeparator === undefined) { wDiff.styleSeparator = ''; }
if (wDiff.styleOmittedChars === undefined) { wDiff.styleOmittedChars = ''; }
 
//
// html for core diff
//
 
// 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(null, this);"'; }
 
if (wDiff.htmlContainerStart === undefined) { wDiff.htmlContainerStart = '<div class="wDiffContainer" style="' + wDiff.styleContainer + '">'; }
if (wDiff.htmlContainerEnd === undefined) { wDiff.htmlContainerEnd = '</div>'; }
 
if (wDiff.htmlDeleteStart === undefined) { wDiff.htmlDeleteStart = '<span class="wDiffDelete" style="' + wDiff.styleDelete + '" title="−">'; }
if (wDiff.htmlDeleteEnd === undefined) { wDiff.htmlDeleteEnd = '</span><!--wDiffDelete-->'; }
 
if (wDiff.htmlInsertStart === undefined) { wDiff.htmlInsertStart = '<span class="wDiffInsert" style="' + wDiff.styleInsert + '" title="+">'; }
if (wDiff.htmlInsertEnd === undefined) { wDiff.htmlInsertEnd = '</span><!--wDiffInsert-->'; }
 
if (wDiff.htmlBlockLeftStart === undefined) { wDiff.htmlBlockLeftStart = '<span class="wDiffBlockLeft wDiffBlock{class}" style="' + wDiff.styleBlockLeft + '{block}" title="▶ ▢" id="wDiffBlock{number}"' + wDiff.blockEvent + '>'; }
if (wDiff.htmlBlockLeftEnd === undefined) { wDiff.htmlBlockLeftEnd = '</span><!--wDiffBlockLeft-->'; }
 
if (wDiff.htmlBlockRightStart === undefined) { wDiff.htmlBlockRightStart = '<span class="wDiffBlockRight wDiffBlock{class}" style="' + wDiff.styleBlockRight + '{block}" title="▭ ◀" id="wDiffBlock{number}"' + wDiff.blockEvent + '>'; }
if (wDiff.htmlBlockRightEnd === undefined) { wDiff.htmlBlockRightEnd = '</span><!--wDiffBlockRight-->'; }
 
if (wDiff.htmlMarkRight === undefined) { wDiff.htmlMarkRight = '<span class="wDiffMarkRight wDiffMark{class}" style="' + wDiff.styleMarkRight + '{mark}"{title} id="wDiffMark{number}"' + wDiff.blockEvent + '></span><!--wDiffMarkRight-->'; }
if (wDiff.htmlMarkLeft === undefined) { wDiff.htmlMarkLeft = '<span class="wDiffMarkLeft wDiffMark{class}" style="' + wDiff.styleMarkLeft + '{mark}"{title} id="wDiffMark{number}"' + wDiff.blockEvent + '></span><!--wDiffMarkLeft-->'; }
 
if (wDiff.htmlNewline === undefined) { wDiff.htmlNewline = '<span class="wDiffNewline" style="' + wDiff.styleNewline + '"></span>\n'; }
if (wDiff.htmlTab === undefined) { wDiff.htmlTab = '<span class="wDiffTab" style="' + wDiff.styleTab + '">\t</span>'; }
 
//
// html for shorten output
//
 
if (wDiff.htmlFragmentStart === undefined) { wDiff.htmlFragmentStart = '<pre class="wDiffFragment" style="' + wDiff.styleFragment + '">'; }
if (wDiff.htmlFragmentEnd === undefined) { wDiff.htmlFragmentEnd = '</pre>'; }
 
if (wDiff.htmlNoChange === undefined) { wDiff.htmlNoChange = '<pre class="wDiffFragment" style="' + wDiff.styleNoChange + '" title="="></pre>'; }
if (wDiff.htmlSeparator === undefined) { wDiff.htmlSeparator = '<div class="wDiffStyleSeparator" style="' + wDiff.styleSeparator + '"></div>'; }
if (wDiff.htmlOmittedChars === undefined) { wDiff.htmlOmittedChars = '<span class="wDiffOmittedChars" style="' + wDiff.styleOmittedChars + '">…</span>'; }
 
 
//
// javascript handler for output code
//
 
// wDiff.BlockHandler: event handler for block and mark elements
if (wDiff.BlockHandler === undefined) { wDiff.BlockHandler = function (event, element) {
 
// get event data
var type;
if (event !== null) {
element = event.currentTarget;
type = event.type;
event.stopPropagation();
}
 
// get mark/block elements
var number = element.id.replace(/\D/g, '');
var block = document.getElementById('wDiffBlock' + number);
var mark = document.getElementById('wDiffMark' + number);
switch (type) {
 
// highlight corresponding mark/block pairs
case undefined:
case 'mouseover':
if (element.addEventListener !== undefined) {
element.addEventListener('mouseout', wDiff.BlockHandler, false);
element.addEventListener('click', wDiff.BlockHandler, false);
}
else if (element.attachEvent !== undefined) {
element.attachEvent('onmouseout', wDiff.BlockHandler);
element.attachEvent('onclick', wDiff.BlockHandler);
}
else {
return;
}
block.className += ' wDiffBlockHighlight';
mark.className += ' wDiffMarkHighlight';
break;
 
// remove mark/block highlighting
case 'mouseout':
var highlighted = document.getElementsByClassName('wDiffBlockHighlight');
for (var i = 0; i < highlighted.length; i ++) {
highlighted[i].className = highlighted[i].className.replace(/ wDiffBlockHighlight/g, '');
}
var highlighted = document.getElementsByClassName('wDiffMarkHighlight');
for (var i = 0; i < highlighted.length; i ++) {
highlighted[i].className = highlighted[i].className.replace(/ wDiffMarkHighlight/g, '');
}
break;
 
// scroll to corresponding mark/block element
case 'click':
 
// remove element click handler
if (element.removeEventListener !== undefined) {
element.removeEventListener('mouseout', wDiff.BlockHandler, false);
}
else {
element.detachEvent('onmouseout', wDiff.BlockHandler);
}
 
// get corresponding element
var corrElement;
if (element == block) {
corrElement = mark;
}
else {
corrElement = block;
}
 
// get offsetTop
var corrElementPos = 0;
var node = corrElement;
do {
corrElementPos += node.offsetTop;
} while ( (node = node.offsetParent) !== null );
 
// scroll element under mouse cursor
var top = window.pageYOffset;
var cursor = event.pageY;
var line = parseInt(window.getComputedStyle(corrElement).getPropertyValue('line-height'));
window.scroll(0, corrElementPos + top - cursor + line / 2);
break;
}
return;
}; }
 
//
// start of diff code
//
 
 
Line 280 ⟶ 396:
wDiff.AddStyleSheet(wDiff.stylesheet);
 
// add block handler to head if running under Greasemonkey
if (typeof GM_info == 'object') {
var script = 'var wDiff; if (wDiff === undefined) { wDiff = {}; } wDiff.BlockHandler = ' + wDiff.BlockHandler.toString();
wDiff.AddScript(script);
}
return;
};
Line 1,177 ⟶ 1,298:
wDiff.GetGroups(blocks, groups);
 
// convert groups to insertions/deletions if maximal block length is too short
if ( (wDiff.blockMinLength > 0) && (wDiff.UnlinkBlocks(text, blocks, groups) === true) ) {
 
// repeat from start after conversion to insertions/deletions
wDiff.GetSameBlocks(text, blocks);
Line 2,042 ⟶ 2,163:
}
 
// moveddisplay block too small, make it aas deletion at its original position
if (wDiff.showBlockMoves === false) {
mark = wDiff.htmlDeleteStart + wDiff.HtmlEscape(movedText) + wDiff.htmlDeleteEnd;
Line 2,097 ⟶ 2,218:
 
//
// wDiff.HtmlCustomize: customize move indicator html: replace {block} with: block number style, {mark} with: mark number style, and{class}: class number, {titlenumber}: withblock number, {title}: title attribute (popup)
// input: text (html or css code)
// returns: customized text
Line 2,104 ⟶ 2,225:
wDiff.HtmlCustomize = function (text, number, title) {
 
if (wDiff.coloredBlocks === true) {
text = text.replace(/\{block\}/, wDiff.styleBlockColor[number] || '');
text var blockStyle = text.replace(/\{mark\}/, wDiff.styleMarkColorstyleBlockColor[number] || '');
if (blockStyle === undefined) {
blockStyle = '';
}
var markStyle = wDiff.styleMarkColor[number];
if (markStyle === undefined) {
markStyle = '';
}
text = text.replace(/\{block\}/g, ' ' + blockStyle);
text = text.replace(/\{mark\}/g, ' ' + markStyle);
text = text.replace(/\{class\}/g, number);
}
else {
text = text.replace(/\{block\}|\{mark\}|\{class\}/g, '');
}
text = text.replace(/\{number\}/g, number);
 
// shorten title text, replace {title}
Line 2,119 ⟶ 2,255:
title = title.replace(/ /g, '&nbsp;&nbsp;');
text = text.replace(/\{title\}/, ' title="' + title + '"');
}
else {
text = text.replace(/\{title\}/, '');
}
return text;
Line 2,174 ⟶ 2,313:
 
// scan for diff html tags
var regExpDiff = /<\w+\b[^>]*\bclass="wDiff[^">]*?\bwDiff(MarkLeft|MarkRight|BlockLeft|BlockRight|Delete|Insert)\b[^">]*"[^>]*>(.|\n)*?<!--wDiff\1-->/g;
var tagStart = [];
var tagEnd = [];
Line 2,183 ⟶ 2,322:
while ( (regExpMatch = regExpDiff.exec(html)) !== null ) {
 
// combine consecutive diff tags
if ( (i > 0) && (tagEnd[i - 1] == regExpMatch.index) ) {
tagEnd[i - 1] = regExpMatch.index + regExpMatch[0].length;
Line 2,456 ⟶ 2,595:
 
return diff;
};
 
 
//
// wDiff.AddScript: add script to head
//
 
wDiff.AddScript = function (code) {
 
var script = document.createElement('script');
script.id = 'wDiffBlockHandler';
if (script.innerText !== undefined) {
script.innerText = code;
}
else {
script.textContent = code;
}
wikEd.head.appendChild(script);
return;
};