User:BrandonXLF/ListSorter.js
/*** List Sorter ***/
// Tool to sort bullet-point lists on a page
// Documentation at en:w:User:BrandonXLF/ListSorter
// By en:w:User:BrandonXLF
$(function() {
var lang = mw.config.get('wgPageContentLanguage'),
title = mw.config.get('wgPageName'),
summary = 'Sorted bullet lists using ListSorter',
regex = /(\n|^)(\*+)(.*)/g;
function unattachedInnerText(el) {
var parent = el.parentNode,
tmp = parent ? document.createElement('i') : null;
parent && parent.replaceChild(tmp, el);
document.body.appendChild(el);
var text = el.innerText;
document.body.removeChild(el);
parent && parent.replaceChild(el, tmp);
return text;
}
function sortList(list) {
var children = Array.prototype.slice.call(list.children);
while (list.firstChild) list.removeChild(list.firstChild);
children.sort(function(a, b) {
return unattachedInnerText(a).trim().localeCompare(
unattachedInnerText(b).trim(), lang, {sensitivity: 'accent'}
);
});
for (var i = 0; i < children.length; i++) list.appendChild(children[i]);
}
function recursiveShow(sortables) {
var options = [],
values = [],
widget = new OO.ui.CheckboxMultiselectInputWidget(),
data = 0;
for (var i = 0; i < sortables.length; i++) {
var sortable = sortables[i],
items = $(sortables[i]).children(),
list = $('
- '),
- ').text(unattachedInnerText(this)).appendTo(list);
});
if (items.length >= 3) {
$('
- ').text('…').appendTo(list);
}
preview.css({
overflow: 'hidden',
pointerEvents: 'none',
whiteSpace: 'nowrap'
}).appendTo(cnt).append(list);
if (sortable.listSortChildren.length) {
$('
').css({paddingTop: '12px'}).appendTo(cnt).append(recursiveShow(sortable.listSortChildren).$element);}
sortable.listSortInputIndex = '' + data++;
options.push({
data: sortable.listSortInputIndex,
label: cnt
});
values.push(sortable.listSortInputIndex);
}
widget.setOptions(options);
widget.setValue(values);
for (i = 0; i < sortables.length; i++) {
sortables[i].listSortWidget = widget.checkboxMultiselectWidget.findItemFromData(sortables[i].listSortInputIndex);
}
return widget;
}
function recursiveDo(sortables, action) {
for (var i = 0; i < sortables.length; i++) {
action(sortables[i]);
recursiveDo(sortables[i].listSortChildren, action);
}
}
function sort() {
var params = {
action: 'query',
format: 'json',
prop: 'revisions',
titles: title,
formatversion: 2,
rvprop: 'content',
rvslots: 'main'
};
new mw.Api().get(params).then(function(res) {
var text = res.query.pages[0].revisions[0].slots.main.content,
afters = [],
marked = text.replace(regex, function(_, before, bullets, after) {
return before + bullets +
'
' +';after +
'
});
new mw.Api().parse(marked).then(function(parsed) {
var container = document.createElement('div'),
topLevel = [];
container.innerHTML = parsed;
var nodes = container.querySelectorAll('.listsorter-start');
for (var i = 0; i < nodes.length; i++) {
var list = nodes[i].parentNode.parentNode;
if (nodes[i].parentNode.previousElementSibling) continue;
list.listSortChildren = [];
if (list.children.length < 2) continue;
var parent = $(list).parents('ul')[0];
(parent ? parent.listSortChildren : topLevel).push(list);
}
var sort = new OO.ui.ButtonInputWidget({
label: 'Sort and review',
flags: ['primary', 'progressive']
}),
select = new OO.ui.ButtonInputWidget({
label: 'Select all'
}),
deselect = new OO.ui.ButtonInputWidget({
label: 'Deselect all'
}),
cancel = new OO.ui.ButtonInputWidget({
label: 'Cancel',
framed: false,
flags: ['destructive']
}),
inputs = recursiveShow(topLevel),
buttons = new OO.ui.HorizontalLayout({
items: [sort, select, deselect, cancel]
}),
fieldset = new OO.ui.FieldsetLayout({
label: 'Select lists to sort',
items: [inputs, buttons],
id: 'listsorterui'
});
inputs.$element.css({
border: '1px solid #888',
borderBottom: '0',
padding: '1em'
});
buttons.$element.css({
paddingTop: '12px',
position: 'sticky',
bottom: '0',
background: '#fff',
borderTop: '1px solid #888',
marginRight: '8px',
boxShadow: '0 -4px 4px -4px #888'
});
select.on('click', function() {
recursiveDo(topLevel, function(sortable) {
sortable.listSortWidget.setSelected(true);
});
});
deselect.on('click', function() {
recursiveDo(topLevel, function(sortable) {
sortable.listSortWidget.setSelected(false);
});
});
sort.on('click', function() {
recursiveDo(topLevel, function(sortable) {
if (sortable.listSortWidget.isSelected()) sortList(sortable);
});
var nodes = container.querySelectorAll('.listsorter-start'),
i = -1;
text = text.replace(regex, function(_, before) {
i++;
return before + nodes[i].getAttribute('data-bullets') + afters[+nodes[i].getAttribute('data-index')];
});
$('
cnt = $('
preview = $('
items.slice(0, 2).each(function() {
return $('