User:Mike Dillon/Scripts/defaultsort.js

// Requires: User:Mike Dillon/Scripts/easydom.js, User:Mike Dillon/Scripts/i18n.js

/* */

/* 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 default sort key to \"$1\""); wfAddMsg("en", "defaultSortRemovedSummaryText",   "Removed 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 = "\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 = "\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 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 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"));   } });

/* */