User:Mike Dillon/Scripts/defaultsort.js

From Wiktionary, the free dictionary
Jump to navigation Jump to search

Note – after saving, you may have to bypass your browser’s cache to see the changes.

  • Mozilla / Firefox / Safari: hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (Command-R on a Macintosh);
  • Konqueror and Chrome: click Reload or press F5;
  • Opera: clear the cache in Tools → Preferences;
  • Internet Explorer: hold Ctrl while clicking Refresh, or press Ctrl-F5.

// 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 [[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"));
    }
});