Content deleted Content added
final 0.9.92 (September 22, 2010) |
0.9.93 rc1 September 26, 2010 |
||
Line 4:
// version info
wikEd.programVersion = '0.9.
wikEd.programDate = 'September
/*
Line 49:
wikEd.InitGlobalConfigs = function() {
// user readable texts, copy changes to http://en.wikipedia.org/wiki/User:Cacycle/wikEd_international_en.js, also defined in
if (typeof(wikEd.config.text) == 'undefined') { wikEd.config.text = {}; }
Line 58:
// logo
'wikEdLogo alt': 'wikEd',
'wikEdLogo title': 'wikEd {
'wikEdLogo error alt': 'wikEd error',
'wikEdLogo error title': 'Loading error - wikEd {
'wikEdLogo browser alt': '(wikEd)',
'wikEdLogo browser title': 'Browser not supported - wikEd {
'wikEdLogo incompatible alt': '(wikEd)',
'wikEdLogo incompatible title': 'Incompatible script or gadget: {
'wikEdLogo disabled alt': '(wikEd)',
'wikEdLogo disabled title': 'Disabled - wikEd {
// top jumper
Line 240:
'/* */ ', 'copyedit', 'reply', 'article created', 'intro rewrite',
'linkfix', 'fixing typos', 'removing linkspam', 'reverting test',
'reverting vandalism', 'formatting source text', '{
],
'wikEdSummaryUsing': '…using [[en:User:Cacycle/wikEd|wikEd]]',
Line 252:
'wikEdLocalDiffImg alt': 'Changes below',
'wikEdLocalDiff title': 'Show current changes below',
'wikEdHelpPageLink': ' | <a href="{
// preview and changes buttons, top
Line 362:
wikEd.InitText();
// use local copies of images for testing (set to true in local copy of edit page), also defined in
if (typeof(wikEd.config.useLocalImages) == 'undefined') { wikEd.config.useLocalImages = false; }
// path to local images for testing, also defined in
if (typeof(wikEd.config.imagePathLocal) == 'undefined') { wikEd.config.imagePathLocal = 'file:///D:/wikEd/images/'; }
// path to images, also defined in
if (typeof(wikEd.config.imagePath) == 'undefined') { wikEd.config.imagePath = 'http://upload.wikimedia.org/wikipedia/commons/'; }
// image filenames, also defined in
if (typeof(wikEd.config.image) == 'undefined') { wikEd.config.image = {}; }
Line 481:
// frame
'.wikEdFrameHtml': 'height: 100%; width: 100%; padding: 0; margin: 0; background: transparent; background-image: url({
'.wikEdFrameBodyPlain': 'height: auto; min-height: 100%; width: auto; background: transparent; margin: 0; padding: 0; padding-left: 0.25em; overflow: auto; font-family: monospace;',
Line 492:
// syntax highlighting
'.wikEdError': 'background-image: url({
'.wikEdHighlightError': 'color: black; background: #faa;',
Line 498:
'.wikEdHtmlTag': 'color: #777;',
'.wikEdHtmlTagButtons': 'color: #777;',
'.wikEdHtmlUnknown': 'background-image: url({
'.wikEdParsingNote': 'border: 1px outset #fcc; padding: 0 0.5em 0 0.5em; margin: 0 0.25em 0 0.25em; color: black; background: #fcc; text-shadow: none; font-weight: normal; font-size: smaller; font-style: normal; text-decoration: none; font-family: sans-serif;',
Line 664:
'.wikEdFrameBodyNewbie .wikEdRefButton:before, .wikEdFrameBodyNewbie .wikEdRefButtonShow:before':
'content: "{
'.wikEdFrameBodyNewbie .wikEdCharEntity, .wikEdFrameBodyNewbie .wikEdCharEntityShow':
Line 670:
'.wikEdFrameBodyNewbie .wikEdTemplButton:before, .wikEdFrameBodyNewbie .wikEdTemplButtonShow:before':
'content: "{
// table edit
'.wikEdTableEdit': 'border: solid black; border-width: 1px 1px 0 0; background: red; text-shadow: none; background-image: url({
'.wikEdTableEdit td': 'border: solid black; border-width: 0 0 1px 1px; background: white; text-shadow: none;',
'.wikEdTableEdit th': 'border: solid black; border-width: 0 0 1px 1px; background: lightgrey; text-shadow: none; font-weight: bold;',
Line 687:
// dashes
'.wikEdFigureDash': 'background-image: url({
'.wikEdEmDash': 'background-image: url({
'.wikEdEnDash': 'background-image: url({
'.wikEdBarDash': 'background-image: url({
'.wikEdMinusDash': 'background-image: url({
'.wikEdSoftHyphen': 'background-image: url({
'.wikEdSoftHyphen:before': 'content: \'\xa0\'',
'.wikEdHyphenDash': '',
// dashes, invisibles, control chars, and strange spaces
'.wikEdTab': 'white-space: pre; background-image: url({
'.wikEdTabPlain': 'white-space: pre;',
'.wikEdCtrl': 'white-space: pre; background-image: url({
'.wikEdCtrl:before': 'content: \'\xa0\'',
'.wikEdEmSpace': 'background-image: url({
'.wikEdEnSpace': 'background-image: url({
'.wikEdThinSpace': 'background-image: url({
'.wikEdIdeographicSpace': 'background-image: url({
});
};
Line 761:
// button bar grip
'.wikEdButtonBarGrip': 'background: #d4d0cc; padding: 0; background-image: url({
// button bar buttons
Line 836:
'.wikEdEditorWrapper': '',
'.wikEdToolbarWrapper': 'margin: 0 0 0.25em 0;',
'.wikEdButtonBarWrapper': '',
'.wikEdCaptchaWrapper': '',
'.wikEdDebugWrapper': 'clear: both; margin: 0 0 0.25em 0;',
Line 844 ⟶ 845:
'.wikEdButtonsWrapper': '',
'.wikEdSummaryInputWrapper': 'display: inline; white-space: nowrap;',
'.
'.
'.wikEdEditOptionsWrapper': 'float: left; margin-right: 1em;',
'.wikEdEditHelp': 'float: left: display: inline-block; white-space: nowrap;',
'.wikEdLocalPrevWrapper': 'margin: 0.5em 0 0 0;',
'.wikEdInsertWrapper': '',
// various
'.wikEdEditOptions': 'display: inline-block;
'.
'#editpage-specialchars': 'clear: both;',
// wDiff
'.wDiffParagraph:before': 'content: "¶";'
});
};
Line 1,013 ⟶ 1,014:
};
// presets for combo input fields dropdown options, {
if (typeof(wikEd.config.comboPresetOptions) == 'undefined') { wikEd.config.comboPresetOptions = {}; }
if (typeof(wikEd.config.comboPresetOptions['summary']) == 'undefined') { wikEd.config.comboPresetOptions['summary'] = wikEd.config.text['wikEdPresetSummary']; }
Line 1,044 ⟶ 1,045:
if (typeof(wikEd.config.focusEdit) == 'undefined') { wikEd.config.focusEdit = true; }
//
if (typeof(wikEd.config.diffPreset) == 'undefined') { wikEd.config.diffPreset = false; }
Line 1,074 ⟶ 1,075:
if (typeof(wikEd.config.loadDiffScript) == 'undefined') { wikEd.config.loadDiffScript = true; }
// enable external
if (typeof(wikEd.config.loadDiff) == 'undefined') { wikEd.config.loadDiff = true; }
Line 1,118 ⟶ 1,119:
if (typeof(wikEd.config.useLocalPreview) == 'undefined') { wikEd.config.useLocalPreview = true; }
// allow ajax requests from local copy for testing, also defined in
if (typeof(wikEd.config.allowLocalAjax) == 'undefined') { wikEd.config.allowLocalAjax = false; }
Line 1,142 ⟶ 1,143:
if (typeof(wikEd.config.autoUpdateScriptUrl) == 'undefined') { wikEd.config.autoUpdateScriptUrl = wikEd.config.homeBaseUrl + 'w/index.php?action=raw&ctype=text/javascript&title=User:Cacycle/wikEd.user.js'; }
// show complete unshortened article text for local diff, also defined in
if (typeof(wikEd.config.fullDiff) == 'undefined') { wikEd.config.fullDiff = false; }
Line 1,243 ⟶ 1,244:
if (typeof(wikEd.config.diffScriptSrc) == 'undefined') { wikEd.config.diffScriptSrc = wikEd.config.homeBaseUrl + 'w/index.php?title=User:Cacycle/diff.js&action=raw&ctype=text/javascript'; }
//
if (typeof(wikEd.config.diffSrc) == 'undefined') { wikEd.config.diffSrc = wikEd.config.homeBaseUrl + 'w/index.php?title=User:Cacycle/
// InstaView script URL
Line 1,409 ⟶ 1,410:
wikEd.editorWrapper = null;
wikEd.toolbarWrapper = null;
wikEd.buttonBarWrapper = null;
wikEd.captchaWrapper = null;
wikEd.debugWrapper = null;
Line 1,418 ⟶ 1,420:
wikEd.summaryWrapper = null;
wikEd.summaryInputWrapper = null;
wikEd.
wikEd.submitWrapper = null;
wikEd.submitButtonsWrapper = null;
Line 1,426 ⟶ 1,428:
// edit form fields
wikEd.editForm = null;
wikEd.starttime
wikEd.edittime = null;
wikEd.editToken = null;
wikEd.autoSummary
wikEd.textarea = null;
Line 1,603 ⟶ 1,607:
// variables needed during startup, might be called multiple times
// hash of loaded scripts, also defined in
if (typeof(wikEd.externalScripts) == 'undefined') { wikEd.externalScripts = null; }
if (typeof(wikEd.pageLoaded) == 'undefined') { wikEd.pageLoaded = false; }
Line 1,635 ⟶ 1,639:
if (typeof(wikEd.loadingTranslation) == 'undefined') { wikEd.loadingTranslation = false; }
if (typeof(wikEd.webStorage) == 'undefined') { wikEd.webStorage = false; }
if (typeof(wikEd.pageOrigin) == 'undefined') { wikEd.pageOrigin = ''; }
// customization
if (typeof(wikEd.wikEdTextAdded) == 'undefined') { wikEd.wikEdTextAdded = false; }
if (typeof(wikEd.wikEdConfigAdded) == 'undefined') { wikEd.wikEdConfigAdded = false; }
// check for web storage availability, throws error in FF 3.6 with dom.storage.enabled=false, see bug 599479 (code copied to wikEdDiff.js)
if (typeof(wikEdTypeofLocalStorage) == 'undefined') {
window.wikEdTypeofLocalStorage = '';
setTimeout('window.wikEdTypeofLocalStorage = typeof(window.localStorage);', 0);
}
// global dom elements, also defined in wikEdDiff.js
if (typeof(wikEd.head) == 'undefined') { wikEd.head = null; }
Line 1,798 ⟶ 1,812:
wikEd.wikEdTextAdded = true;
}
// compatibility fixes for older customizations and wikEd-compatibilizations in other scripts
Line 1,811 ⟶ 1,824:
wikEd.greasemonkey = true;
}
// define head
wikEd.head = document.getElementsByTagName('head')[0];
// get site of origin (window.___location.href is about:blank if Firefox during page load)
var url = window.___location.href;
if (url == 'about:blank') {
url = wikEd.head.baseURI
}
wikEd.pageOrigin = url.replace(/^((https?|file):\/\/[^\/?#]*)?.*$/, '$1');
// parse global-context (MediaWiki) variables into hash (for Greasemonkey)
wikEd.AddEventListener(window, 'message', wikEd.GetGlobalsReceiver, false);
var globalNames = ['skin', 'wgServer', 'wgTitle', 'wgCanonicalNamespace', 'wgArticlePath', 'wgScript', 'wgScriptPath', 'wgUserName', 'wgCurRevisionId
if (wikEd.greasemonkey == true) {
globalNames.push('wikEdConfig', 'wikEdText');
Line 1,841 ⟶ 1,864:
// parse globals (asynchronous)
wikEd.GetGlobals(globalNames, gotGlobalsHook);
// schedule the setup routine
Line 2,100 ⟶ 2,118:
}
// load the external
if ( (wikEd.config.loadDiff == true) && (wikEd.externalScripts['wikEdDiff.js'] == null) ) {
if (typeof(wikEd.Diff) == 'undefined') {
Line 2,236 ⟶ 2,254:
// get form elements
var array
array = document.getElementsByName('wpEdittime'); if (array[0] != null) {
wikEd.edittime = array[0].value
}
if (array[0] != null) {
wikEd.starttime = array[0].value
}
array = document.getElementsByName('wpAutoSummary');
if (array[0] != null) {
wikEd.autoSummary = array[0].value
}
array = document.getElementsByName('wpEditToken');
if (array[0] != null) {
wikEd.editToken = array[0].value
Line 2,461 ⟶ 2,488:
// disable wikEd for Lupin's autoedit scripts
// disable wikEd for js pages
if (/\.js$/.test(wikEd.wikiGlobals.wgTitle) == true) {
if ( (wikEd.wikiGlobals.wgCanonicalNamespace != 'User_talk') && (wikEd.wikiGlobals.wgCanonicalNamespace != 'Talk') ) {
Line 2,627 ⟶ 2,654:
}
// create buttons wrapper for toolbar and wikEd
if (wikEd.wikiEditor != null) {
wikEd.buttonsWrapper = wikEd.wikiEditorTop;
Line 2,637 ⟶ 2,664:
}
wikEd.editorWrapper.insertBefore(wikEd.buttonsWrapper, wikEd.editWrapper);
// create button bar wrapper
wikEd.buttonBarWrapper = document.createElement('div');
wikEd.buttonBarWrapper.id = 'wikEdButtonBarWrapper';
wikEd.buttonBarWrapper.className = 'wikEdButtonBarWrapper';
wikEd.buttonsWrapper.appendChild(wikEd.buttonBarWrapper);
// create summary wrapper for summary, minor edit, and watch this page
Line 2,660 ⟶ 2,693:
// create minor edit and watch page wrapper
wikEd.
wikEd.
wikEd.
// create submit wrapper for submit elements
Line 2,675 ⟶ 2,707:
wikEd.submitButtonsWrapper.id = 'wikEdSubmitButtonsWrapper';
wikEd.submitButtonsWrapper.className = 'wikEdSubmitButtonsWrapper';
}
Line 2,723 ⟶ 2,754:
}
//
if (typeof(wikEd.diffTable) == 'object') {
if ( (wikEd.diffTable != null) && (wikEd.diff == true) ) {
Line 2,792 ⟶ 2,823:
wpEditButtons = wikEd.diffPreviewButton.parentNode;
}
if (wpEditButtons != null) {
wikEd.submitButtonsWrapper.appendChild(wpEditButtons);
}
}
// add a link to the wikEd help page
if (wikEd.rearrange == true) {
if ( (wikEd.config.helpPageLink != '') && (wikEd.config.helpPageLink != null) ) {
var editHelpParent = wikEd.diffPreviewButton;
while (editHelpParent != null) {
if (editHelpParent.tagName == 'SPAN') {
break;
}
editHelpParent = editHelpParent.nextSibling;
}
if (editHelpParent != null) {
var editHelp = editHelpParent.lastChild;
while (editHelp != null) {
if (editHelp.tagName == 'A') {
break;
}
editHelp = editHelp.previousSibling;
}
if (editHelp != null) {
wikEd.helpSpan = document.createElement('span');
wikEd.helpSpan.id = 'wikEdHelpSpan';
wikEd.helpSpan.className = 'wikEdHelpSpan';
wikEd.helpSpan.innerHTML = wikEd.config.helpPageLink.replace(/\{wikEdHomeBaseUrl\}/g, wikEd.config.homeBaseUrl);
editHelpParent.insertBefore(wikEd.helpSpan, editHelp.nextSibling);
wikEd.editHelp = wikEd.helpSpan.parentNode;
wikEd.editHelp.id = 'wikEdEditHelp';
wikEd.editHelp.className = 'wikEdEditHelp';
}
}
}
}
// add submit buttons, edit options, and edit help to submit wrapper
if (wikEd.submitWrapper != null) {
if (wikEd.submitButtonsWrapper != null) {
wikEd.submitWrapper.appendChild(wikEd.submitButtonsWrapper);
}
if (wikEd.editOptionsWrapper != null) {
wikEd.submitWrapper.appendChild(wikEd.editOptionsWrapper);
if (wikEd.editOptions != null) {
wikEd.editOptionsWrapper.appendChild(wikEd.editOptions);
// remove linebreak before minor edit checkbox
var node = wikEd.editOptions.firstChild;
while (node != null) {
if (node.tagName != null) {
if (node.tagName == 'BR') {
node.parentNode.removeChild(node);
break;
}
}
node = node.nextSibling;
}
}
}
if (wikEd.editHelp != null) {
wikEd.submitWrapper.appendChild(wikEd.editHelp);
}
}
Line 2,896 ⟶ 2,989:
// MS-IE needs styling for full width frame
if (wikEd.msie == true) {
}
Line 2,909 ⟶ 3,002:
if (wikEd.readOnly == false) {
wikEd.buttonBarFormat = MakeButtonBar(wikEd.config.buttonBar['format']);
wikEd.
wikEd.buttonBarTextify = MakeButtonBar(wikEd.config.buttonBar['textify']);
wikEd.
}
wikEd.buttonBarControl = MakeButtonBar(wikEd.config.buttonBar['control']);
wikEd.
if (wikEd.config.buttonBar['custom1'][6].length > 0) {
wikEd.buttonBarCustom1 = MakeButtonBar(wikEd.config.buttonBar['custom1']);
wikEd.
}
wikEd.buttonBarFind = MakeButtonBar(wikEd.config.buttonBar['find']);
wikEd.
if (wikEd.readOnly == false) {
wikEd.buttonBarFix = MakeButtonBar(wikEd.config.buttonBar['fix']);
wikEd.
}
if (wikEd.config.buttonBar['custom2'][6].length > 0) {
wikEd.buttonBarCustom2 = MakeButtonBar(wikEd.config.buttonBar['custom2']);
wikEd.
}
Line 2,948 ⟶ 3,041:
// add preview box top bar to submit wrapper
wikEd.buttonBarPreview = MakeButtonBar(wikEd.config.buttonBar['preview']);
if ( (wikEd.rearrange == true) && (wikEd.submitWrapper != null) ) {
wikEd.submitWrapper.insertBefore(wikEd.buttonBarPreview, wikEd.submitWrapper.firstChild);
}
Line 3,160 ⟶ 3,253:
// display only the textarea or the iframe, dont change the frame
wikEd.SetEditArea(wikEd.useWikEd, true);
// copy page warnings above edit window
Line 4,105 ⟶ 4,163:
// after cursor movements set cursor position into closest highest text node so that highlighting does not bleed out
case 'keyup':
case 17: // ctrl-v
wikEd.AntiHighlightBleeding(new Object());
}
break;
Line 4,637 ⟶ 4,693:
version += ' GM';
}
wikEd.logo.title = wikEd.logo.title.replace(/\{
wikEd.logo.title = wikEd.logo.title.replace(/\{
wikEd.logo.title = wikEd.logo.title.replace(/\{
return;
Line 4,813 ⟶ 4,869:
}
// set visibility of native toolbar
if (wikEd.closeToolbar == true) {
wikEd.toolbarWrapper.style.display = 'none';
Line 4,819 ⟶ 4,876:
wikEd.toolbarWrapper.style.display = 'block';
}
if (wikEd.buttonBarFormat != null) {
wikEd.buttonBarFormat.style.display = 'block';
Line 4,865 ⟶ 4,923:
wikEd.textarea.style.display = 'block';
// force visibility of native toolbar
if (wikEd.toolbarWrapper != null) {
wikEd.toolbarWrapper.style.display = 'block';
Line 5,193 ⟶ 5,252:
break;
// toggle
case 'wikEdDiff':
// turn
if (wikEd.GetAttribute(buttonObj, 'checked') != 'true') {
wikEd.diff = false;
Line 5,208 ⟶ 5,267:
}
// turn
else {
wikEd.diff = true;
Line 8,504 ⟶ 8,563:
postFields['format'] = 'xml';
postFields['action'] = 'query';
if (wikEd.starttime != null) {
postFields['wpStarttime'] = wikEd.starttime;
}
if (wikEd.edittime != null) {
postFields['
}
if (wikEd.editToken != null) {
postFields['wpEditToken'] = wikEd.editToken;
}
if (wikEd.autoSummary != null) {
postFields['wpAutoSummary'] = wikEd.autoSummary;
}
var requestUrl = wikEd.scriptURL + 'api.php';
Line 13,224 ⟶ 13,290:
wikEd.UpdateFrame();
}
wikEd.
wikEd.buttonBarPreview.style.display = 'block';
if (wikEd.buttonBarJump != null) {
Line 13,263 ⟶ 13,329:
wikEd.frame.style.width = wikEd.frameWidth;
wikEd.
wikEd.buttonBarPreview.style.display = 'none';
wikEd.localPrevWrapper.style.display = 'none';
Line 13,748 ⟶ 13,814:
option.text = wikEd.config.comboPresetOptions[field][i].replace(/ /g, '\xa0');
if (field == 'summary') {
option.text = option.text.replace(/\{
}
wikEd.selectElement[field].options[j++] = option;
Line 13,976 ⟶ 14,042:
var getStr = '';
// check for web storage
if (wikEd.wikiGlobals.wikEdTypeofLocalStorage == 'object') {
wikEd.webStorage = true;
}
// get a value from web storage
if (wikEd.webStorage == true) {
getStr = window.localStorage.getItem(name);
}
Line 14,001 ⟶ 14,072:
wikEd.SetPersistent = function(name, value, expires, path, ___domain, secure) {
// check for web storage
if (wikEd.wikiGlobals.wikEdTypeofLocalStorage == 'object') {
wikEd.webStorage = true;
}
// set a value in web storage
if (wikEd.webStorage == true) {
if (expires == -1) {
window.localStorage.removeItem(name, '');
}
else {
window.localStorage.setItem(name, value);
}
}
Line 14,127 ⟶ 14,202:
wikEd.AppendScript = function(scriptUrl, onLoadFunction) {
var script = document.createElement('script');
script.setAttribute('type', 'text/javascript');
script.setAttribute('src', scriptUrl);
wikEd.head.appendChild(script);
if (onLoadFunction != null) {
wikEd.AddEventListener(script, 'load', onLoadFunction, false);
Line 14,467 ⟶ 14,541:
// replace {wikedImage:image} in css rules with image path
ruleStyle = ruleStyle.replace(/\{
function (p, p1) {
return(wikEd.config.image[p1]);
Line 14,474 ⟶ 14,548:
// replace {wikedText:text} in css rules with translation
ruleStyle = ruleStyle.replace(/\{
function (p, p1) {
return(wikEd.config.text[p1]);
Line 14,631 ⟶ 14,705:
postFields['action'] = 'submit';
postFields['wpTextbox1'] = textValue;
if (wikEd.starttime != null) {
postFields['wpStarttime'] = wikEd.starttime;
}
if (wikEd.edittime != null) {
postFields['
}
if (wikEd.editToken != null) {
postFields['wpEditToken'] = wikEd.editToken;
}
if (wikEd.autoSummary != null) {
postFields['wpAutoSummary'] = wikEd.autoSummary;
}
postFields['wpPreview'] = 'true';
if (livePreview != false) {
Line 14,797 ⟶ 14,879:
wikEd.GetGlobals = function(names, gotGlobalsHook) {
if ( (typeof(window.postMessage)
return;
}
Line 14,818 ⟶ 14,900:
}
globalScopeCode += 'var globalObjStr = JSON.stringify(globalObj);';
var origin = wikEd.pageOrigin;
if (origin == 'file://') {
origin = '*';
}
globalScopeCode += 'window.postMessage(globalObjStr, \'' + origin + '\');';
// create head script to execute the code
Line 14,829 ⟶ 14,910:
script.id = 'wikEdGetGlobalScript' + wikEd.getGlobalsCounter;
wikEd.getGlobalsCounter ++;
if (typeof(script.
script.innerText = globalScopeCode;
}
else {
script.textContent = globalScopeCode;
}
wikEd.head.appendChild(script);
return;
Line 14,837 ⟶ 14,923:
//
// wikEd.GetGlobalsReceiver: event handler for wikEd.GetGlobals postMessage (code copied to wikEdDiff.js)
//
Line 14,845 ⟶ 14,931:
return;
}
if ( (event.origin != 'null') && (event.origin !=
return;
}
Line 14,865 ⟶ 14,951:
// clean up head script
var script = document.getElementById(globalObj.scriptId);
var script = document.getElementById(globalObj.scriptId);
}
Line 15,395 ⟶ 15,481:
for (var i = 0; i < functionsHook.length; i ++) {
if (typeof(functionsHook[i]
functionsHook[i]();
}
}
if (onlyOnce == true) {
Line 15,762 ⟶ 15,850:
// call startup
wikEd.Startup();
// </nowiki></pre>
// <pre><nowiki>
if (typeof(wikEd) == 'undefined') { window.wikEd = {}; }
// version info
wikEd.diffProgramVersion = '0.9.10';
wikEd.diffProgramDate = 'September 22, 2010';
/*
== wikEdDiff ==
A user script that provides an improved and easier to read diff view for comparing article versions
on Wikipedia and other MediaWiki sites.
Features:
* Additions and deletions are highlighted by color in the same text
* Block moves are detected and indicated by color
* Unchanged regions of the text are omitted from the output
* Highly optimized for MediaWiki source texts
* Compatibel with Greasemonkey
wikEdDiff uses the Cacycle diff.js routines [[en:User:Cacycle/diff]] and is also an integrated part of wikEd,
the full-featured JavaScript in-browser editor (http://en.wikipedia.org/wiki/User:Cacycle/wikEd)
Homepage: http://en.wikipedia.org/wiki/User:Cacycle/wikEdDiff
Author: Cacycle (http://en.wikipedia.org/wiki/User:Cacycle)
License: This code has been released into the public ___domain
== Installation ==
* Copy the following short block of code to [[User:YOURUSERNAME/monobook.js]]
* Press SHIFT-Reload to update to the newest version
* PLEASE DO NOT COPY THE WHOLE PROGRAM
* See http://en.wikipedia.org/wiki/User:Cacycle/wikEdDiff for detailed instructions
* Users of wikEd do not have to install wikEdDiff
// ---- START INSTALLATION CODE ----
// install [[User:Cacycle/wikEdDiff]] enhanced diff view using ajax
document.write('<script type="text/javascript" src="'
+ 'http://en.wikipedia.org/w/index.php?title=User:Cacycle/wikEdDiff.js'
+ '&action=raw&ctype=text/javascript"></script>');
// ---- END INSTALLATION CODE ----
*/
if (typeof(wikEd.config) == 'undefined') { wikEd.config = {}; }
//
// wikEd.DiffInit: initialize variables
//
wikEd.DiffInit = function() {
//
// user configurable variables
//
// wikEd code home base URL for https compatibility, also defined in wikEd.js
if (typeof(wikEd.config.homeBaseUrlStandard) == 'undefined') { wikEd.config.homeBaseUrlStandard = 'http://en.wikipedia.org/'; }
if (typeof(wikEd.config.homeBaseUrlSecure) == 'undefined') { wikEd.config.homeBaseUrlSecure = 'https://secure.wikimedia.org/wikipedia/en/'; }
// set wikEd home base url depending on current page address: standard (http:) or secure (https:), also defined in wikEd.js
if (window.___location.protocol == 'https:') {
wikEd.config.homeBaseUrl = wikEd.config.homeBaseUrlSecure;
}
else {
wikEd.config.homeBaseUrl = wikEd.config.homeBaseUrlStandard;
}
// diff.js routines URL, also defined in wikEd.js
if (typeof(wikEd.config.diffScriptSrc) == 'undefined') { wikEd.config.diffScriptSrc = wikEd.config.homeBaseUrl + 'w/index.php?title=User:Cacycle/diff.js&action=raw&ctype=text/javascript'; }
// allow ajax requests from local copy for testing, also defined in wikEd.js
if (typeof(wikEd.config.allowLocalAjax) == 'undefined') { wikEd.config.allowLocalAjax = false; }
// wikEdDiff css rules
if (typeof(wikEd.config.diffCSS) == 'undefined') { wikEd.config.diffCSS = {}; }
wikEd.InitObject(wikEd.config.diffCSS, {
'.wikEdDiffWrapper': 'margin: 0 0 1em 0;',
'.wikEdDiffButtonWrapper': 'text-align: center;',
'.wikEdDiffButton': 'padding: 0; margin: 0.2em 0 0.33em 0;',
'.wikEdDiffDiv': 'background: #faf8f6; padding: 0.5em; border: 1px solid; border-color: #808080;'
});
// use local copies of images for testing (set to true in local copy of edit page), also defined in wikEd.js
if (typeof(wikEd.config.localImages) == 'undefined') { wikEd.config.localImages = false; }
// path to local images for testing, also defined in wikEd.js
if (typeof(wikEd.config.imagePathLocal) == 'undefined') { wikEd.config.imagePathLocal = 'file:///D:/wikEd/images/'; }
// path to images, also defined in wikEd.js
if (typeof(wikEd.config.imagePath) == 'undefined') { wikEd.config.imagePath = 'http://upload.wikimedia.org/wikipedia/commons/'; }
// image filenames, also defined in wikEd.js
if (typeof(wikEd.config.image) == 'undefined') { wikEd.config.image = {}; }
wikEd.InitImage(wikEd.config.image, {
'wikEdDiff': 'c/c6/WikEdDiff.png'
});
// user readable texts, copy changes to http://en.wikipedia.org/wiki/User:Cacycle/wikEd_international_en.js, also defined in wikEd.js
if (typeof(wikEd.config.text) == 'undefined') { wikEd.config.text = {}; }
wikEd.InitObject(wikEd.config.text, {
'wikEdDiffButtonImg alt': 'wikEdDiff',
'wikEdDiffButton title': 'Show improved diff view',
'wikEdDiffLoading': '...'
});
// show complete unshortened article text for local diff, also defined in wikEd.js
if (typeof(wikEd.config.fullDiff) == 'undefined') { wikEd.config.fullDiff = false; }
//
// end of user configurable variables
//
// global dom elements
wikEd.diffDiv = null;
wikEd.diffWrapper = null;
wikEd.diffButtonWrapper = null;
wikEd.diffButton = null;
wikEd.diffGetGlobalNode = null;
// hash of loaded scripts, also defined in wikEd.js
if (typeof(wikEd.externalScripts) == 'undefined') { wikEd.externalScripts = null; }
if (typeof(wikEd.diffPreset) == 'undefined') { wikEd.diffPreset = false; }
// diff table element
wikEd.diffTable = null;
};
// variables needed during startup
if (typeof(wikEd.diffStartup) == 'undefined') { wikEd.diffStartup = false; }
// customization, also defined in wikEd.js
if (typeof(wikEd.wikEdConfigAdded) == 'undefined') { wikEd.wikEdConfigAdded = false; }
// detect web storage capability and Greasemonkey related, also defined in wikEd.js
if (typeof(wikEd.webStorage) == 'undefined') { wikEd.webStorage = false; }
if (typeof(wikEd.greasemonkey) == 'undefined') { wikEd.greasemonkey = false; }
if (typeof(wikEd.gotGlobalsHook) == 'undefined') { wikEd.gotGlobalsHook = []; }
if (typeof(wikEd.getGlobalsCounter) == 'undefined') { wikEd.getGlobalsCounter = 0; }
// get global MediaWiki settings, also defined in wikEd.js
if (typeof(wikEd.wikiGlobals) == 'undefined') { wikEd.wikiGlobals = {}; }
// check for web storage availability, throws error in FF 3.6 with dom.storage.enabled=false, see bug 599479 (code copied from wikEd.js)
if (typeof(wikEdTypeofLocalStorage) == 'undefined') {
window.wikEdTypeofLocalStorage = '';
setTimeout('window.wikEdTypeofLocalStorage = typeof(window.localStorage);', 0);
}
// global dom elements, also defined in wikEdDiff.js
if (typeof(wikEd.head) == 'undefined') { wikEd.head = null; }
//
// wikEd.DiffStartup: call the setup routine
//
wikEd.DiffStartup = function() {
// check if this has already been run
if (wikEd.diffStartup == true) {
return;
}
wikEd.diffStartup = true;
// check if this runs under Greasemonkey
if (typeof(GM_getValue) == 'function') {
wikEd.greasemonkey = true;
}
// define head
wikEd.head = document.getElementsByTagName('head')[0];
// parse global-context (MediaWiki) variables into hash (for Greasemonkey)
wikEd.AddEventListener(window, 'message', wikEd.GetGlobalsReceiver, false);
var globalNames = ['wgServer', 'wgArticlePath', 'wgScriptPath', 'wgCurRevisionId', 'wikEdTypeofLocalStorage'];
if (wikEd.greasemonkey == true) {
globalNames.push('wikEdConfig');
}
// copy custom config settings after values have arrived
var gotGlobalsHook = [
function() {
if ( (typeof(wikEd.wikiGlobals.wikEdConfig) == 'object') && (wikEd.wikEdConfigAdded == false) ) {
wikEd.AddToObject(wikEd.config, wikEd.wikiGlobals.wikEdConfig);
wikEd.wikEdConfigAdded = true;
}
return;
}
];
// parse globals (asynchronous)
wikEd.GetGlobals(globalNames, gotGlobalsHook);
// run the setup routine if loaded dynamically from wikEd
if (wikEd.startup == true) {
wikEd.DiffSetup();
}
// schedule the setup routine
else {
wikEd.AddEventListener(window, 'load', wikEd.DiffSetup, false);
}
};
//
// wikEd.DiffSetup: create wikEdDiff elements
//
wikEd.DiffSetup = function() {
// check if this has already been run
if (document.getElementById('wikEdDiffStartupFlag') != null) {
return;
}
wikEd.head = document.getElementsByTagName('head')[0];
var flag = document.createElement('meta');
flag.id = 'wikEdDiffStartupFlag';
wikEd.head.appendChild(flag);
// import customization
if ( (typeof(wikEdConfig) == 'object') && (wikEd.wikEdConfigAdded == false) ) {
wikEd.AddToObject(wikEd.config, wikEdConfig);
wikEd.wikEdConfigAdded = true;
}
// initialize variables
wikEd.DiffInit();
// get MediaWiki globals for old browsers
if ( (typeof(wikEd.wikiGlobals.wgServer) == 'undefined') && (typeof(wgServer) != 'undefined') ) {
wikEd.wikiGlobals.wgServer = wgServer;
}
if ( (typeof(wikEd.wikiGlobals.wgArticlePath) == 'undefined') && (typeof(wgArticlePath) != 'undefined') ) {
wikEd.wikiGlobals.wgArticlePath = wgArticlePath;
}
if ( (typeof(wikEd.wikiGlobals.wgScriptPath) == 'undefined') && (typeof(wgScriptPath) != 'undefined') ) {
wikEd.wikiGlobals.wgScriptPath = wgScriptPath;
}
if ( (typeof(wikEd.wikiGlobals.wgCurRevisionId) == 'undefined') && (typeof(wgCurRevisionId) != 'undefined') ) {
wikEd.wikiGlobals.wgCurRevisionId = wgCurRevisionId;
}
if ( (typeof(wikEd.wikiGlobals.wgPageName) == 'undefined') && (typeof(wgPageName) != 'undefined') ) {
wikEd.wikiGlobals.wgPageName = wgPageName;
}
// detect diff table
var table = document.getElementsByTagName('table');
for (var i = 0; i < table.length; i ++) {
if (table[i].className == 'diff') {
wikEd.diffTable = table[i];
}
}
// check if this is a diff page
if (wikEd.diffTable == null) {
return;
}
// detect already loaded external scripts, also in wikEd.js
if (wikEd.externalScripts == null) {
wikEd.externalScripts = [];
var pageScripts = document.getElementsByTagName('script');
for (var i = 0; i < pageScripts.length; i ++) {
var scriptSrc = pageScripts[i].src;
var nameMatch = scriptSrc.match(/\btitle=([^&]*)/);
if (nameMatch == null) {
nameMatch = scriptSrc.match(/\/([^\/]*?)($|\?)/);
}
if (nameMatch != null) {
var scriptName = nameMatch[1];
if (scriptName != '') {
// ignore other diff.js scripts
if ( (scriptName == 'diff.js') && (scriptSrc != wikEd.config.diffScriptSrc) ) {
continue;
}
wikEd.externalScripts[scriptName] = true;
}
}
}
}
// load the external diff script
if (wikEd.externalScripts['diff.js'] == null) {
if (typeof(WDiffString) == 'undefined') {
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = wikEd.config.diffScriptSrc;
wikEd.head.appendChild(script);
}
wikEd.externalScripts['diff.js'] = true;
}
// add stylesheet definitions (slow method for IE compatibility)
var diffStyle = new wikEd.StyleSheet();
for (var ruleName in wikEd.config.diffCSS) {
if (wikEd.config.diffCSS.hasOwnProperty(ruleName) == true) {
var ruleStyle = wikEd.config.diffCSS[ruleName];
diffStyle.AddRule(ruleName, ruleStyle);
}
}
// create wikEdDiff wrapper
wikEd.diffWrapper = document.createElement('div');
wikEd.diffWrapper.id = 'wikEdDiffWrapper';
wikEd.diffWrapper.className = 'wikEdDiffWrapper';
// create wikEdDiff button wrapper
wikEd.diffButtonWrapper = document.createElement('div');
wikEd.diffButtonWrapper.id = 'wikEdDiffButtonWrapper';
wikEd.diffButtonWrapper.className = 'wikEdDiffButtonWrapper';
wikEd.diffWrapper.appendChild(wikEd.diffButtonWrapper);
// create wikEdDiff button
wikEd.diffButton = document.createElement('button');
wikEd.diffButton.id = 'wikEdDiffButton';
wikEd.diffButton.title = wikEd.config.text['wikEdDiffButton title'];
wikEd.diffButton.className = 'wikEdDiffButton';
wikEd.diffButtonWrapper.appendChild(wikEd.diffButton);
// add button image
var diffImg = document.createElement('img');
diffImg.id = 'wikEdDiffButtonImg';
diffImg.src = wikEd.config.image['wikEdDiff'];
diffImg.title = wikEd.config.text['wikEdDiffButton title'];
diffImg.alt = wikEd.config.text['wikEdDiffButtonImg alt'];
wikEd.diffButton.appendChild(diffImg);
wikEd.diffDiv = document.createElement('div');
wikEd.diffDiv.id = 'wikEdDiffDiv';
wikEd.diffDiv.className = 'wikEdDiffDiv';
wikEd.diffDiv.style.display = 'none';
// add wrapper after diff table
wikEd.diffWrapper.appendChild(wikEd.diffDiv);
if (wikEd.diffTable.nextSibling != null) {
wikEd.diffTable.parentNode.insertBefore(wikEd.diffWrapper, wikEd.diffTable.nextSibling);
}
else {
wikEd.diffTable.parentNode.appendChild(wikEd.diffWrapper);
}
// add event listener to button
wikEd.AddEventListener(wikEd.diffButton, 'click', wikEd.Diff);
// linkify wikilinks in diff text
var cells = wikEd.diffTable.getElementsByTagName('td');
for (var i = 0; i < cells.length; i ++) {
if (
(cells[i].className == 'diff-context') ||
(cells[i].className == 'diff-deletedline') ||
(cells[i].className == 'diff-addedline')
) {
cells[i].innerHTML = wikEd.DiffLinkify(cells[i].innerHTML);
}
}
// run WikEdDiff if enabled in wikEd
var setting = wikEd.GetPersistent('wikEdDiff');
if ( (setting == '') && (typeof(wikEd.config.diffPreset) == 'boolean') ) {
setting = wikEd.config.diffPreset;
}
else if (setting == '1') {
setting = true;
}
if (setting == true) {
WikEdDiff();
}
// register links for Lupin's Wikipedia:Tools/Navigation_popups
if (typeof(setupTooltips) == 'function') {
setupTooltips(wikEd.diffTable);
}
return;
};
//
// wikEd.Diff: fetch the old versions using ajax to display a diff
//
wikEd.Diff = function() {
// check if set tup
if (wikEd.diffDiv == null) {
return;
}
// check if diff.js is loaded
if (typeof(WDiffString) != 'function') {
return;
}
// display diff
wikEd.diffDiv.style.display = 'block';
// fetch only once
if (wikEd.diffDiv.innerHTML.length > 0) {
return;
}
// check if this is a diff page
if (wikEd.diffTable == null) {
return;
}
// display div
wikEd.diffDiv.innerHTML = wikEd.config.text['wikEdDiffLoading'];
// generate request url from MediaWiki variables or from ___location url
var url;
var server = wikEd.wikiGlobals.wgServer;
var scriptPath = wikEd.wikiGlobals.wgScriptPath;
scriptPath = scriptPath.replace(server, '');
if ( (server != '') && (scriptPath != '') ) {
url = server + scriptPath.replace(/\$1/, '') + '/index.php';
}
else {
url = window.___location.protocol + '//' + window.___location.hostname + '/' + window.___location.pathname;
}
var article;
var pageName = wikEd.wikiGlobals.wgPageName;
if (pageName != '') {
article = pageName;
}
else {
var articleMatch = window.___location.search.match(/(\?|&)title=([^&#]+)/);
if(articleMatch != null) {
article = articleMatch[2];
}
}
url += '?title=' + encodeURIComponent(article) + '&action=raw&maxage=0';
// get diff table and version link cells
var tdArray = document.getElementsByTagName('TD');
var tdOld;
var tdNew;
for (var i = 0; i < tdArray.length; i ++) {
if (tdArray[i].className == 'diff-otitle') {
tdOld = tdArray[i];
}
else if (tdArray[i].className == 'diff-ntitle') {
tdNew = tdArray[i];
break;
}
}
if ( (tdOld == null) || (tdNew == null) ) {
return;
}
var oldVersion = null;
var newVersion = null;
var oldUrl;
var newUrl;
// preview pages use latest article version and textarea
if (
(/(\?|&)action=submit\b/.test(window.___location.search) == true) ||
(/(\?|&)undoafter=/.test(window.___location.search) == true)
) {
var textarea = document.getElementsByName('wpTextbox1');
if (textarea.length == 0) {
return;
}
newVersion = textarea[0].value;
newVersion = newVersion.replace(/\s+$/g, '');
var curRevisionId = wikEd.wikiGlobal.wgCurRevisionId;
if (curRevisionId != '') {
oldUrl = url + '&oldid=' + curRevisionId;
}
else {
oldUrl = url;
}
// get section for section editing
var section = document.getElementsByName('wpSection');
if (section != null) {
if (section.length > 0) {
if (section[0].value != '') {
oldUrl += '§ion=' + section[0].value;
}
}
}
}
// diff pages use two different old versions
else {
// get revision id numbers from links in table cells
var versionMatchOld = tdOld.innerHTML.match(/(\?|&)oldid=(\d+)/);
var versionMatchNew = tdNew.innerHTML.match(/(\?|&)oldid=(\d+)/);
if (versionMatchOld == null) {
return;
}
oldUrl = url + '&oldid=' + versionMatchOld[2];
if (versionMatchNew != null) {
newUrl = url + '&oldid=' + versionMatchNew[2];
}
else {
newUrl = url;
}
}
// get the old version using ajax
var requestMethod = 'GET';
var requestUrl = oldUrl;
var postFields = null;
var overrideMimeType = null;
wikEd.AjaxRequest(requestMethod, requestUrl, postFields, overrideMimeType, function(ajax) {
oldVersion = ajax.responseText;
if (newVersion != null) {
wikEd.DiffResponse(oldVersion, newVersion);
}
return;
});
// get the new version using ajax
if (newUrl != null) {
var requestMethod = 'GET';
var requestUrl = newUrl;
var postFields = null;
var overrideMimeType = null;
wikEd.AjaxRequest(requestMethod, requestUrl, postFields, overrideMimeType, function(ajax) {
newVersion = ajax.responseText;
if (oldVersion != null) {
wikEd.DiffResponse(oldVersion, newVersion);
// does not work yet because added and removed are in same text:
// wikEd.diffDiv.innerHTML = wikEd.DiffLinkify(wikEd.diffDiv.innerHTML);
}
return;
});
}
return;
};
//
// wikEd.DiffResponse: calculate and display the diff between two versions
//
wikEd.DiffResponse = function(oldVersion, newVersion) {
// add trailing newline
if (oldVersion.substr(oldVersion.length - 1, 1) != '\n') {
oldVersion += '\n';
}
if (newVersion.substr(newVersion.length - 1, 1) != '\n') {
newVersion += '\n';
}
// call external diff program
var diffText = WDiffString(oldVersion, newVersion);
if (wikEd.config.fullDiff != true) {
diffText = WDiffShortenOutput(diffText);
}
wikEd.diffDiv.innerHTML = diffText;
wikEd.diffDiv.style.display = 'block';
return;
};
//
// wikEd.DiffLinkify: linkify wikilinks
//
wikEd.DiffLinkify = function(html) {
// < > & to \x00 \x01
html = html.replace(/</g, '\x00');
html = html.replace(/>/g, '\x01');
// external links
html = html.replace(/\b(((https?|ftp|irc|gopher):\/\/)|news:|mailto:)([^\x00-\x20\s\"\[\]\x7f\|\{\}<>]|<[^>]*>)+?(?=([\!\"\(\)\.\,\:\;\‘-•]*(\s|$)|[\x00-\x20\s\"\[\]\x7f\|\{\}]))/gi,
function (p) {
var wholeLink = p;
var targetNoTags = wholeLink.replace(/<[^>]*>/g, '');
targetNoTags = targetNoTags.replace(/<.*?>/g, '');
targetNoTags = targetNoTags.replace(/<.*$/g, '');
var url = targetNoTags.replace(/\s/g, '_');
url = encodeURI(url);
url = url.replace(/\"/g, '%22');
url = url.replace(/\'/g, '%27');
url = url.replace(/#/g, '%23');
var linkTitle = targetNoTags.replace(/\"/g, '"');
// linkify all url text fragments between highlighting <span>s seperately
var anchorOpen = '<a href = "' + url + '" style="text-decoration: none; color: inherit; color: expression(parentElement.currentStyle.color);" title="' + linkTitle + '">';
var anchorClose = '</a>';
var wholeLinkAnchorified = wholeLink.replace(/(<[^>]*>)/g, anchorClose + '$1' + anchorOpen);
return(anchorOpen + wholeLinkAnchorified + anchorClose);
}
);
// linkify links and templates
if ( (wikEd.wikiGlobals.wgServer != null) && (wikEd.wikiGlobals.wgArticlePath != null) ) {
// 12 [ 23 <> 3 [ 4 <> 4 156 article 67 text 75
html = html.replace(/((\[|\{)(<[^>]*>)*\2(<[^>]*>)*)(([^\|\[\]\{\}\n]*)([^\n\[\]\{\}]*))/g,
function (p, p1, p2, p3, p4, p5, p6, p7, p8) {
var pre = p1;
var linkType = p2;
var wholeLink = p5;
var linkTarget = p6;
// create article name
var targetNoTags = linkTarget.replace(/<[^>]*>/g, '');
targetNoTags = targetNoTags.replace(/<.*?>/g, '');
targetNoTags = targetNoTags.replace(/<.*$/g, '');
targetNoTags = targetNoTags.replace(/^\s+|\s+$/g, '');
// create url
var url = targetNoTags.replace(/\s/g, '_');
url = encodeURI(url);
url = url.replace(/\"/g, '%22');
url = url.replace(/\'/g, '%27');
url = url.replace(/#/g, '%23');
var articleTitle = targetNoTags.replace(/\"/g, '"');
if (linkType == '{') {
if (/^[^\|\}\n\r]*:/.test(targetNoTags) == false) {
url = 'Template:' + url;
articleTitle = 'Template:' + articleTitle;
}
}
url = wikEd.wikiGlobals.wgServer + wikEd.wikiGlobals.wgArticlePath.replace(/\$1/, url);
// linkify all text fragments between highlighting <span>s seperately
var anchorOpen = '<a href = "' + url + '" style = "text-decoration: none; color: inherit; color: expression(parentElement.currentStyle.color)" title="' + articleTitle + '">';
var anchorClose = '</a>';
var wholeLinkAnchorified = wholeLink.replace(/(<[^>]*>)/g, anchorClose + '$1' + anchorOpen);
return(pre + anchorOpen + wholeLinkAnchorified + anchorClose);
}
);
}
// \x00 and \x01 back to < and >
html = html.replace(/\x00/g, '<');
html = html.replace(/\x01/g, '>');
return(html);
};
//
// wikEd.InitObject: initialize object, keep pre-defined values (code copied from wikEd.js)
//
if (typeof(wikEd.InitObject) == 'undefined')
wikEd.InitObject = function(target, source, showMissing) {
if (typeof(target) == 'object') {
for (var key in source) {
if (typeof(target[key]) == 'undefined') {
target[key] = source[key];
// show missing array entries
if (showMissing == true) {
if (typeof(target[key]) == 'string') {
wikEd.config.debugStartUp += '\t\t\t\'' + key + '\': \'' + target[key].replace(/\n/g, '\\n') + '\',\n';
}
}
}
}
}
return;
};
//
// wikEd.AddToObject: add or replace properties, replace existing values (code copied from wikEd.js)
//
if (typeof(wikEd.AddToObject) == 'undefined')
wikEd.AddToObject = function(target, source) {
if (typeof(target) == 'object') {
for (var key in source) {
target[key] = source[key];
}
}
return;
};
//
// wikEd.InitImage: initialize images, keep pre-defined values (code copied from wikEd.js)
//
if (typeof(wikEd.InitImage) == 'undefined')
wikEd.InitImage = function(target, source) {
for (var key in source) {
if (typeof(target[key]) == 'undefined') {
// remove MediaWiki path prefixes and add local path
if (wikEd.config.useLocalImages == true) {
target[key] = wikEd.config.imagePathLocal + source[key].replace(/^[0-9a-f]+\/[0-9a-f]+\/()/, '');
}
// add path
else {
target[key] = wikEd.config.imagePath + source[key];
}
}
}
return;
};
//
// wikEd.StyleSheet: create a new style sheet object (code copied from wikEd.js)
//
if (typeof(wikEd.StyleSheet) == 'undefined')
wikEd.StyleSheet = function(contextObj) {
if (contextObj == null) {
contextObj = document;
}
this.styleElement = null;
// MS IE compatibility
if (contextObj.createStyleSheet) {
this.styleElement = contextObj.createStyleSheet();
}
// standards compliant browsers
else {
this.styleElement = contextObj.createElement('style');
this.styleElement.from = 'text/css';
var insert = contextObj.getElementsByTagName('head')[0];
if (insert != null) {
this.styleElement.appendChild(contextObj.createTextNode('')); // Safari 3 fix
insert.appendChild(this.styleElement);
}
}
//
// wikEd.StyleSheet.AddRule: add one rule at the time using DOM method, very slow
//
this.AddRule = function(selector, declaration) {
// MS IE compatibility
if (this.styleElement.AddRule != null) {
if (declaration.length > 0) {
this.styleElement.AddRule(selector, declaration);
}
}
// standards compliant browsers
else {
if (this.styleElement.sheet != null) {
if (this.styleElement.sheet.insertRule != null) {
this.styleElement.sheet.insertRule(selector + ' { ' + declaration + ' } ', 0);
}
}
}
};
//
// wikEd.StyleSheet.AddRules: add or replace all rules at once, much faster
//
this.AddRules = function(rules) {
// MS IE compatibility
if (this.styleElement.innerHTML == null) {
this.styleElement.cssText = rules;
}
// Safari, Chrome, WebKit
else if ( (wikEd.safari == true) || (wikEd.chrome == true) || (wikEd.webkit == true) ) {
if (this.styleElement.firstChild != null) {
this.styleElement.removeChild(this.styleElement.firstChild);
}
this.styleElement.appendChild(contextObj.createTextNode(rules));
}
// via innerHTML
else {
this.styleElement.innerHTML = rules;
}
return;
};
};
//
// wikEd.GetPersistent: get a cookie or a Greasemonkey persistent value (code copied from wikEd.diff.js)
//
if (typeof(wikEd.GetPersistent) == 'undefined')
wikEd.GetPersistent = function(name) {
var getStr = '';
// check for web storage
if (wikEd.wikiGlobals.wikEdTypeofLocalStorage == 'object') {
wikEd.webStorage = true;
}
// get a value from web storage
if (wikEd.webStorage == true) {
getStr = window.localStorage.getItem(name);
}
// get a Greasemonkey persistent value
else if (wikEd.greasemonkey == true) {
getStr = GM_getValue(name, '');
}
// get a cookie value
else {
getStr = wikEd.GetCookie(name);
}
return(getStr);
};
//
// wikEd.GetCookie: get a cookie (code copied from wikEd.diff.js)
//
if (typeof(wikEd.GetCookie) == 'undefined')
wikEd.GetCookie = function(cookieName) {
var cookie = ' ' + document.cookie;
var search = ' ' + cookieName + '=';
var cookieValue = '';
var offset = 0;
var end = 0;
offset = cookie.indexOf(search);
if (offset != -1) {
offset += search.length;
end = cookie.indexOf(';', offset);
if (end == -1) {
end = cookie.length;
}
cookieValue = cookie.substring(offset, end);
cookieValue = cookieValue.replace(/\\+/g, ' ');
cookieValue = decodeURIComponent(cookieValue);
}
return(cookieValue);
};
//
// wikEd.AjaxRequest: cross browser wrapper for Ajax requests (code copied from wikEd.js)
//
if (typeof(wikEd.AjaxRequest) == 'undefined')
wikEd.AjaxRequest = function(requestMethod, requestUrl, postFields, overrideMimeType, ResponseHandler) {
var request;
// generate body data from form field object
var headerName = null;
var headerValue = null;
var bodyData = '';
if (requestMethod == 'POST') {
//create boundary
var boundary = wikEd.CreateRandomString(12);
// POST header
headerName = 'Content-Type';
headerValue = 'multipart/form-data; boundary=' + boundary;
// assemble body data
for (var fieldName in postFields) {
if (postFields.hasOwnProperty(fieldName) == true) {
var fieldValue = postFields[fieldName];
bodyData += '--' + boundary + '\r\n';
bodyData += 'Content-Disposition: form-data; name="' + fieldName + '"\r\n\r\n' + fieldValue + '\r\n';
}
}
bodyData += '--' + boundary + '--\r\n';
}
// use Greasemonkey GM_xmlhttpRequest
if (wikEd.greasemonkey == true) {
var headerObj = { 'User-Agent': navigator.userAgent };
if (headerName != null) {
headerObj[headerName] = headerValue;
}
// workaround for Error: Greasemonkey access violation: unsafeWindow cannot call GM_xmlhttpRequest.
// see http://wiki.greasespot.net/Greasemonkey_access_violation
setTimeout(function() {
new GM_xmlhttpRequest({
'method': requestMethod,
'url': requestUrl,
'overrideMimeType': overrideMimeType,
'headers': headerObj,
'data': bodyData,
'onreadystatechange':
function(ajax) {
if (ajax.readyState != 4) {
return;
}
ResponseHandler(ajax);
return;
}
});
}, 0);
}
// use standard XMLHttpRequest
else {
// allow ajax request from local copy for testing
if (wikEd.config.allowLocalAjax == true) {
if (typeof(netscape) == 'object') {
netscape.security.PrivilegeManager.enablePrivilege('UniversalBrowserRead');
}
}
// new ajax request object
if (typeof(XMLHttpRequest) == 'function') {
request = new XMLHttpRequest();
}
// IE
else if (typeof(ActiveXObject) == 'object') {
// IE 6
try {
request = new ActiveXObject('Microsoft.XMLHTTP');
}
// IE 5.5
catch(err) {
try {
request = new ActiveXObject('Msxml2.XMLHTTP');
}
catch(err) {
return;
}
}
}
if (request == null) {
return;
}
request.open(requestMethod, requestUrl, true);
if (headerName != null) {
request.setRequestHeader(headerName, headerValue);
}
if ( (request.overrideMimeType != null) && (overrideMimeType != null) ) {
request.overrideMimeType(overrideMimeType);
}
// catch security violations Opera 0.9.51
try {
request.send(bodyData);
}
catch(err) {
return;
}
request.onreadystatechange = function() {
if (request.readyState != 4) {
return;
}
ResponseHandler(request);
return;
};
}
return;
};
//
// wikEd.CreateRandomString: create random string of specified length and character set
//
if (typeof(wikEd.CreateRandomString) == 'undefined')
wikEd.CreateRandomString = function(strLength, charSet) {
if (charSet == null) {
charSet = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_';
}
var str = '';
for (var i = 0; i < strLength; i ++) {
str += charSet.charAt(Math.floor(Math.random() * charSet.length));
}
return(str);
}
//
// wikEd.GetOffsetTop: get element offset relative to window top (code copied from wikEd.js)
//
if (typeof(wikEd.GetOffsetTop) == 'undefined')
wikEd.GetOffsetTop = function(element) {
var offset = 0;
do {
offset += element.offsetTop;
} while ( (element = element.offsetParent) != null );
return(offset);
};
//
// wikEd.AddEventListener: wrapper for addEventListener (http://ejohn.org/projects/flexible-javascript-events/) (code copied from wikEd.js)
//
if (typeof(wikEd.AddEventListener) == 'undefined')
wikEd.AddEventListener = function(domElement, eventType, eventHandler, useCapture) {
if (domElement == null) {
return;
}
if (typeof(domElement.addEventListener) == 'function') {
domElement.addEventListener(eventType, eventHandler, useCapture);
}
else {
domElement['wikEd' + eventType + eventHandler] = eventHandler;
domElement[eventType + eventHandler] = function() {
var eventRootElement = document;
if (document.addEventListener == null) {
eventRootElement = window;
}
domElement['wikEd' + eventType + eventHandler](eventRootElement.event);
};
domElement.attachEvent('on' + eventType, domElement[eventType + eventHandler] );
}
return;
};
//
// wikEd.GetGlobals: copy global context variables into Greasemonkey context (code copied from wikEd.js)
// uses Greasemonkey postMessage, head script, and JSON encoding
if (typeof(wikEd.GetGlobals) == 'undefined')
wikEd.GetGlobals = function(names, gotGlobalsHook) {
if ( (typeof(window.postMessage) != 'function') || (typeof(JSON) != 'object') ) {
return;
}
// prepare code to be executed in global context
var globalScopeCode = 'var globalObj = {};';
if (gotGlobalsHook != null) {
wikEd.gotGlobalsHook.push(gotGlobalsHook);
globalScopeCode += 'globalObj.hookNumber = ' + (wikEd.gotGlobalsHook.length - 1) + ';';
}
globalScopeCode += 'globalObj.scriptId = \'wikEdGetGlobalScript' + wikEd.getGlobalsCounter + '\';';
globalScopeCode += 'globalObj.wikEdGetGlobals = {};';
// add global scope variables
for (var i = 0; i < names.length; i ++) {
globalScopeCode += ''
+ 'if (typeof(' + names[i] + ') != \'undefined\') {'
+ ' globalObj.wikEdGetGlobals[\'' + names[i] + '\'] = ' + names[i] + ';'
+ '}';
}
globalScopeCode += 'var globalObjStr = JSON.stringify(globalObj);';
if (window.___location.host != '') {
globalScopeCode += 'window.postMessage(globalObjStr, \'' + window.___location.protocol + '//' + window.___location.host + '\');'
}
else {
globalScopeCode += 'window.postMessage(globalObjStr, \'*\');';
}
// create head script to execute the code
var script = document.createElement('script');
script.id = 'wikEdGetGlobalScript' + wikEd.getGlobalsCounter;
wikEd.getGlobalsCounter ++;
script.innerHTML = globalScopeCode;
wikEd.head.appendChild(script);
return;
};
//
// wikEd.GetGlobalsReceiver: event handler for wikEd.GetGlobals postMessage (code copied from wikEd.js)
//
if (typeof(wikEd.GetGlobalsReceiver) == 'undefined')
wikEd.GetGlobalsReceiver = function(event) {
if (event.source != window) {
return;
}
if ( (event.origin != 'null') && (event.origin != null) && (event.origin != window.___location.protocol + '//' + window.___location.host) ) {
return;
}
if (event.data != '') {
var globalObj = JSON.parse(event.data);
var globals = globalObj.wikEdGetGlobals;
if (globals != null) {
for (var key in globals) {
if (globals.hasOwnProperty(key) == true) {
wikEd.wikiGlobals[key] = globals[key];
}
}
// run scheduled functions only once
if (globalObj.hookNumber != null) {
wikEd.ExecuteHook(wikEd.gotGlobalsHook[globalObj.hookNumber], true);
}
// clean up head script
var script = document.getElementById(globalObj.scriptId);
wikEd.head.removeChild(script);
var script = document.getElementById(globalObj.scriptId);
}
}
return;
}
//
// wikEd.ExecuteHook: executes scheduled custom functions from functionsHook array (code copied from wikEd.js)
//
if (typeof(wikEd.ExecuteHook) == 'undefined')
wikEd.ExecuteHook = function(functionsHook, onlyOnce) {
for (var i = 0; i < functionsHook.length; i ++) {
functionsHook[i]();
}
if (onlyOnce == true) {
functionsHook = [];
}
return;
};
// call startup
wikEd.DiffStartup();
// </nowiki></pre>
// <pre><nowiki>
/*
Name: wDiff.js
Version: 0.9.8 (March 20, 2010)
Info: http://en.wikipedia.org/wiki/User:Cacycle/diff
Code: http://en.wikipedia.org/wiki/User:Cacycle/diff.js
JavaScript diff algorithm by [[en:User:Cacycle]] (http://en.wikipedia.org/wiki/User_talk:Cacycle).
Outputs html/css-formatted new text with highlighted deletions, inserts, and block moves.
For newline highlighting the following style rules have to be added to the document:
.wDiffParagraph:before { content: "¶"; };
The program uses cross-browser code and should work with all modern browsers. It has been tested with:
* Mozilla Firefox 1.5.0.1
* Mozilla SeaMonkey 1.0
* Opera 8.53
* Internet Explorer 6.0.2900.2180
* Internet Explorer 7.0.5730.11
This program is also compatibel with Greasemonkey
An implementation of the word-based algorithm from:
Communications of the ACM 21(4):264 (1978)
http://doi.acm.org/10.1145/359460.359467
With the following additional feature:
* Word types have been optimized for MediaWiki source texts
* Additional post-pass 5 code for resolving islands caused by adding
two common words at the end of sequences of common words
* Additional detection of block borders and color coding of moved blocks and their original position
* Optional "intelligent" omission of unchanged parts from the output
This code is used by the MediaWiki in-browser text editors [[en:User:Cacycle/editor]] and [[en:User:Cacycle/wikEd]]
and the enhanced diff view tool wikEdDiff [[en:User:Cacycle/wikEd]].
Usage: var htmlText = WDiffString(oldText, newText);
This code has been released into the public ___domain.
Datastructures (abbreviations from publication):
text: an object that holds all text related datastructures
.newWords: consecutive words of the new text (N)
.oldWords: consecutive words of the old text (O)
.newToOld: array pointing to corresponding word number in old text (NA)
.oldToNew: array pointing to corresponding word number in new text (OA)
.message: output message for testing purposes
symbol table:
symbols[word]: associative array (object) of detected words for passes 1 - 3, points to symbol[i]
symbol[i]: array of objects that hold word counters and pointers:
.newCtr: new word occurences counter (NC)
.oldCtr: old word occurences counter (OC)
.toNew: first word occurrence in new text, points to text.newWords[i]
.toOld: last word occurrence in old text, points to text.oldWords[i]
block: an object that holds block move information
blocks indexed after new text:
.newStart: new text word number of start of this block
.newLength: element number of this block including non-words
.newWords: true word number of this block
.newNumber: corresponding block index in old text
.newBlock: moved-block-number of a block that has been moved here
.newLeft: moved-block-number of a block that has been moved from this border leftwards
.newRight: moved-block-number of a block that has been moved from this border rightwards
.newLeftIndex: index number of a block that has been moved from this border leftwards
.newRightIndex: index number of a block that has been moved from this border rightwards
blocks indexed after old text:
.oldStart: word number of start of this block
.oldToNew: corresponding new text word number of start
.oldLength: element number of this block including non-words
.oldWords: true word number of this block
*/
// css for change indicators
if (typeof(wDiffStyleDelete) == 'undefined') { window.wDiffStyleDelete = 'font-weight: normal; text-decoration: none; color: #fff; background-color: #990033;'; }
if (typeof(wDiffStyleInsert) == 'undefined') { window.wDiffStyleInsert = 'font-weight: normal; text-decoration: none; color: #fff; background-color: #009933;'; }
if (typeof(wDiffStyleMoved) == 'undefined') { window.wDiffStyleMoved = 'font-weight: bold; color: #000; vertical-align: text-bottom; font-size: xx-small; padding: 0; border: solid 1px;'; }
if (typeof(wDiffStyleBlock) == 'undefined') { window.wDiffStyleBlock = [
'color: #000; background-color: #ffff80;',
'color: #000; background-color: #c0ffff;',
'color: #000; background-color: #ffd0f0;',
'color: #000; background-color: #ffe080;',
'color: #000; background-color: #aaddff;',
'color: #000; background-color: #ddaaff;',
'color: #000; background-color: #ffbbbb;',
'color: #000; background-color: #d8ffa0;',
'color: #000; background-color: #d0d0d0;'
]; }
// html for change indicators, {number} is replaced by the block number
// {block} is replaced by the block style, class and html comments are important for shortening the output
if (typeof(wDiffHtmlMovedRight) == 'undefined') { window.wDiffHtmlMovedRight = '<input class="wDiffHtmlMovedRight" type="button" value=">" style="' + wDiffStyleMoved + ' {block}"><!--wDiffHtmlMovedRight-->'; }
if (typeof(wDiffHtmlMovedLeft) == 'undefined') { window.wDiffHtmlMovedLeft = '<input class="wDiffHtmlMovedLeft" type="button" value="<" style="' + wDiffStyleMoved + ' {block}"><!--wDiffHtmlMovedLeft-->'; }
if (typeof(wDiffHtmlBlockStart) == 'undefined') { window.wDiffHtmlBlockStart = '<span class="wDiffHtmlBlock" style="{block}">'; }
if (typeof(wDiffHtmlBlockEnd) == 'undefined') { window.wDiffHtmlBlockEnd = '</span><!--wDiffHtmlBlock-->'; }
if (typeof(wDiffHtmlDeleteStart) == 'undefined') { window.wDiffHtmlDeleteStart = '<span class="wDiffHtmlDelete" style="' + wDiffStyleDelete + '">'; }
if (typeof(wDiffHtmlDeleteEnd) == 'undefined') { window.wDiffHtmlDeleteEnd = '</span><!--wDiffHtmlDelete-->'; }
if (typeof(wDiffHtmlInsertStart) == 'undefined') { window.wDiffHtmlInsertStart = '<span class="wDiffHtmlInsert" style="' + wDiffStyleInsert + '">'; }
if (typeof(wDiffHtmlInsertEnd) == 'undefined') { window.wDiffHtmlInsertEnd = '</span><!--wDiffHtmlInsert-->'; }
// minimal number of real words for a moved block (0 for always displaying block move indicators)
if (typeof(wDiffBlockMinLength) == 'undefined') { window.wDiffBlockMinLength = 3; }
// exclude identical sequence starts and endings from change marking
if (typeof(wDiffWordDiff) == 'undefined') { window.wDiffWordDiff = true; }
// enable recursive diff to resolve problematic sequences
if (typeof(wDiffRecursiveDiff) == 'undefined') { window.wDiffRecursiveDiff = true; }
// enable block move display
if (typeof(wDiffShowBlockMoves) == 'undefined') { window.wDiffShowBlockMoves = true; }
// remove unchanged parts from final output
// characters before diff tag to search for previous heading, paragraph, line break, cut characters
if (typeof(wDiffHeadingBefore) == 'undefined') { window.wDiffHeadingBefore = 1500; }
if (typeof(wDiffParagraphBefore) == 'undefined') { window.wDiffParagraphBefore = 1500; }
if (typeof(wDiffLineBeforeMax) == 'undefined') { window.wDiffLineBeforeMax = 1000; }
if (typeof(wDiffLineBeforeMin) == 'undefined') { window.wDiffLineBeforeMin = 500; }
if (typeof(wDiffBlankBeforeMax) == 'undefined') { window.wDiffBlankBeforeMax = 1000; }
if (typeof(wDiffBlankBeforeMin) == 'undefined') { window.wDiffBlankBeforeMin = 500; }
if (typeof(wDiffCharsBefore) == 'undefined') { window.wDiffCharsBefore = 500; }
// characters after diff tag to search for next heading, paragraph, line break, or characters
if (typeof(wDiffHeadingAfter) == 'undefined') { window.wDiffHeadingAfter = 1500; }
if (typeof(wDiffParagraphAfter) == 'undefined') { window.wDiffParagraphAfter = 1500; }
if (typeof(wDiffLineAfterMax) == 'undefined') { window.wDiffLineAfterMax = 1000; }
if (typeof(wDiffLineAfterMin) == 'undefined') { window.wDiffLineAfterMin = 500; }
if (typeof(wDiffBlankAfterMax) == 'undefined') { window.wDiffBlankAfterMax = 1000; }
if (typeof(wDiffBlankAfterMin) == 'undefined') { window.wDiffBlankAfterMin = 500; }
if (typeof(wDiffCharsAfter) == 'undefined') { window.wDiffCharsAfter = 500; }
// maximal fragment distance to join close fragments
if (typeof(wDiffFragmentJoin) == 'undefined') { window.wDiffFragmentJoin = 1000; }
if (typeof(wDiffOmittedChars) == 'undefined') { window.wDiffOmittedChars = '…'; }
if (typeof(wDiffOmittedLines) == 'undefined') { window.wDiffOmittedLines = '<hr style="height: 2px; margin: 1em 10%;">'; }
if (typeof(wDiffNoChange) == 'undefined') { window.wDiffNoChange = '<hr style="height: 2px; margin: 1em 20%;">'; }
// compatibility fix for old name of main function
window.StringDiff = window.WDiffString;
// WDiffString: main program
// input: oldText, newText, strings containing the texts
// returns: html diff
window.WDiffString = function(oldText, newText) {
// IE / Mac fix
oldText = oldText.replace(/\r\n?/g, '\n');
newText = newText.replace(/\r\n?/g, '\n');
var text = {};
text.newWords = [];
text.oldWords = [];
text.newToOld = [];
text.oldToNew = [];
text.message = '';
var block = {};
var outText = '';
// trap trivial changes: no change
if (oldText == newText) {
outText = newText;
outText = WDiffEscape(outText);
outText = WDiffHtmlFormat(outText);
return(outText);
}
// trap trivial changes: old text deleted
if ( (oldText == null) || (oldText.length == 0) ) {
outText = newText;
outText = WDiffEscape(outText);
outText = WDiffHtmlFormat(outText);
outText = wDiffHtmlInsertStart + outText + wDiffHtmlInsertEnd;
return(outText);
}
// trap trivial changes: new text deleted
if ( (newText == null) || (newText.length == 0) ) {
outText = oldText;
outText = WDiffEscape(outText);
outText = WDiffHtmlFormat(outText);
outText = wDiffHtmlDeleteStart + outText + wDiffHtmlDeleteEnd;
return(outText);
}
// split new and old text into words
WDiffSplitText(oldText, newText, text);
// calculate diff information
WDiffText(text);
//detect block borders and moved blocks
WDiffDetectBlocks(text, block);
// process diff data into formatted html text
outText = WDiffToHtml(text, block);
// IE fix
outText = outText.replace(/> ( *)</g, '> $1<');
return(outText);
};
// WDiffSplitText: split new and old text into words
// input: oldText, newText, strings containing the texts
// changes: text.newWords and text.oldWords, arrays containing the texts in arrays of words
window.WDiffSplitText = function(oldText, newText, text) {
// convert strange spaces
oldText = oldText.replace(/[\t\u000b\u00a0\u2028\u2029]+/g, ' ');
newText = newText.replace(/[\t\u000b\u00a0\u2028\u2029]+/g, ' ');
// split old text into words
// / | | | | | | | | | | | | | | /
var pattern = /[\w]+|\[\[|\]\]|\{\{|\}\}|\n+| +|&\w+;|'''|''|=+|\{\||\|\}|\|\-|./g;
var result;
do {
result = pattern.exec(oldText);
if (result != null) {
text.oldWords.push(result[0]);
}
} while (result != null);
// split new text into words
do {
result = pattern.exec(newText);
if (result != null) {
text.newWords.push(result[0]);
}
} while (result != null);
return;
};
// WDiffText: calculate diff information
// input: text.newWords and text.oldWords, arrays containing the texts as arrays of words
// optionally for recursive calls: newStart, newEnd, oldStart, oldEnd, recursionLevel
// changes: text.newToOld and text.oldToNew, arrays pointing to corresponding words
window.WDiffText = function(text, newStart, newEnd, oldStart, oldEnd, recursionLevel) {
var symbol = [];
var symbols = {};
// set defaults
if (typeof(newStart) == 'undefined') { newStart = 0; }
if (typeof(newEnd) == 'undefined') { newEnd = text.newWords.length; }
if (typeof(oldStart) == 'undefined') { oldStart = 0; }
if (typeof(oldEnd) == 'undefined') { oldEnd = text.oldWords.length; }
if (typeof(recursionLevel) == 'undefined') { recursionLevel = 0; }
// limit recursion depth
if (recursionLevel > 10) {
return;
}
//
// pass 1: Parse new text into symbol table
//
for (var i = newStart; i < newEnd; i ++) {
var word = text.newWords[i];
// preserve the native method
if (word.indexOf('hasOwnProperty') == 0) {
word = word.replace(/^(hasOwnProperty_*)$/, '$1_');
}
// add new entry to symbol table
if (symbols.hasOwnProperty(word) == false) {
var last = symbol.length;
symbols[word] = last;
symbol[last] = { newCtr: 1, oldCtr: 0, toNew: i, toOld: null };
}
// or update existing entry
else {
// increment word counter for new text
var hashToArray = symbols[word];
symbol[hashToArray].newCtr ++;
}
}
//
// pass 2: parse old text into symbol table
//
for (var i = oldStart; i < oldEnd; i ++) {
var word = text.oldWords[i];
// preserve the native method
if (word.indexOf('hasOwnProperty') == 0) {
word = word.replace(/^(hasOwnProperty_*)$/, '$1_');
}
// add new entry to symbol table
if (symbols.hasOwnProperty(word) == false) {
var last = symbol.length;
symbols[word] = last;
symbol[last] = { newCtr: 0, oldCtr: 1, toNew: null, toOld: i };
}
// or update existing entry
else {
// increment word counter for old text
var hashToArray = symbols[word];
symbol[hashToArray].oldCtr ++;
// add word number for old text
symbol[hashToArray].toOld = i;
}
}
//
// pass 3: connect unique words
//
for (var i = 0; i < symbol.length; i ++) {
// find words in the symbol table that occur only once in both versions
if ( (symbol[i].newCtr == 1) && (symbol[i].oldCtr == 1) ) {
var toNew = symbol[i].toNew;
var toOld = symbol[i].toOld;
// do not use spaces as unique markers
if (/^\s+$/.test(text.newWords[toNew]) == false) {
// connect from new to old and from old to new
text.newToOld[toNew] = toOld;
text.oldToNew[toOld] = toNew;
}
}
}
//
// pass 4: connect adjacent identical words downwards
//
for (var i = newStart; i < newEnd - 1; i ++) {
// find already connected pairs
if (text.newToOld[i] != null) {
var j = text.newToOld[i];
// check if the following words are not yet connected
if ( (text.newToOld[i + 1] == null) && (text.oldToNew[j + 1] == null) ) {
// connect if the following words are the same
if (text.newWords[i + 1] == text.oldWords[j + 1]) {
text.newToOld[i + 1] = j + 1;
text.oldToNew[j + 1] = i + 1;
}
}
}
}
//
// pass 5: connect adjacent identical words upwards
//
for (var i = newEnd - 1; i > newStart; i --) {
// find already connected pairs
if (text.newToOld[i] != null) {
var j = text.newToOld[i];
// check if the preceeding words are not yet connected
if ( (text.newToOld[i - 1] == null) && (text.oldToNew[j - 1] == null) ) {
// connect if the preceeding words are the same
if ( text.newWords[i - 1] == text.oldWords[j - 1] ) {
text.newToOld[i - 1] = j - 1;
text.oldToNew[j - 1] = i - 1;
}
}
}
}
//
// "pass" 6: recursively diff still unresolved regions downwards
//
if (wDiffRecursiveDiff == true) {
var i = newStart;
var j = oldStart;
while (i < newEnd) {
if (text.newToOld[i - 1] != null) {
j = text.newToOld[i - 1] + 1;
}
// check for the start of an unresolved sequence
if ( (text.newToOld[i] == null) && (text.oldToNew[j] == null) ) {
// determine the ends of the sequences
var iStart = i;
var iEnd = i;
while ( (text.newToOld[iEnd] == null) && (iEnd < newEnd) ) {
iEnd ++;
}
var iLength = iEnd - iStart;
var jStart = j;
var jEnd = j;
while ( (text.oldToNew[jEnd] == null) && (jEnd < oldEnd) ) {
jEnd ++;
}
var jLength = jEnd - jStart;
// recursively diff the unresolved sequence
if ( (iLength > 0) && (jLength > 0) ) {
if ( (iLength > 1) || (jLength > 1) ) {
if ( (iStart != newStart) || (iEnd != newEnd) || (jStart != oldStart) || (jEnd != oldEnd) ) {
WDiffText(text, iStart, iEnd, jStart, jEnd, recursionLevel + 1);
}
}
}
i = iEnd;
}
else {
i ++;
}
}
}
//
// "pass" 7: recursively diff still unresolved regions upwards
//
if (wDiffRecursiveDiff == true) {
var i = newEnd - 1;
var j = oldEnd - 1;
while (i >= newStart) {
if (text.newToOld[i + 1] != null) {
j = text.newToOld[i + 1] - 1;
}
// check for the start of an unresolved sequence
if ( (text.newToOld[i] == null) && (text.oldToNew[j] == null) ) {
// determine the ends of the sequences
var iStart = i;
var iEnd = i + 1;
while ( (text.newToOld[iStart - 1] == null) && (iStart >= newStart) ) {
iStart --;
}
if (iStart < 0) {
iStart = 0;
}
var iLength = iEnd - iStart;
var jStart = j;
var jEnd = j + 1;
while ( (text.oldToNew[jStart - 1] == null) && (jStart >= oldStart) ) {
jStart --;
}
if (jStart < 0) {
jStart = 0;
}
var jLength = jEnd - jStart;
// recursively diff the unresolved sequence
if ( (iLength > 0) && (jLength > 0) ) {
if ( (iLength > 1) || (jLength > 1) ) {
if ( (iStart != newStart) || (iEnd != newEnd) || (jStart != oldStart) || (jEnd != oldEnd) ) {
WDiffText(text, iStart, iEnd, jStart, jEnd, recursionLevel + 1);
}
}
}
i = iStart - 1;
}
else {
i --;
}
}
}
return;
};
// WDiffToHtml: process diff data into formatted html text
// input: text.newWords and text.oldWords, arrays containing the texts in arrays of words
// text.newToOld and text.oldToNew, arrays pointing to corresponding words
// block data structure
// returns: outText, a html string
window.WDiffToHtml = function(text, block) {
var outText = text.message;
var blockNumber = 0;
var i = 0;
var j = 0;
var movedAsInsertion;
// cycle through the new text
do {
var movedIndex = [];
var movedBlock = [];
var movedLeft = [];
var blockText = '';
var identText = '';
var delText = '';
var insText = '';
var identStart = '';
// check if a block ends here and finish previous block
if (movedAsInsertion != null) {
if (movedAsInsertion == false) {
identStart += wDiffHtmlBlockEnd;
}
else {
identStart += wDiffHtmlInsertEnd;
}
movedAsInsertion = null;
}
// detect block boundary
if ( (text.newToOld[i] != j) || (blockNumber == 0 ) ) {
if ( ( (text.newToOld[i] != null) || (i >= text.newWords.length) ) && ( (text.oldToNew[j] != null) || (j >= text.oldWords.length) ) ) {
// block moved right
var moved = block.newRight[blockNumber];
if (moved > 0) {
var index = block.newRightIndex[blockNumber];
movedIndex.push(index);
movedBlock.push(moved);
movedLeft.push(false);
}
// block moved left
moved = block.newLeft[blockNumber];
if (moved > 0) {
var index = block.newLeftIndex[blockNumber];
movedIndex.push(index);
movedBlock.push(moved);
movedLeft.push(true);
}
// check if a block starts here
moved = block.newBlock[blockNumber];
if (moved > 0) {
// mark block as inserted text
if (block.newWords[blockNumber] < wDiffBlockMinLength) {
identStart += wDiffHtmlInsertStart;
movedAsInsertion = true;
}
// mark block by color
else {
if (moved > wDiffStyleBlock.length) {
moved = wDiffStyleBlock.length;
}
identStart += WDiffHtmlCustomize(wDiffHtmlBlockStart, moved - 1);
movedAsInsertion = false;
}
}
if (i >= text.newWords.length) {
i ++;
}
else {
j = text.newToOld[i];
blockNumber ++;
}
}
}
// get the correct order if moved to the left as well as to the right from here
if (movedIndex.length == 2) {
if (movedIndex[0] > movedIndex[1]) {
movedIndex.reverse();
movedBlock.reverse();
movedLeft.reverse();
}
}
// handle left and right block moves from this position
for (var m = 0; m < movedIndex.length; m ++) {
// insert the block as deleted text
if (block.newWords[ movedIndex[m] ] < wDiffBlockMinLength) {
var movedStart = block.newStart[ movedIndex[m] ];
var movedLength = block.newLength[ movedIndex[m] ];
var str = '';
for (var n = movedStart; n < movedStart + movedLength; n ++) {
str += text.newWords[n];
}
str = WDiffEscape(str);
str = str.replace(/\n/g, '<span class="wDiffParagraph"></span><br>');
blockText += wDiffHtmlDeleteStart + str + wDiffHtmlDeleteEnd;
}
// add a placeholder / move direction indicator
else {
if (movedBlock[m] > wDiffStyleBlock.length) {
movedBlock[m] = wDiffStyleBlock.length;
}
if (movedLeft[m]) {
blockText += WDiffHtmlCustomize(wDiffHtmlMovedLeft, movedBlock[m] - 1);
}
else {
blockText += WDiffHtmlCustomize(wDiffHtmlMovedRight, movedBlock[m] - 1);
}
}
}
// collect consecutive identical text
while ( (i < text.newWords.length) && (j < text.oldWords.length) ) {
if ( (text.newToOld[i] == null) || (text.oldToNew[j] == null) ) {
break;
}
if (text.newToOld[i] != j) {
break;
}
identText += text.newWords[i];
i ++;
j ++;
}
// collect consecutive deletions
while ( (text.oldToNew[j] == null) && (j < text.oldWords.length) ) {
delText += text.oldWords[j];
j ++;
}
// collect consecutive inserts
while ( (text.newToOld[i] == null) && (i < text.newWords.length) ) {
insText += text.newWords[i];
i ++;
}
// remove leading and trailing similarities between delText and ins from highlighting
var preText = '';
var postText = '';
if (wDiffWordDiff) {
if ( (delText != '') && (insText != '') ) {
// remove leading similarities
while ( delText.charAt(0) == insText.charAt(0) && (delText != '') && (insText != '') ) {
preText = preText + delText.charAt(0);
delText = delText.substr(1);
insText = insText.substr(1);
}
// remove trailing similarities
while ( delText.charAt(delText.length - 1) == insText.charAt(insText.length - 1) && (delText != '') && (insText != '') ) {
postText = delText.charAt(delText.length - 1) + postText;
delText = delText.substr(0, delText.length - 1);
insText = insText.substr(0, insText.length - 1);
}
}
}
// output the identical text, deletions and inserts
// moved from here indicator
if (blockText != '') {
outText += blockText;
}
// identical text
if (identText != '') {
outText += identStart + WDiffEscape(identText);
}
outText += preText;
// deleted text
if (delText != '') {
delText = wDiffHtmlDeleteStart + WDiffEscape(delText) + wDiffHtmlDeleteEnd;
delText = delText.replace(/\n/g, '<span class="wDiffParagraph"></span><br>');
outText += delText;
}
// inserted text
if (insText != '') {
insText = wDiffHtmlInsertStart + WDiffEscape(insText) + wDiffHtmlInsertEnd;
insText = insText.replace(/\n/g, '<span class="wDiffParagraph"></span><br>');
outText += insText;
}
outText += postText;
} while (i <= text.newWords.length);
outText += '\n';
outText = WDiffHtmlFormat(outText);
return(outText);
};
// WDiffEscape: replaces html-sensitive characters in output text with character entities
window.WDiffEscape = function(text) {
text = text.replace(/&/g, '&');
text = text.replace(/</g, '<');
text = text.replace(/>/g, '>');
text = text.replace(/\"/g, '"');
return(text);
};
// HtmlCustomize: customize indicator html: replace {number} with the block number, {block} with the block style
window.WDiffHtmlCustomize = function(text, block) {
text = text.replace(/\{number\}/, block);
text = text.replace(/\{block\}/, wDiffStyleBlock[block]);
return(text);
};
// HtmlFormat: replaces newlines and multiple spaces in text with html code
window.WDiffHtmlFormat = function(text) {
text = text.replace(/ {2}/g, ' ');
text = text.replace(/\n/g, '<br>');
return(text);
};
// WDiffDetectBlocks: detect block borders and moved blocks
// input: text object, block object
window.WDiffDetectBlocks = function(text, block) {
block.oldStart = [];
block.oldToNew = [];
block.oldLength = [];
block.oldWords = [];
block.newStart = [];
block.newLength = [];
block.newWords = [];
block.newNumber = [];
block.newBlock = [];
block.newLeft = [];
block.newRight = [];
block.newLeftIndex = [];
block.newRightIndex = [];
var blockNumber = 0;
var wordCounter = 0;
var realWordCounter = 0;
// get old text block order
if (wDiffShowBlockMoves) {
var j = 0;
var i = 0;
do {
// detect block boundaries on old text
if ( (text.oldToNew[j] != i) || (blockNumber == 0 ) ) {
if ( ( (text.oldToNew[j] != null) || (j >= text.oldWords.length) ) && ( (text.newToOld[i] != null) || (i >= text.newWords.length) ) ) {
if (blockNumber > 0) {
block.oldLength[blockNumber - 1] = wordCounter;
block.oldWords[blockNumber - 1] = realWordCounter;
wordCounter = 0;
realWordCounter = 0;
}
if (j >= text.oldWords.length) {
j ++;
}
else {
i = text.oldToNew[j];
block.oldStart[blockNumber] = j;
block.oldToNew[blockNumber] = text.oldToNew[j];
blockNumber ++;
}
}
}
// jump over identical pairs
while ( (i < text.newWords.length) && (j < text.oldWords.length) ) {
if ( (text.newToOld[i] == null) || (text.oldToNew[j] == null) ) {
break;
}
if (text.oldToNew[j] != i) {
break;
}
i ++;
j ++;
wordCounter ++;
if ( /\w/.test( text.newWords[i] ) ) {
realWordCounter ++;
}
}
// jump over consecutive deletions
while ( (text.oldToNew[j] == null) && (j < text.oldWords.length) ) {
j ++;
}
// jump over consecutive inserts
while ( (text.newToOld[i] == null) && (i < text.newWords.length) ) {
i ++;
}
} while (j <= text.oldWords.length);
// get the block order in the new text
var lastMin;
var currMinIndex;
lastMin = null;
// sort the data by increasing start numbers into new text block info
for (var i = 0; i < blockNumber; i ++) {
currMin = null;
for (var j = 0; j < blockNumber; j ++) {
curr = block.oldToNew[j];
if ( (curr > lastMin) || (lastMin == null) ) {
if ( (curr < currMin) || (currMin == null) ) {
currMin = curr;
currMinIndex = j;
}
}
}
block.newStart[i] = block.oldToNew[currMinIndex];
block.newLength[i] = block.oldLength[currMinIndex];
block.newWords[i] = block.oldWords[currMinIndex];
block.newNumber[i] = currMinIndex;
lastMin = currMin;
}
// detect not moved blocks
for (var i = 0; i < blockNumber; i ++) {
if (block.newBlock[i] == null) {
if (block.newNumber[i] == i) {
block.newBlock[i] = 0;
}
}
}
// detect switches of neighbouring blocks
for (var i = 0; i < blockNumber - 1; i ++) {
if ( (block.newBlock[i] == null) && (block.newBlock[i + 1] == null) ) {
if (block.newNumber[i] - block.newNumber[i + 1] == 1) {
if ( (block.newNumber[i + 1] - block.newNumber[i + 2] != 1) || (i + 2 >= blockNumber) ) {
// the shorter one is declared the moved one
if (block.newLength[i] < block.newLength[i + 1]) {
block.newBlock[i] = 1;
block.newBlock[i + 1] = 0;
}
else {
block.newBlock[i] = 0;
block.newBlock[i + 1] = 1;
}
}
}
}
}
// mark all others as moved and number the moved blocks
j = 1;
for (var i = 0; i < blockNumber; i ++) {
if ( (block.newBlock[i] == null) || (block.newBlock[i] == 1) ) {
block.newBlock[i] = j++;
}
}
// check if a block has been moved from this block border
for (var i = 0; i < blockNumber; i ++) {
for (var j = 0; j < blockNumber; j ++) {
if (block.newNumber[j] == i) {
if (block.newBlock[j] > 0) {
// block moved right
if (block.newNumber[j] < j) {
block.newRight[i] = block.newBlock[j];
block.newRightIndex[i] = j;
}
// block moved left
else {
block.newLeft[i + 1] = block.newBlock[j];
block.newLeftIndex[i + 1] = j;
}
}
}
}
}
}
return;
};
// WDiffShortenOutput: remove unchanged parts from final output
// input: the output of WDiffString
// returns: the text with removed unchanged passages indicated by (...)
window.WDiffShortenOutput = function(diffText) {
// html <br/> to newlines
diffText = diffText.replace(/<br[^>]*>/g, '\n');
// scan for diff html tags
var regExpDiff = new RegExp('<\\w+ class=\\"(\\w+)\\"[^>]*>(.|\\n)*?<!--\\1-->', 'g');
var tagStart = [];
var tagEnd = [];
var i = 0;
var found;
while ( (found = regExpDiff.exec(diffText)) != null ) {
// combine consecutive diff tags
if ( (i > 0) && (tagEnd[i - 1] == found.index) ) {
tagEnd[i - 1] = found.index + found[0].length;
}
else {
tagStart[i] = found.index;
tagEnd[i] = found.index + found[0].length;
i ++;
}
}
// no diff tags detected
if (tagStart.length == 0) {
return(wDiffNoChange);
}
// define regexps
var regExpHeading = new RegExp('\\n=+.+?=+ *\\n|\\n\\{\\||\\n\\|\\}', 'g');
var regExpParagraph = new RegExp('\\n\\n+', 'g');
var regExpLine = new RegExp('\\n+', 'g');
var regExpBlank = new RegExp('(<[^>]+>)*\\s+', 'g');
// determine fragment border positions around diff tags
var rangeStart = [];
var rangeEnd = [];
var rangeStartType = [];
var rangeEndType = [];
for (var i = 0; i < tagStart.length; i ++) {
var found;
// find last heading before diff tag
var lastPos = tagStart[i] - wDiffHeadingBefore;
if (lastPos < 0) {
lastPos = 0;
}
regExpHeading.lastIndex = lastPos;
while ( (found = regExpHeading.exec(diffText)) != null ) {
if (found.index > tagStart[i]) {
break;
}
rangeStart[i] = found.index;
rangeStartType[i] = 'heading';
}
// find last paragraph before diff tag
if (rangeStart[i] == null) {
lastPos = tagStart[i] - wDiffParagraphBefore;
if (lastPos < 0) {
lastPos = 0;
}
regExpParagraph.lastIndex = lastPos;
while ( (found = regExpParagraph.exec(diffText)) != null ) {
if (found.index > tagStart[i]) {
break;
}
rangeStart[i] = found.index;
rangeStartType[i] = 'paragraph';
}
}
// find line break before diff tag
if (rangeStart[i] == null) {
lastPos = tagStart[i] - wDiffLineBeforeMax;
if (lastPos < 0) {
lastPos = 0;
}
regExpLine.lastIndex = lastPos;
while ( (found = regExpLine.exec(diffText)) != null ) {
if (found.index > tagStart[i] - wDiffLineBeforeMin) {
break;
}
rangeStart[i] = found.index;
rangeStartType[i] = 'line';
}
}
// find blank before diff tag
if (rangeStart[i] == null) {
lastPos = tagStart[i] - wDiffBlankBeforeMax;
if (lastPos < 0) {
lastPos = 0;
}
regExpBlank.lastIndex = lastPos;
while ( (found = regExpBlank.exec(diffText)) != null ) {
if (found.index > tagStart[i] - wDiffBlankBeforeMin) {
break;
}
rangeStart[i] = found.index;
rangeStartType[i] = 'blank';
}
}
// fixed number of chars before diff tag
if (rangeStart[i] == null) {
rangeStart[i] = tagStart[i] - wDiffCharsBefore;
rangeStartType[i] = 'chars';
if (rangeStart[i] < 0) {
rangeStart[i] = 0;
}
}
// find first heading after diff tag
regExpHeading.lastIndex = tagEnd[i];
if ( (found = regExpHeading.exec(diffText)) != null ) {
if (found.index < tagEnd[i] + wDiffHeadingAfter) {
rangeEnd[i] = found.index + found[0].length;
rangeEndType[i] = 'heading';
}
}
// find first paragraph after diff tag
if (rangeEnd[i] == null) {
regExpParagraph.lastIndex = tagEnd[i];
if ( (found = regExpParagraph.exec(diffText)) != null ) {
if (found.index < tagEnd[i] + wDiffParagraphAfter) {
rangeEnd[i] = found.index;
rangeEndType[i] = 'paragraph';
}
}
}
// find first line break after diff tag
if (rangeEnd[i] == null) {
regExpLine.lastIndex = tagEnd[i] + wDiffLineAfterMin;
if ( (found = regExpLine.exec(diffText)) != null ) {
if (found.index < tagEnd[i] + wDiffLineAfterMax) {
rangeEnd[i] = found.index;
rangeEndType[i] = 'break';
}
}
}
// find blank after diff tag
if (rangeEnd[i] == null) {
regExpBlank.lastIndex = tagEnd[i] + wDiffBlankAfterMin;
if ( (found = regExpBlank.exec(diffText)) != null ) {
if (found.index < tagEnd[i] + wDiffBlankAfterMax) {
rangeEnd[i] = found.index;
rangeEndType[i] = 'blank';
}
}
}
// fixed number of chars after diff tag
if (rangeEnd[i] == null) {
rangeEnd[i] = tagEnd[i] + wDiffCharsAfter;
if (rangeEnd[i] > diffText.length) {
rangeEnd[i] = diffText.length;
rangeEndType[i] = 'chars';
}
}
}
// remove overlaps, join close fragments
var fragmentStart = [];
var fragmentEnd = [];
var fragmentStartType = [];
var fragmentEndType = [];
fragmentStart[0] = rangeStart[0];
fragmentEnd[0] = rangeEnd[0];
fragmentStartType[0] = rangeStartType[0];
fragmentEndType[0] = rangeEndType[0];
var j = 1;
for (var i = 1; i < rangeStart.length; i ++) {
if (rangeStart[i] > fragmentEnd[j - 1] + wDiffFragmentJoin) {
fragmentStart[j] = rangeStart[i];
fragmentEnd[j] = rangeEnd[i];
fragmentStartType[j] = rangeStartType[i];
fragmentEndType[j] = rangeEndType[i];
j ++;
}
else {
fragmentEnd[j - 1] = rangeEnd[i];
fragmentEndType[j - 1] = rangeEndType[i];
}
}
// assemble the fragments
var outText = '';
for (var i = 0; i < fragmentStart.length; i ++) {
// get text fragment
var fragment = diffText.substring(fragmentStart[i], fragmentEnd[i]);
var fragment = fragment.replace(/^\n+|\n+$/g, '');
// add inline marks for omitted chars and words
if (fragmentStart[i] > 0) {
if (fragmentStartType[i] == 'chars') {
fragment = wDiffOmittedChars + fragment;
}
else if (fragmentStartType[i] == 'blank') {
fragment = wDiffOmittedChars + ' ' + fragment;
}
}
if (fragmentEnd[i] < diffText.length) {
if (fragmentStartType[i] == 'chars') {
fragment = fragment + wDiffOmittedChars;
}
else if (fragmentStartType[i] == 'blank') {
fragment = fragment + ' ' + wDiffOmittedChars;
}
}
// add omitted line separator
if (fragmentStart[i] > 0) {
outText += wDiffOmittedLines;
}
// encapsulate span errors
outText += '<div>' + fragment + '</div>';
}
// add trailing omitted line separator
if (fragmentEnd[i - 1] < diffText.length) {
outText = outText + wDiffOmittedLines;
}
// remove leading and trailing empty lines
outText = outText.replace(/^(<div>)\n+|\n+(<\/div>)$/g, '$1$2');
// convert to html linebreaks
outText = outText.replace(/\n/g, '<br />');
return(outText);
};
// </nowiki></pre>
|