Jump to content

User:Enterprisey/archiver.js

From Wikipedia, the free encyclopedia
Note: After saving, you have to bypass your browser's cache to see the changes. Google Chrome, Firefox, Microsoft Edge and Safari: Hold down the ⇧ Shift key and click the Reload toolbar button. For details and instructions about other browsers, see Wikipedia:Bypass your cache.
// forked from https://en.wikipedia.org/w/index.php?title=User:%CE%A3/Testing_facility/Archiver.js&oldid=1003561411
$.when( mw.loader.using(['mediawiki.util','mediawiki.api']), $.ready).done( function () {
    if (mw.config.get("wgNamespaceNumber") % 2 == 0 && mw.config.get("wgNamespaceNumber") != 4) {
        // not a talk page and not project namespace
        return;
    }
    if (mw.config.get("wgNamespaceNumber") == -1) {
        // is a special page
        return;
    }

    mw.util.addCSS(".arky-selected-section { background-color:#D9E9FF } .arky-selected-section .arky-span a { font-weight:bold }");

    var sectionCodepointOffsets = new Object();
    var wikiText = "";
    var revStamp; // The timestamp when we originally got the page contents - we pass it to the "edit" API call for edit conflict detection

    var portletLink = mw.util.addPortletLink("p-cactions", "#", "ØCA", "pt-oeca", "Enter/exit the archival process", null, null);
    var archiveButton = $(document.createElement("button"));
    $(portletLink).click(function(e) {
        $(".arky-selected-section").removeClass('.arky-selected-section');
        $(".arky-span").toggle();
        $("#arky-archive-button").toggle();
    });

    archiveButton.html("archive all the selected threads")
        .attr("id", 'arky-archive-button')
        .css("position", 'sticky')
        .css("bottom", 0)
        .css("width", '100%')
        .css("font-size", '200%');
    $(document.body).append(archiveButton);
    archiveButton.toggle();
    archiveButton.click(function(e) {
        // returns `s` without the substring starting at `start` and ending at `end`
        function cut(s, start, end) {
            return s.substr(0, start) + s.substring(end);
        }
        var selectedSections = $(".arky-selected-section .arky-span").map(function() {
            return $(this).data("section");
        }).toArray();
        if (selectedSections.length === 0) {
            return alert("No threads selected, aborting");
        }

        var archivePageName = prompt("Archiving " + selectedSections.length + " threads: where should we move them to? (e.g. Wikipedia:Sandbox/Archive 1)", mw.config.get("wgPageName"));
        if (!archivePageName || archivePageName == mw.config.get("wgPageName")) {
            return alert("No archive target selected, aborting");
        }

        // codepointToUtf16Idx maps codepoint idx (i.e. MediaWiki index into page text) to utf-16 idx (i.e. JavaScript index into wikiText)
        var codepointToUtf16Idx = {};

        // Initialize "important" (= either a section start or end) values to 0
        selectedSections.forEach(function(n) {
            codepointToUtf16Idx[sectionCodepointOffsets[n].start] = 0;
            codepointToUtf16Idx[sectionCodepointOffsets[n].end] = 0;
        });
        codepointToUtf16Idx[Infinity] = Infinity; // Because sometimes we'll have Infinity as an "end" value

        // fill in our mapping from codepoints (MediaWiki indices) to utf-16 (i.e. JavaScript).
        // yes, this loops through every character in the wikitext. very unfortunate.
        var codepointPos = 0;
        for (var utf16Pos = 0; utf16Pos < wikiText.length; utf16Pos++, codepointPos++) {
            if (codepointToUtf16Idx.hasOwnProperty(codepointPos)) {
                codepointToUtf16Idx[codepointPos] = utf16Pos;
            }

            if ((0xD800 <= wikiText.charCodeAt(utf16Pos)) && (wikiText.charCodeAt(utf16Pos) <= 0xDBFF)) {
                // high surrogate! utf16Pos goes up by 2, but codepointPos goes up by only 1.
                utf16Pos++; // skip the low surrogate
            }
        }

        var newTextForArchivePage = selectedSections.map(function(n) {
            return wikiText.substring(
                codepointToUtf16Idx[sectionCodepointOffsets[n].start],
                codepointToUtf16Idx[sectionCodepointOffsets[n].end]
            );
        }).join("");

        selectedSections.reverse(); // go in reverse order so that we don't invalidate the offsets of earlier sections
        var newWikiText = wikiText;
        selectedSections.forEach(function(n) {
            newWikiText = cut(
                newWikiText,
                codepointToUtf16Idx[sectionCodepointOffsets[n].start],
                codepointToUtf16Idx[sectionCodepointOffsets[n].end]
            );
        });

        console.log("archive this:" + newTextForArchivePage);
        console.log("revised page:" + newWikiText);
        var pluralizedThreads = selectedSections.length + ' thread' + ((selectedSections.length === 1) ? '' : 's');
        new mw.Api().postWithToken("csrf", {
            action: 'edit',
            title: mw.config.get("wgPageName"),
            text: newWikiText,
            summary: "Removing " + pluralizedThreads + ", will be on [[" + archivePageName + "]]",
            basetimestamp: revStamp,
            starttimestamp: revStamp
        }).done(function(res1) {
            alert("Successfully removed threads from talk page");
            console.log(res1);
            new mw.Api().postWithToken("csrf", {action: 'edit', title: archivePageName, appendtext: "\n" + newTextForArchivePage, summary: "Adding " + pluralizedThreads + " from [[" + mw.config.get("wgPageName") + "]]"})
                .done(function(res2) {
                    alert("Successfully added threads to archive page");
                })
                .fail(function(res2) {
                    alert("failed to add threads to archive page. manual inspection needed.");
                })
                .always(function(res2) {
                    console.log(res2);
                    window.location.reload();
                });
            })
            .fail(function(res1) {
                alert("failed to remove threads from talk page. aborting archive process.");
                console.log(res1);
                window.location.reload();
            });
    }); // end of archiveButton click handler

    // grab page sections and wikitext so we can add the "archive" links to appropriate sections
    new mw.Api().get({action: 'parse', page: mw.config.get("wgPageName")}).done(function(parseApiResult) {
        new mw.Api().get({action: 'query', pageids: mw.config.get("wgArticleId"), prop: ['revisions'], rvprop: ['content', 'timestamp']}).done(function(revisionsApiResult) {
            var rv;
            rv = revisionsApiResult.query.pages[mw.config.get("wgArticleId")].revisions[0];
            wikiText = rv["*"];
            revStamp = rv['timestamp'];
        });

        var validSections = {};

        $(parseApiResult.parse.sections)
            // For sections transcluded from other pages, s.index will look
            // like T-1 instead of just 1. Remove those.
            .filter(function(i, s) { return s.index == parseInt(s.index) })

            .each(function(i, s) { validSections[s.index] = s });

        for (var i in validSections) {
            i = parseInt(i);
            // What MediaWiki calls "byteoffset" is actually a codepoint offset!! Drat!!
            sectionCodepointOffsets[i] = {
                start: validSections[i].byteoffset,
                end: validSections.hasOwnProperty(i+1)?validSections[i+1].byteoffset:Infinity
            };
        }
        $("#mw-content-text").find(":header").find("span.mw-headline").each(function(i, title) {
            var header, headerLevel, editSection, sectionNumber;
            header = $(this).parent();
            headerLevel = header.prop("tagName").substr(1, 1) * 1; // wtf javascript
            editSection = header.find(".mw-editsection"); // 1st child
            var editSectionLink = header.find(".mw-editsection a:last");
            var sectionNumber = undefined;

            if (editSectionLink[0]) {
                // Note: href may not be set.
                var sectionNumberMatch = editSectionLink.attr("href") && editSectionLink.attr("href").match(/&section=(\d+)/);
                if (sectionNumberMatch) {
                    sectionNumber = sectionNumberMatch[1];
                }
            }
            // if the if statement fails, it might be something like <h2>not a real section</h2>
            if (validSections.hasOwnProperty(sectionNumber)){
                $(editSection[0]).append(
                    "&nbsp;",
                    $("<span>", { "class": "arky-span" })
                    .css({'display':'none'})
                    .data({'header-level': headerLevel, 'section': sectionNumber})
                    .append(
                        $('<span>', { 'class': 'mw-editsection-bracket' }).text('['),
                        $('<a>')
                        .text('archive')
                        .click(function(){
                            var parentHeader = $(this).parents(':header');
                            parentHeader.toggleClass('arky-selected-section');

                            // now, click all sub-sections of this section
                            var isThisSectionSelected = parentHeader.hasClass('arky-selected-section');
                            var thisHeaderLevel = $(this).parents('.arky-span').data('header-level');

                            // starting from the current section, loop through each section
                            var allArchiveSpans = $('.arky-span');
                            var currSectionIdx = allArchiveSpans.index($(this).parents('.arky-span'));
                            for(var i = currSectionIdx + 1; i < allArchiveSpans.length; i++) {
                                if($(allArchiveSpans[i]).data('header-level') <= thisHeaderLevel) {
                                    // if this isn't a subsection, quit
                                    break;
                                }
                                var closestHeader = $(allArchiveSpans[i]).parents(':header');
                                if(closestHeader.hasClass('arky-selected-section') != isThisSectionSelected) {
                                    // if this section needs toggling, toggle it
                                    closestHeader.toggleClass('arky-selected-section');
                                }
                            }

                            // finally, update button
                            $('#arky-archive-button')
                                .prop('disabled', !$('.arky-selected-section').length)
                                .text('archive ' + $('.arky-selected-section').length + ' selected thread' +
                                    (($('.arky-selected-section').length === 1) ? '' : 's'));
                        }),
                        $('<span>', { 'class': 'mw-editsection-bracket' }).text(']')
                    ));
            }
        });
    });
});
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy