User:Mike Dillon/Scripts/defaultsort.js
Appearance
Code that you insert on this page could contain malicious content capable of compromising your account. If you import a script from another page with "importScript", "mw.loader.load", "iusc", or "lusc", take note that this causes you to dynamically load a remote script, which could be changed by others. Editors are responsible for all edits and actions they perform, including by scripts. User scripts are not centrally supported and may malfunction or become inoperable due to software changes. A guide to help you find broken scripts is available. If you are unsure whether code you are adding to this page is safe, you can ask at the appropriate village pump. This code will be executed when previewing this page. |
Documentation for this user script can be added at User:Mike Dillon/Scripts/defaultsort. |
// Requires: [[User:Mike Dillon/Scripts/easydom.js]], [[User:Mike Dillon/Scripts/i18n.js]]
/* <pre><nowiki> */
/* Messages */
wfAddMsg("en", "defaultSortNoteText", "Note");
wfAddMsg("en", "defaultSortMultipleText", "Multiple default sorts found");
wfAddMsg("en", "defaultSortDefaultText", "default");
wfAddMsg("en", "defaultSortInputLabel", "Default sort");
wfAddMsg("en", "defaultSortSubmitLabel", "Edit default sort");
wfAddMsg("en", "defaultSortCancelLabel", "Cancel");
wfAddMsg("en", "defaultSortChangedSummaryText",
"Changed [[Help:Category#Default_sort_key|default sort key]] to \"$1\"");
wfAddMsg("en", "defaultSortRemovedSummaryText",
"Removed [[Help:Category#Default_sort_key|default sort key]]");
wfAddMsg("en", "defaultSortToolboxItemLabel", "Edit default sort");
wfAddMsg("en", "defaultSortToolboxItemTitle", "Edit default sort");
function scrollToFit(target) {
// Determine the bounds of the target element
var top = target.offsetTop;
var e = target.offsetParent;
while (e != null) {
top += e.offsetTop;
e = e.offsetParent;
}
var height = target.offsetHeight;
var bottom = top + height;
// Determine the visible bounds of the viewable area
var viewTop = document.documentElement.scrollTop;
var viewHeight = window.innerHeight;
var viewBottom = viewTop + viewHeight;
// Determine where to scroll to
var scrollTop = viewTop;
if (top < viewTop || height > viewHeight) {
scrollTop = top;
} else if (bottom > viewBottom) {
scrollTop = viewTop + (bottom - viewBottom);
}
// Scroll the window
window.scroll(0, scrollTop);
}
function editDefaultSort(textbox, existingDefaults, categories, defaultSortForm) {
var text = textbox.value;
// Remove any existing defaults
for (var i in existingDefaults) {
text = text.substring(0, existingDefaults[i][0])
+ text.substring(existingDefaults[i][0] + existingDefaults[i][1]);
// Shift any categories that come after the removed default back
// by the length of the removed default
for (var j in categories) {
if (categories[j][0] < existingDefaults[i][0]) continue;
categories[j][0] -= existingDefaults[i][1];
}
}
// Remove sort keys from selected categories
var removed = 0;
for (var i in categories) {
categories[i][0] -= removed;
if (!defaultSortForm.elements["category_" + i].checked) continue;
var newCategoryText = "[[Category:" + categories[i][2] + "]]\n";
text = text.substring(0, categories[i][0]) + newCategoryText
+ text.substring(categories[i][0] + categories[i][1]);
removed += categories[i][1] - newCategoryText.length;
}
// Insert new default before first category, if any
if (defaultSortForm.elements["defaultsort"].value) {
var newDefaultSort = "{{DEFAULTSORT:"
+ defaultSortForm.elements["defaultsort"].value
+ "}}\n";
if (categories.length) {
text = text.substring(0, categories[0][0]) + newDefaultSort
+ text.substring(categories[0][0]);
} else {
// Else place at the end
text += "\n" + newDefaultSort;
}
}
textbox.value = text;
return defaultSortForm.elements["defaultsort"].value;
}
function doDefaultSort() {
var formDiv = document.getElementById("defaultSortForm");
if (formDiv != null) {
formDiv.parentNode.removeChild(formDiv);
}
var editform = document.getElementById("editform");
var textbox = document.getElementById("wpTextbox1");
if (editform == null || textbox == null) return;
var match;
var text = textbox.value;
// Find any existing {{DEFAULTSORT:}}
var existingDefaultRegex = new RegExp(
"\\{\\{DEFAULTSORT:(\\{\\{(?:FULL)?PAGENAME\\}\\}|[^}]+)\\}\\}[ \\t]*(?:\\n\\r?|\\r)?",
"gi");
var existingDefaults = [];
while (match = existingDefaultRegex.exec(text)) {
// Start, length, default sort key
existingDefaults.push([
existingDefaultRegex.lastIndex - match[0].length,
match[0].length,
match[1]
]);
}
// Find all categories
var categoryRegex = new RegExp(
"\\[\\[Category:([^|\\]]+)(?:\\|([^\\]]+))?\\]\\][ \\t]*(?:\\n\\r?|\\r)?",
"gi");
var categories = [];
while (match = categoryRegex.exec(text)) {
// Start, length, category, sort key
categories.push([
categoryRegex.lastIndex - match[0].length,
match[0].length,
match[1],
match[2]
]);
}
// Construct the default sort form
with (easydom) {
var defaultSortForm = form();
// Create default sort input box
var initialDefaultSort = '';
var defaultSortInput = input({ name: "defaultsort", size: "40" });
for (var i in existingDefaults) {
initialDefaultSort = existingDefaults[i][2];
}
// Create warning for multiple default sorts
var defaultSortWarning = span({ style: "font-size: smaller; color: red" });
if (existingDefaults.length > 1) {
defaultSortWarning.appendChild(span(" ", strong(wfMsg("defaultSortNoteText")), ": ",
wfMsg("defaultSortMultipleText")));
}
// Use most frequent sort key if no initialDefaultSort
if (!initialDefaultSort) {
var sortKeyFreq = {};
for (var i in categories) {
var cat = categories[i];
var currentSort = cat[3];
if (currentSort) {
if (!sortKeyFreq[currentSort]) {
sortKeyFreq[currentSort] = 0;
}
sortKeyFreq[currentSort]++;
}
}
var maxSortKeyCount = 0;
for (var i in sortKeyFreq) {
if (sortKeyFreq[i] > maxSortKeyCount) {
initialDefaultSort = i;
maxSortKeyCount = sortKeyFreq[i];
}
}
}
// Create category list
var categoryList = ul();
for (var i in categories) {
var cat = categories[i];
var isDefault = false;
var currentSort = cat[3];
if (!currentSort) {
currentSort = em(wfMsg("defaultSortDefaultText"));
isDefault = true;
}
var boxId = "category_" + i;
var checkbox = input({ type: "checkbox", name: boxId, id: boxId });
if (isDefault) {
checkbox.checked = true;
checkbox.disabled = true;
} else {
var changeDefaultSort = (function (value) {
return function () {
defaultSortInput.value = value;
return false;
};
})(currentSort);
if (currentSort == initialDefaultSort) {
checkbox.checked = true;
}
currentSort = a({ href: "#", onclick: changeDefaultSort }, currentSort);
}
categoryList.appendChild(li(checkbox,
" ", label({ "for": boxId }, cat[2]),
" (", span({ style: "color: gray" }, currentSort), ")"
));
}
// Add category list to form
defaultSortForm.appendChild(categoryList);
// Set initial default sort
defaultSortInput.value = initialDefaultSort;
// Add default sort input box to form
defaultSortForm.appendChild(div({ style: "padding: 0.1em" },
strong(wfMsg("defaultSortInputLabel")), ": ", defaultSortInput, defaultSortWarning
));
// Create cancel button
var defaultSortCancel = input({ type: "button", value: wfMsg("defaultSortCancelLabel") });
defaultSortCancel.onclick = function () {
formDiv.parentNode.removeChild(formDiv);
textbox.disabled = false;
window.focus();
return false;
};
// Add submit and cancel buttons to form
defaultSortForm.appendChild(div({ style: "padding: 0.1em" },
input({ type: "submit", style: "font-weight: bold",
value: wfMsg("defaultSortSubmitLabel")
}), " ", defaultSortCancel
));
// Create the form <div>
var formDiv = div({
id: "defaultSortForm",
style: "border: solid thin #aaa; padding: 0.1em;"
}, defaultSortForm);
// Attach the submit handler
defaultSortForm.onsubmit = function () {
var changed = editDefaultSort(textbox, existingDefaults, categories, this);
formDiv.parentNode.removeChild(formDiv);
textbox.disabled = false;
var summary = document.getElementById("wpSummary");
if (summary.value) {
summary.value += "; ";
}
if (changed) {
summary.value += wfMsgForContent("defaultSortChangedSummaryText", changed);
} else {
summary.value += wfMsgForContent("defaultSortRemovedSummaryText");
}
document.getElementById("wpMinoredit").checked = true;
document.getElementById("wpDiff").click();
return false;
};
// Stick the form in the page
editform.insertBefore(formDiv, document.getElementById("wpSummaryLabel"));
// Scroll to the form div
scrollToFit(formDiv);
// Disable the main text box
textbox.disabled = true;
// Focus on the input field
defaultSortInput.focus();
}
}
$(function () {
if (document.getElementById("wpTextbox1") != null) {
mw.util.addPortletLink("p-tb", "javascript:doDefaultSort()",
wfMsg("defaultSortToolboxItemLabel"), "t-defaultsort",
wfMsg("defaultSortToolboxItemTitle"));
}
});
/* </nowiki></pre> */