MediaWiki:Split!.js
Jump to navigation
Jump to search
Note: After saving, you have to bypass your browser's cache to see the changes. Internet Explorer: press Ctrl-F5, Mozilla: hold down Shift while clicking Reload (or press Ctrl-Shift-R), Opera/Konqueror: press F5, Safari: hold down Shift + Alt while clicking Reload, Chrome: hold down Shift while clicking Reload.
Documentation for this user script can be added at MediaWiki:Split!. This user script seems to have an accompanying .css page at MediaWiki:Split!.css. |
- Report page listing warnings and errors.
/**
* Split!
* A script for history splitting.
* Does not depend on any of Wikimedia Commons scripts
* --> Less stable, easier portable.
* Only tested with browser supporting CSS3 and HTML5.
*
* @author Rillke, 2014
*
* License: Choose one or more of the following:
* GPL v.2 and later, LGPL (all versions), CC-By-SA 3.0, GFDL, Apache License 2 and later.
*/
(function($, mw) {
var mwCfg = mw.config.get(['wgNamespaceNumber', 'wgArticleId', 'wgPageName']),
pageName = mwCfg.wgPageName.replace(/_/g, ' '),
title = new mw.Title(pageName);
function firstItem(o) { for (var i in o) { if (o.hasOwnProperty(i)) { return o[i]; } } }
/************************************
* Message logic
*/
var msgs = {
'com-split-tab-label': "Split!",
'com-split-tab-tooltip': "Split file",
'com-split-usage': "Click a separator and confirm to split the revision history. For pages with no text revision, a copy of the latest revision will be submitted. Note that you cannot split revisions having the same timestamp.",
'com-split-separator-tooltip': "split the revision history here",
'com-split-file-input-placeholder': "Destination file name",
'com-split-file-input-label': "Destination file name:",
'com-split-confirm-button': "Split now!"
};
mw.messages.set(msgs);
var msg = function(k) {
return mw.msg('com-split-' + k);
};
/**
* END: Message logic
***********************************/
var split = {
installLink: function() {
var $tab = $( mw.util.addPortletLink(
'p-cactions',
document.location + '#!action=split',
msg('tab-label'),
'ca-user-js-split',
msg('tab-tooltip'),
'',
document.getElementById('ca-edit')
) );
$tab.click(function(e) {
e.preventDefault();
split.createUI();
});
},
sortMerge: function(prop, first, second) {
// This could be more performant ...
var retVal = first.slice(0).concat(second);
retVal.sort(function(a, b) {
var aProp = a[prop],
bProp = b[prop];
if (aProp > bProp) {
return 1;
} else if(aProp === bProp) {
return 0;
} else {
return -1;
}
});
return retVal;
},
formatTS: function(ts) {
return ts.replace('T', ' ').replace('Z', '');
},
/* Format a number > 1 (1000 --> 1 000) */
formattNumber: function( iNr ) {
iNr += '';
var rx = /(\d+)(\d{3})/;
while (rx.test(iNr)) {
iNr = iNr.replace(rx, '$1' + '<span style="font-size:40%;font-weight:100"> </span>' + '$2');
}
return iNr;
},
formatSize: function(h, w, fs) {
return h + ' × ' + w + ' (' + split.formattNumber( fs >> 10 ) + ' <abbr title="1 KibiByte= 1024 Bytes">KiB</abbr>)';
},
createUI: function() {
mw.loader.using(['jquery.lengthLimit'], function() {
var $targetNode = $('#mw-content-text'),
$ui = split.getUI();
$ui.prependTo($targetNode);
split.initUI().done(function(r) {
var pg = firstItem(r.query.pages),
rvs = pg.revisions,
iis = pg.imageinfo,
merged = split.sortMerge('timestamp', rvs, iis);
$.each(merged, function(i, rev) {
if (rev.url) {
$ui.imgRevItem(rev);
} else {
$ui.textRevItem(rev);
}
});
}).fail(function() {
// TODO
});
});
},
getUI: function() {
var $ui = $('<div>')
.addClass('com-split-ui')
.text(msg('usage')),
$confirm = $('<button>')
.addClass('com-split-confirm')
.attr('type', 'button')
.text(msg('confirm-button'))
.appendTo($ui),
$separator = $('<a>')
.addClass('com-split-separator')
.attr('href', '#')
.attr('title', msg('separator-tooltip'))
.text(' '),
$textRevItem = $('<div>')
.addClass('com-split-textrev-item'),
$txtComment = $('<span>')
.addClass('com-split-txt-comment'),
$txtUser = $('<code>')
.addClass('com-split-txt-user'),
$txtTime = $('<span>')
.addClass('com-split-txt-time'),
$imageRevItem = $('<div>')
.addClass('com-split-imagerev-item'),
$imgImageContainer = $('<div>')
.addClass('com-split-img-img-container'),
$imgDescription = $('<div>')
.addClass('com-split-img-desc'),
$imgComment = $('<span>')
.addClass('com-split-img-comment'),
$imgTime = $('<span>')
.addClass('com-split-img-time'),
$imgImage = $('<img>')
.addClass('com-split-img-img'),
$imgSize = $('<span>')
.addClass('com-split-img-size'),
$imgMIME = $('<span>')
.addClass('com-split-img-mime'),
$imgUser = $('<code>')
.addClass('com-split-img-user'),
$fileName = $('<div>')
.addClass('com-split-filename')
.hide(),
$fileNameInputLabel = $('<span>')
.text(msg('file-input-label'))
.hide(),
$fileNameInput = $('<input>')
.attr('placeholder', msg('file-input-placeholder'))
.attr('title', msg('file-input-placeholder'))
.addClass('com-split-filename-input'),
items = [];
onSeparatorClick = function(e) {
var $sep = $(this);
e.preventDefault();
$sep.toggleClass('com-split-separator-selected');
$sep.next().toggle();
};
$ui.textRevItem = function(data) {
var $textRevItemClone, $fileNameClone, $fileNameInputLabelClone, $fileNameInputClone, $separatorClone;
$separatorClone = $separator.clone().click(onSeparatorClick).appendTo($ui);
$fileNameClone = $fileName.clone().appendTo($ui);
$fileNameInputLabelClone = $fileNameInputLabel.clone()
.appendTo($fileNameClone);
$fileNameInputClone = $fileNameInput.clone()
.val(title.getNameText() + ' ' + data.revid + '.' + title.getExtension())
.byteLimit(240 - title.getExtension().length - 1)
.keyup()
.focus(function() {
$fileNameInputLabelClone.fadeTo(400, 1);
})
.blur(function() {
$fileNameInputLabelClone.fadeTo(400, 0);
})
.appendTo($fileNameClone);
$textRevItemClone = $textRevItem.clone().appendTo($ui);
$txtTime.clone().html(split.formatTS(data.timestamp)).appendTo($textRevItemClone);
$txtUser.clone().text('[[User:' + data.user + ']]').appendTo($textRevItemClone);
$txtComment.clone().html(data.parsedcomment).appendTo($textRevItemClone);
items.push({
$sep: $separatorClone,
$inp: $fileNameInputClone,
ts: data.timestamp,
isText: true
});
};
$ui.imgRevItem = function(data) {
var $imageRevItemClone, $imgDescriptionClone, $imgImageContainerClone, $fileNameClone, $fileNameInputLabelClone, $fileNameInputClone;
$separatorClone = $separator.clone().click(onSeparatorClick).appendTo($ui);
$fileNameClone = $fileName.clone().appendTo($ui);
$fileNameInputLabelClone = $fileNameInputLabel.clone()
.appendTo($fileNameClone);
$fileNameInputClone = $fileNameInput.clone()
.val(title.getNameText() + ' ' + data.timestamp + '.' + title.getExtension())
.byteLimit(240 - title.getExtension().length - 1)
.keyup()
.focus(function() {
$fileNameInputLabelClone.fadeTo(400, 1);
})
.blur(function() {
$fileNameInputLabelClone.fadeTo(400, 0);
})
.appendTo($fileNameClone);
$imageRevItemClone = $imageRevItem.clone().appendTo($ui);
$imgDescriptionClone = $imgDescription.clone().appendTo($imageRevItemClone);
$imgImageContainerClone = $imgImageContainer.clone().appendTo($imageRevItemClone);
$imgImage.clone().attr({
src: data.thumburl,
height: data.thumbheight,
width: data.thumbwidth
}).appendTo($imgImageContainerClone);
$imgMIME.clone().text(data.mime).appendTo($imgImageContainerClone);
$imgTime.clone().html( split.formatTS(data.timestamp) ).appendTo($imgDescriptionClone);
$imgUser.clone().text('[[User:' + data.user + ']]').appendTo($imgDescriptionClone);
$imgSize.clone().html( split.formatSize(data.width, data.height, data.size) ).appendTo($imgDescriptionClone);
$imgComment.clone().html(data.parsedcomment).appendTo($imgDescriptionClone);
items.push({
$sep: $separatorClone,
$inp: $fileNameInputClone,
ts: data.timestamp
});
};
$confirm.click(function(e) {
e.preventDefault();
var parts = [];
$.each(items, function(i, item) {
if (item.$sep.hasClass('com-split-separator-selected')) {
if (!$.trim(item.$inp.val())) {
alert('Empty destination input detected!');
throw new Error('Empty destination input detected!');
}
// Create a new part
parts.push([]);
}
if (!parts.length) {
// No split -- keep here!
parts.push([]);
var lastPart = parts[parts.length - 1];
lastPart.push($.extend({}, item, {
$inp: $('<input>').val(pageName)
}));
} else {
var lastPart = parts[parts.length - 1];
lastPart.push(item);
}
});
var dest, ts2archiveName = {}, timestamps = [], pageText, needsText;
function undelete() {
if (0 === parts.length) {
return alert('All work done!');
}
var part = parts.pop();
needsText = true;
dest = part[0].$inp.val();
dest = 'File:' + dest.replace(/[Ff]ile:/, '');
// TODO: Link destination file instead
part[0].$sep.click();
$.each(part, function(i, p) {
if (p.ts) {
timestamps.push(p.ts);
}
if (p.isText) {
needsText = false;
}
});
if (timestamps.length) {
new mw.Api().postWithToken( 'delete', {
action: 'undelete',
title: pageName,
reason: '[[COM:SPLIT|History splitting]].',
timestamps: timestamps.join('|')
} )
.done(function(r) {
if (!r['undelete']) {
// TODO
return;
}
faUndelete();
})
.fail(function(r) {
// TODO
});
timestamps = [];
} else {
return undelete();
}
}
function faUndelete() {
}
function move() {
new mw.Api().postWithToken( 'move', {
action: 'move',
from: pageName,
to: dest,
noredirect: 1,
reason: '[[COM:SPLIT|History splitting]].'
} )
.done(function(r) {
if (!r['move']) {
// TODO
return;
}
if (needsText) {
edit();
} else {
undelete();
}
})
.fail(function(r) {
// TODO
});
}
function edit() {
new mw.Api().postWithToken( 'edit', {
action: 'edit',
title: dest,
text: pageText,
reason: '[[COM:SPLIT|History splitting]]. Adding last revision text from [[' + pageName + ']]'
} )
.done(function(r) {
if (!r['edit']) {
// TODO
return;
}
undelete();
})
.fail(function(r) {
// TODO
});
}
function delInfo() {
new mw.Api().get({
action: 'query',
list: 'filearchive',
fafrom: pageName,
fato: pageName,
falimit: 'max',
faprop: 'timestamp|archivename'
})
.done(function(r) {
$.each(r.query.filearchive, function(i, fa) {
ts2archiveName[fa.timestamp] = fa.name;
});
undelete();
})
.fail(function() {
// TODO
});
}
function del() {
new mw.Api().postWithToken( 'delete', {
action: 'delete',
title: pageName,
reason: '[[COM:SPLIT|History splitting]].'
} )
.done(function(r) {
if (!r['delete'] || !r['delete'].title) {
// TODO
return;
}
delInfo();
})
.fail(function(r) {
// TODO
});
}
new mw.Api().get({
action: 'query',
prop: 'revisions',
rvprop: 'content',
titles: pageName
})
.done(function(r) {
pageText = firstItem(r.query.pages).revisions[0]['*'];
del();
})
.fail(function() {
// TODO
});
});
return $ui;
},
initUI: function() {
return new mw.Api().post({
action: 'query',
titles: pageName,
prop: 'info|revisions|imageinfo',
rvprop: 'timestamp|user|ids|parsedcomment',
rvlimit: 'max',
iiprop: 'timestamp|user|ids|url|parsedcomment|size|mime|archivename',
iilimit: 'max',
iiurlwidth: 120,
iiurlheight: 120,
intoken: 'delete|move'
});
}
};
window.splitBang = split;
if (mwCfg.wgNamespaceNumber !== 6 || !mwCfg.wgArticleId || mw.user.isAnon()) {
return;
}
$(function() {
if (window.location.hash.indexOf('!action=split') !== -1) {
window.location.hash = '';
split.createUI();
}
split.installLink();
});
}(jQuery, mediaWiki));