Jump to content

User:Ed6767/sandbox.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.
// #REDIRECT [[User:Ed6767]]
// <nowiki>
// THE MAIN PAGE DOES NOT INCLUDE MATERIAL DESIGN LITE CSS. Include all the things here if needed.
var wikiEditorStyle = `
.mdl-tooltip {
    -webkit-transform: scale(0);
    transform: scale(0);
    -webkit-transform-origin: top center;
    transform-origin: top center;
    z-index: 999;
    background: rgba(97, 97, 97, .9);
    border-radius: 2px;
    color: #fff;
    display: inline-block;
    font-size: 10px;
    font-weight: 500;
    line-height: 14px;
    max-width: 170px;
    position: fixed;
    top: -500px;
    left: -500px;
    padding: 8px;
    text-align: center
}

.mdl-tooltip.is-active {
    -webkit-animation: pulse 200ms cubic-bezier(0, 0, .2, 1)forwards;
    animation: pulse 200ms cubic-bezier(0, 0, .2, 1)forwards
}

.mdl-tooltip--large {
    line-height: 14px;
    font-size: 14px;
    padding: 16px
}

@-webkit-keyframes pulse {
    0% {
        -webkit-transform: scale(0);
        transform: scale(0);
        opacity: 0
    }

    50% {
        -webkit-transform: scale(.99);
        transform: scale(.99)
    }

    100% {
        -webkit-transform: scale(1);
        transform: scale(1);
        opacity: 1;
        visibility: visible
    }
}

@keyframes pulse {
    0% {
        -webkit-transform: scale(0);
        transform: scale(0);
        opacity: 0
    }

    50% {
        -webkit-transform: scale(.99);
        transform: scale(.99)
    }

    100% {
        -webkit-transform: scale(1);
        transform: scale(1);
        opacity: 1;
        visibility: visible
    }
}
.mdl-snackbar {
    position: fixed;
    bottom: 0;
    left: 50%;
    cursor: default;
    background-color: #323232;
    z-index: 3;
    display: block;
    display: -webkit-flex;
    display: -ms-flexbox;
    display: flex;
    -webkit-justify-content: space-between;
    -ms-flex-pack: justify;
    justify-content: space-between;
    font-family: "Roboto", "Helvetica", "Arial", sans-serif;
    will-change: transform;
    -webkit-transform: translate(0, 80px);
    transform: translate(0, 80px);
    transition: transform .25s cubic-bezier(.4, 0, 1, 1);
    transition: transform .25s cubic-bezier(.4, 0, 1, 1), -webkit-transform .25s cubic-bezier(.4, 0, 1, 1);
    pointer-events: none
}

@media (max-width:479px) {
    .mdl-snackbar {
        width: 100%;
        left: 0;
        min-height: 48px;
        max-height: 80px
    }
}

@media (min-width:480px) {
    .mdl-snackbar {
        min-width: 288px;
        max-width: 568px;
        border-radius: 2px;
        -webkit-transform: translate(-50%, 80px);
        transform: translate(-50%, 80px)
    }
}

.mdl-snackbar--active {
    -webkit-transform: translate(0, 0);
    transform: translate(0, 0);
    pointer-events: auto;
    transition: transform .25s cubic-bezier(0, 0, .2, 1);
    transition: transform .25s cubic-bezier(0, 0, .2, 1), -webkit-transform .25s cubic-bezier(0, 0, .2, 1)
}

@media (min-width:480px) {
    .mdl-snackbar--active {
        -webkit-transform: translate(-50%, 0);
        transform: translate(-50%, 0)
    }
}

.mdl-snackbar__text {
    padding: 14px 12px 14px 24px;
    vertical-align: middle;
    color: #fff;
    float: left
}

.mdl-snackbar__action {
    background: 0 0;
    border: none;
    color: rgb(83, 109, 254);
    float: right;
    padding: 14px 24px 14px 12px;
    font-family: "Roboto", "Helvetica", "Arial", sans-serif;
    font-size: 14px;
    font-weight: 500;
    text-transform: uppercase;
    line-height: 1;
    letter-spacing: 0;
    overflow: hidden;
    outline: none;
    opacity: 0;
    pointer-events: none;
    cursor: pointer;
    text-decoration: none;
    text-align: center;
    -webkit-align-self: center;
    -ms-flex-item-align: center;
    -ms-grid-row-align: center;
    align-self: center
}

.mdl-snackbar__action::-moz-focus-inner {
    border: 0
}

.mdl-snackbar__action:not([aria-hidden]) {
    opacity: 1;
    pointer-events: auto
}

.mdl-dialog {
    border: none;
    box-shadow: 0 9px 46px 8px rgba(0, 0, 0, .14), 0 11px 15px -7px rgba(0, 0, 0, .12), 0 24px 38px 3px rgba(0, 0, 0, .2);
}

.mdl-dialog__title {
    padding: 24px 24px 0;
    margin: 0;
    font-size: 2.5rem
}

.mdl-dialog__actions {
    padding: 8px 8px 8px 24px;
    display: -webkit-flex;
    display: -ms-flexbox;
    display: flex;
    -webkit-flex-direction: row-reverse;
    -ms-flex-direction: row-reverse;
    flex-direction: row-reverse;
    -webkit-flex-wrap: wrap;
    -ms-flex-wrap: wrap;
    flex-wrap: wrap
}

.mdl-dialog__actions>* {
    margin-right: 8px;
    height: 36px
}

.mdl-dialog__actions>*:first-child {
    margin-right: 0
}

.mdl-dialog__actions--full-width {
    padding: 0 0 8px
}

.mdl-dialog__actions--full-width>* {
    height: 48px;
    -webkit-flex: 0 0 100%;
    -ms-flex: 0 0 100%;
    flex: 0 0 100%;
    padding-right: 16px;
    margin-right: 0;
    text-align: right
}

.mdl-dialog__content {
    padding: 20px 24px 24px;
    color: rgba(0, 0, 0, .54)
}


.mdl-button {
    background: 0 0;
    border: none;
    border-radius: 2px;
    color: #000;
    position: relative;
    height: 36px;
    margin: 0;
    min-width: 64px;
    padding: 0 16px;
    display: inline-block;
    font-family: "Roboto", "Helvetica", "Arial", sans-serif;
    font-size: 14px;
    font-weight: 500;
    text-transform: uppercase;
    letter-spacing: 0;
    overflow: hidden;
    will-change: box-shadow;
    transition: box-shadow .2s cubic-bezier(.4, 0, 1, 1), background-color .2s cubic-bezier(.4, 0, .2, 1), color .2s cubic-bezier(.4, 0, .2, 1);
    outline: none;
    cursor: pointer;
    text-decoration: none;
    text-align: center;
    line-height: 36px;
    vertical-align: middle
}

.mdl-button::-moz-focus-inner {
    border: 0
}

.mdl-button:hover {
    background-color: rgba(158, 158, 158, .2)
}

.mdl-button:focus:not(:active) {
    background-color: rgba(0, 0, 0, .12)
}

.mdl-button:active {
    background-color: rgba(158, 158, 158, .4)
}

.mdl-button.mdl-button--colored {
    color: rgb(33, 150, 243)
}

.mdl-button.mdl-button--colored:focus:not(:active) {
    background-color: rgba(0, 0, 0, .12)
}

input.mdl-button[type="submit"] {
    -webkit-appearance: none
}

.mdl-button--raised {
    background: rgba(158, 158, 158, .2);
    box-shadow: 0 2px 2px 0 rgba(0, 0, 0, .14), 0 3px 1px -2px rgba(0, 0, 0, .2), 0 1px 5px 0 rgba(0, 0, 0, .12)
}

.mdl-button--raised:active {
    box-shadow: 0 4px 5px 0 rgba(0, 0, 0, .14), 0 1px 10px 0 rgba(0, 0, 0, .12), 0 2px 4px -1px rgba(0, 0, 0, .2);
    background-color: rgba(158, 158, 158, .4)
}

.mdl-button--raised:focus:not(:active) {
    box-shadow: 0 0 8px rgba(0, 0, 0, .18), 0 8px 16px rgba(0, 0, 0, .36);
    background-color: rgba(158, 158, 158, .4)
}

.mdl-button--raised.mdl-button--colored {
    background: rgb(33, 150, 243);
    color: rgb(255, 255, 255)
}

.mdl-button--raised.mdl-button--colored:hover {
    background-color: rgb(33, 150, 243)
}

.mdl-button--raised.mdl-button--colored:active {
    background-color: rgb(33, 150, 243)
}

.mdl-button--raised.mdl-button--colored:focus:not(:active) {
    background-color: rgb(33, 150, 243)
}

.mdl-button--raised.mdl-button--colored .mdl-ripple {
    background: rgb(255, 255, 255)
}

.mdl-button--fab {
    border-radius: 50%;
    font-size: 24px;
    height: 56px;
    margin: auto;
    min-width: 56px;
    width: 56px;
    padding: 0;
    overflow: hidden;
    background: rgba(158, 158, 158, .2);
    box-shadow: 0 1px 1.5px 0 rgba(0, 0, 0, .12), 0 1px 1px 0 rgba(0, 0, 0, .24);
    position: relative;
    line-height: normal
}

.mdl-button--fab .material-icons {
    position: absolute;
    top: 50%;
    left: 50%;
    -webkit-transform: translate(-12px, -12px);
    transform: translate(-12px, -12px);
    line-height: 24px;
    width: 24px
}

.mdl-button--fab.mdl-button--mini-fab {
    height: 40px;
    min-width: 40px;
    width: 40px
}

.mdl-button--fab .mdl-button__ripple-container {
    border-radius: 50%;
    -webkit-mask-image: -webkit-radial-gradient(circle, #fff, #000)
}

.mdl-button--fab:active {
    box-shadow: 0 4px 5px 0 rgba(0, 0, 0, .14), 0 1px 10px 0 rgba(0, 0, 0, .12), 0 2px 4px -1px rgba(0, 0, 0, .2);
    background-color: rgba(158, 158, 158, .4)
}

.mdl-button--fab:focus:not(:active) {
    box-shadow: 0 0 8px rgba(0, 0, 0, .18), 0 8px 16px rgba(0, 0, 0, .36);
    background-color: rgba(158, 158, 158, .4)
}

.mdl-button--fab.mdl-button--colored {
    background: rgb(83, 109, 254);
    color: rgb(255, 255, 255)
}

.mdl-button--fab.mdl-button--colored:hover {
    background-color: rgb(83, 109, 254)
}

.mdl-button--fab.mdl-button--colored:focus:not(:active) {
    background-color: rgb(83, 109, 254)
}

.mdl-button--fab.mdl-button--colored:active {
    background-color: rgb(83, 109, 254)
}

.mdl-button--fab.mdl-button--colored .mdl-ripple {
    background: rgb(255, 255, 255)
}

.mdl-button--icon {
    border-radius: 50%;
    font-size: 24px;
    height: 32px;
    margin-left: 0;
    margin-right: 0;
    min-width: 32px;
    width: 32px;
    padding: 0;
    overflow: hidden;
    color: inherit;
    line-height: normal
}

.mdl-button--icon .material-icons {
    position: absolute;
    top: 50%;
    left: 50%;
    -webkit-transform: translate(-12px, -12px);
    transform: translate(-12px, -12px);
    line-height: 24px;
    width: 24px
}

.mdl-button--icon.mdl-button--mini-icon {
    height: 24px;
    min-width: 24px;
    width: 24px
}

.mdl-button--icon.mdl-button--mini-icon .material-icons {
    top: 0;
    left: 0
}

.mdl-button--icon .mdl-button__ripple-container {
    border-radius: 50%;
    -webkit-mask-image: -webkit-radial-gradient(circle, #fff, #000)
}

.mdl-button__ripple-container {
    display: block;
    height: 100%;
    left: 0;
    position: absolute;
    top: 0;
    width: 100%;
    z-index: 0;
    overflow: hidden
}

.mdl-button[disabled] .mdl-button__ripple-container .mdl-ripple,
.mdl-button.mdl-button--disabled .mdl-button__ripple-container .mdl-ripple {
    background-color: transparent
}

.mdl-button--primary.mdl-button--primary {
    color: rgb(33, 150, 243)
}

.mdl-button--primary.mdl-button--primary .mdl-ripple {
    background: rgb(255, 255, 255)
}

.mdl-button--primary.mdl-button--primary.mdl-button--raised,
.mdl-button--primary.mdl-button--primary.mdl-button--fab {
    color: rgb(255, 255, 255);
    background-color: rgb(33, 150, 243)
}

.mdl-button--accent.mdl-button--accent {
    color: rgb(83, 109, 254)
}

.mdl-button--accent.mdl-button--accent .mdl-ripple {
    background: rgb(255, 255, 255)
}

.mdl-button--accent.mdl-button--accent.mdl-button--raised,
.mdl-button--accent.mdl-button--accent.mdl-button--fab {
    color: rgb(255, 255, 255);
    background-color: rgb(83, 109, 254)
}

.mdl-button[disabled][disabled],
.mdl-button.mdl-button--disabled.mdl-button--disabled {
    color: rgba(0, 0, 0, .26);
    cursor: default;
    background-color: transparent
}

.mdl-button--fab[disabled][disabled],
.mdl-button--fab.mdl-button--disabled.mdl-button--disabled {
    background-color: rgba(0, 0, 0, .12);
    color: rgba(0, 0, 0, .26)
}

.mdl-button--raised[disabled][disabled],
.mdl-button--raised.mdl-button--disabled.mdl-button--disabled {
    background-color: rgba(0, 0, 0, .12);
    color: rgba(0, 0, 0, .26);
    box-shadow: none
}

.mdl-button--colored[disabled][disabled],
.mdl-button--colored.mdl-button--disabled.mdl-button--disabled {
    color: rgba(0, 0, 0, .26)
}

.mdl-button .material-icons {
    vertical-align: middle
}

`;
// (c) Ed.E 2020

function waitForMDLLoad(cb) { // Used to wait for MDL load
    if(typeof componentHandler !== "undefined"){
        cb(); // callback
    } else {
        setTimeout(()=>waitForMDLLoad(cb), 250);
    }
}

function redirect(url, inNewTab) {
    if (inNewTab) {
        Object.assign(document.createElement('a'), { target: '_blank', href: url}).click(); // Open in new tab
    } else {
        window.location.href = url; // open here
    }
}

var wikiEditor = {
    "version" : "rev6final", // don't forget to change each version!
    "sign": ()=>{return atob("fn5+fg==")}, // we have to do this because mediawiki will swap this out with devs sig.
    "welcome": ()=> {return atob("e3tzdWJzdDpXZWxjb21lfX0=");},
    "visuals" : {
        "init" : (callback) => {
            // Welcome message
            console.log(`
+------------------------------+
|                              |
| RedWarn (c) 2020 Ed E        |
| and contributors             |
|                              |
+------------------------------+
            `);
            // Load MDL and everything needed, then callback when all loaded
            $('head').append(`
                <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/jquery-contextmenu/2.7.1/jquery.contextMenu.min.css">
                <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-contextmenu/2.7.1/jquery.contextMenu.min.js"></script>
                <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-contextmenu/2.7.1/jquery.ui.position.js"></script>
                <link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
                <script src="https://cdnjs.cloudflare.com/ajax/libs/dialog-polyfill/0.4.2/dialog-polyfill.min.js"></script> <!-- firefox being dumb -->
                <script src="https://code.getmdl.io/1.3.0/material.min.js" id="MDLSCRIPT"></script>
                <style>
                /* Context menus */
                .context-menu-list {
                    list-style-type: none;
                    list-style-image: none;
                }

                /* MDL */
                `+ wikiEditorStyle +`
                </style>
            `); // Append required libaries to page
            // wait for load
            waitForMDLLoad(callback);
        },

        "register" : function(c) {
            // Register a componant with MDL
            componentHandler.upgradeElement(c);
        },

        "pageIcons" : ()=> {
            try {
                /* DO NOT REMOVE THIS LINE */

if (mw.config.get("wgRelevantPageName").includes("User:") || mw.config.get("wgRelevantPageName").includes("User_talk:")) {
    document.getElementsByClassName("mw-indicators mw-body-content")[0].innerHTML += `
        <!-- BEGIN USER ONLY ICONS -->
        &nbsp;&nbsp;&nbsp;
        <div id="newUsrMsg" class="icon material-icons"><span style="cursor: pointer;" onclick="wikiEditor.ui.newMsg();">send</span></div>
        <div class="mdl-tooltip mdl-tooltip--large" for="newUsrMsg">
            New Message
        </div>

        <div id="welcomeUsr" class="icon material-icons"><span style="cursor: pointer;" onclick="wikiEditor.info.quickWelcome();">sentiment_satisfied_alt</span></div>
        <div class="mdl-tooltip mdl-tooltip--large" for="welcomeUsr">
            Quick Welcome
        </div>

        <div id="warnUsr" class="icon material-icons"><span style="cursor: pointer;" onclick="wikiEditor.ui.beginWarn();">report</span></div>
        <div class="mdl-tooltip mdl-tooltip--large" for="warnUsr">
            Send Notice
        </div>

        <div id="reportUsr" class="icon material-icons"><span style="cursor: pointer;" onclick="alert('action here!');">gavel</span></div>
        <div class="mdl-tooltip mdl-tooltip--large" for="reportUsr">
            Report to Admin
        </div>
    `;
} // end this section
document.getElementsByClassName("mw-indicators mw-body-content")[0].innerHTML += `
    &nbsp;&nbsp;&nbsp;
    <!-- START PAGE ONLY ICONS -->

    <div id="mrevPg" class="icon material-icons"><span style="cursor: pointer;" onclick="wikiEditor.info.isLatestRevision(mw.config.get('wgRelevantPageName'), 0, ()=>{});">watch_later</span></div>
    <div class="mdl-tooltip mdl-tooltip--large" for="mrevPg">
        Latest revision
    </div>
    <div id="rwOpenPref" class="icon material-icons"><span style="cursor: pointer;" onclick="wikiEditor.ui.openPreferences();">settings</span></div>
    <div class="mdl-tooltip mdl-tooltip--large" for="rwOpenPref">
        RedWarn Preferences
    </div>
`;


/* DO NOT REMOVE THIS LINE */
            } catch (error) {
                // Likely invalid theme
                dialogEngine.create(`
                <b>Sorry</b><br>
                RedWarn isn't compatible with this theme. Please revert to a compatible theme to continue using RedWarn.<br>
                <a href="/wiki/Special:Preferences#mw-prefsection-rendering" style="font-size:25px">Go to Theme Preferences</a> <br>
                <a href='#' onclick='dialogEngine.dialog.close();'>Close</a>`).showModal();
                return; // Exit
            }
            

            // Now register all icons
            for (let item of document.getElementsByClassName("mdl-tooltip")) {
                wikiEditor.visuals.register(item); 
            }
            // That's done :)
        }
    },
    "alerts" : {
        "accountNotPermitted" : () => {
            // BEHAVIOR
            // Shows toast for 15 seconds, shows dialog on click
            wikiEditor.visuals.toast.show(`
                To prevent vandalism, you cannot use this tool until you are a confirmed user. Come back later.
                `, false, false, 15000);
        }
    },

    "recentChanges" : {
        "openPage" : (filters)=> {
            // Open recent changes url
            let sidebarSize = 500;
            let addCol = "0,255,0"; // rbg
            let rmCol = "255,0,0"; // rgb
            if (wikiEditor.config.ptrSidebar) sidebarSize = wikiEditor.config.ptrSidebar; // If preferences set, apply them
            if (wikiEditor.config.ptrAddCol) addCol = wikiEditor.config.ptrAddCol;
            if (wikiEditor.config.ptrRmCol) rmCol = wikiEditor.config.ptrRmCol;
            
            let url = URL.createObjectURL(new Blob([mdlContainers.generateHtml(`
            <!DOCTYPE HTML>
<html>
    <!-- RECENT CHANGES EDITOR. RUNS COMPLETLY SEPERATE AS STANDALONE PAGE. DO NOT USE A CONTAINER. -->
    <head>
        <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
        <link href="https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&display=swap" rel="stylesheet">
        <link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
        <script defer src="https://code.getmdl.io/1.3.0/material.min.js"></script>
        <link rel="stylesheet" href="https://code.getmdl.io/1.3.0/material.blue-indigo.min.css" />
    </head>
    <body onload="refreshLive();"> <!-- Show a few edits on load -->
    <!-- No header, and the drawer stays open on larger screens (fixed drawer). -->
        <div class="mdl-layout mdl-js-layout mdl-layout--fixed-drawer">
            <div class="mdl-layout__drawer" style="width:`+ sidebarSize +`px">
            <span class="mdl-layout-title">RedWarn Recent Changes Patrol</span>
            <nav class="mdl-navigation">
                <a class="mdl-navigation__link" href="/wiki/Special:RecentChanges">Return to Wikipedia / Change filter options</a>
                <a class="mdl-navigation__link" href="#" style="background-color: teal;color:white;" onmouseover="tI = setInterval(refreshLive,  750);" onmouseout="clearTimeout(tI);">Hover your cursor here for live updates</a>
                <div id="recentChangesContainer">
                </div>
                
            </nav>
            </div>
            <main class="mdl-layout__content">
            <div class="page-content" style="height:100%">

                <div id="welcomeContainer" style="padding-left: `+ (sidebarSize - 220) +`;padding-top: calc(5% - 60px);padding-right:5%;">
                    <h2>Welcome to RedWarn Patrol!</h2>
                    <p>
                        To the left, you can see a list of recent changes. To view the latest revision of the article that was changed, simply click it on the left.<br>
                        The stronger the colour, the more the amount of content that has been changed. By default, red means a change that made the page smaller, and green as an edit that made the page bigger. You can change these colours in your preferences.<br>
                        For full instructions, refer to the user guide.
                    </p>
                </div>

                <div id="loadingContainer" style="display: none;padding-left: `+ (sidebarSize - 240) +`;text-align: center;padding-top: calc(10% - 60px);">
                    <div class="mdl-spinner mdl-js-spinner is-active"></div>
                </div>

                <iframe src="about:blank" style="display: none; height:100%; width: calc(100% - `+ (sidebarSize - 250) +`px);padding-left: `+ (sidebarSize - 250) +`;"></iframe>
            </div>
            </main>
        </div>

    <script>
var tI;
function convertRange( value, r1, r2 ) { 
    return ( value - r1[ 0 ] ) * ( r2[ 1 ] - r2[ 0 ] ) / ( r1[ 1 ] - r1[ 0 ] ) + r2[ 0 ];
}
// Check for new updates every 750ms
function refreshLive() {
    // Don't use JSON API so that the vars are interchangable from Special:RecentChanges
    parseRecentChanges(arr=>{
        arr.forEach(edit => {
            console.log(edit);
            // Clear excess
            if ($('a[id^="recentChange"]').length > 50) {
                // Max 50, delete bottom
                $('a[id^="recentChange"]').last().remove(); // remove the last one
            }
            // Add new
            if ($("#recentChange-"+ edit.revID).length < 1) { // Only continue if edit not already in page
                let sizeDiff = edit.changeSize;
                let opacityLevel = 0;
                if ((sizeDiff > 1000) || (sizeDiff < -1000)) {
                    // Max opacity
                    opacityLevel = 1;
                } else {
                    // We can map
                    if (sizeDiff < 0) {
                        // Red map
                        opacityLevel = convertRange(sizeDiff, [0, -1000], [0.1, 0.75]);
                    } else {
                        // Green map
                        opacityLevel = convertRange(sizeDiff, [0, 1000], [0.1, 0.75]);
                    }
                    
                }
                let style = (sizeDiff < 0) ? "background-color: rgba(`+ rmCol +`,"+ opacityLevel +"); color:black;" : "background-color: rgba(`+ addCol +`,"+ opacityLevel +"); color:black;" // First neg style, next add style
                $("#recentChangesContainer").prepend('<a class="mdl-navigation__link notif" href="#" onclick="goToLatestDiffPage(\\\''+ edit.title +'\\\')" style="display:none;'+ style +'" id="recentChange-'+ edit.revID +'"><i id="title-'+ edit.revID +'"></i><br><b>'+ edit.user +'</b> ('+ sizeDiff +')<br /><span id="comment-'+ edit.revID +'"></span><br>'+ edit.tags +'</a>');
                $('#title-'+ edit.revID).text(edit.title); // XSS security
                $('#comment-'+ edit.revID).text(edit.comment);
                $("#recentChange-"+ edit.revID).fadeIn(); // fancy
            }
            
        });
    });
}

function goToLatestDiffPage(article) {
    $("#welcomeContainer").hide();
    $("iframe").hide();
    $("#loadingContainer").show();

    $("iframe").on("load", ()=>{
        // Page loaded handke
        $("#welcomeContainer").hide();
        $("iframe").show();
        $("#loadingContainer").hide();
    });
    // Nav to latest diff page
    $.getJSON("https://en.wikipedia.org/w/api.php?action=query&prop=revisions&titles="+ encodeURIComponent(article) +"&rvslots=*&rvprop=ids%7Cuser&formatversion=2&format=json", r=>{
            // We got the response
            let latestRId = r.query.pages[0].revisions[0].revid;
            let parentRId = r.query.pages[0].revisions[0].parentid;
            // Load the preview page of the latest one
            $("iframe").attr("src", "https://en.wikipedia.org/w/index.php?title="+ encodeURIComponent(name) +"&diff="+ latestRId +"&oldid="+ parentRId +"&diffmode=source");
    });
}

function parseRecentChanges(callback) {
    // USING HTML API for one reason
    // 1. works easily with Special:RecentChanges filters
    $.get("https://en.wikipedia.org/wiki/Special:RecentChanges?`+ filters +`&action=render&enhanced=0", r=>{
        $("body").append("<div id='toDelete' style='display:none;'></div>"); // create temp div
        $("#toDelete").html(r);
        let _ = $("#toDelete").find("ul").find("li");
        let arr = [];
        let recentChanges = _.each(i=> {
            let el = _[i];
            let revId = $(el).attr("data-mw-revid"); // get revision ID
            let changeSize = $(el).find(".mw-diff-bytes").text(); // get size (+123)
            let changeTitle = $(el).find(".mw-changeslist-title").text(); // get title of page
            let changeUsername = $(el).find(".mw-userlink").text(); // get username of changer
            let tags =  $(el).find(".mw-tag-markers").text(); // get tags (mobile edit ext)
            let comment = $(el).find(".comment").text(); // get comment
            if (!isNaN(parseInt(changeSize))) {
                // Valid
                arr[i] = {
                "revID": revId,
                "changeSize": parseInt(changeSize),
                "tags": tags,
                "user": changeUsername,
                "title": changeTitle,
                "comment": comment
                }; // append data to the array
            }
        });

        $("toDelete").remove(); // dispose unneeded
        callback(arr); // we done
    });
}

    </script>
    </body>
</html>
            `)], { type: 'text/html' })); // blob url
            redirect(url, false);
        },
        "diffLinkAddRedWarn" : () => { // add redwarn to recent changes page
            $('body').unbind('DOMSubtreeModified'); // Prevent infinite loop
            $.each($(".mw-changeslist-diff"), (i,el)=>{
                if ($(el).parent().html().includes("#redwarn")) {
                    // Link already there.
                } else {
                    $(el).parent().prepend(`<a href='#redwarn' onclick='wikiEditor.ui.revisionBrowser("`+ el.href +`");'>Redwarn</a>  | `);
                }
            });
            window.addEventListener("focus", e=>{ 
                wikiEditor.recentChanges.bindRecentChanges(); // fix chrome unfocus bug 
            }, false);
            wikiEditor.recentChanges.bindRecentChanges();
        },

        "bindRecentChanges" : () => { // on list change add redwarn
            $('body').on('DOMSubtreeModified', 'ul.special', ()=>wikiEditor.recentChanges.diffLinkAddRedWarn());
        }
    }
};

var messageHandlers = {"testHandler": () => {alert("Working!");}};

function addMessageHandler(msg, callback) { // calling more than once will just overwrite
    Object.assign(messageHandlers, ((a,b)=>{let _ = {}; _[a]=b; return _;})(msg, callback)); // function ab returns a good formatted obj
}

window.onmessage = function(e){
    if (messageHandlers[e.data]){messageHandlers[e.data]();} // Excecute handler if exact
    else { // We find ones that contain
        for (const evnt in messageHandlers) {
            if ((evnt.substr(evnt.length - 1) == "*") && e.data.includes(evnt.substr(0, evnt.length - 2))) { // and includes w * chopped off
                messageHandlers[evnt](e.data);
                return;
            } // if contains and ends with wildcard then we do it
        }
    }
};

// init everthing
function initwikiEdit() {
    wikiEditor.visuals.init(()=>{
        wikiEditor.visuals.toast.init();
        dialogEngine.init();
        wikiEditor.visuals.pageIcons();
        wikiEditor.ui.registerContextMenu(); // register context menus

        // Quick check we have perms to use (in confirmed/autoconfirmed group)
        wikiEditor.info.featureRestrictPermissionLevel("confirmed", false, ()=>{
            // We don't have permission
            
            // Add red lock to the top right to show that RedWarn cannot be used
            document.getElementsByClassName("mw-indicators mw-body-content")[0].innerHTML = `
            <div id="Lock" class="icon material-icons"><span style="cursor: help; color:red;" onclick="">lock</span></div>
            <div class="mdl-tooltip" for="Lock">
                You don't have permission to use RedWarn yet. Please refer to the user guide for more information.
            </div>
            `;
            // Now register that
            for (let item of document.getElementsByClassName("mdl-tooltip")) {
                wikiEditor.visuals.register(item); 
            }
            wikiEditor = {}; // WIPE OUT ENTIRE CLASS. We're not doing anything here.
            // Notification
            mw.notify("Thank you for you interest in moderating Wikipedia. However, you do not have permission to use RedWarn. Please refer to the user guide for more information.");
            // That's it
        });

        // We have perms, let's continue.

        // Load config and check if updated
        wikiEditor.info.getConfig(()=> {
            if (wikiEditor.config.lastVersion != wikiEditor.version) {
                // We've had an update
                wikiEditor.config.lastVersion = wikiEditor.version; // update entry 
                wikiEditor.info.writeConfig(true, ()=> { // update the config file
                    // Push an update toast
                    wikiEditor.visuals.toast.show("RedWiki has been updated!", "MORE",
                    ()=>redirect("https://en.wikipedia.org/wiki/User:Ed6767/redwarn/bugsquasher", true), 7500);
                });
            }
        });

        // Check if a message is in URL (i.e edit complete ext)
        if(window.location.hash.includes("#noticeApplied-")) {
            // Show toast w undo edit capabilities
            // #noticeApplied-currentEdit-pastEdit
            wikiEditor.visuals.toast.show("Message saved", "UNDO", ()=>{
                // Redirect to undo page mw.config.get("wgRelevantPageName");
                // TODO: maybe replace with custom page in future? 
                window.location.href = "/w/index.php?title="+ mw.config.get("wgRelevantPageName") +"&action=edit&undoafter="+ window.location.hash.split("-")[2] +"&undo="+ window.location.hash.split("-")[1];
            }, 7500);
        } else if (window.location.hash.includes("#redirectLatestRevision")) {
            wikiEditor.visuals.toast.show("Redirected to the lastest revision.");
        } else if (window.location.hash.includes("#configChange")) {
            wikiEditor.visuals.toast.show("Preferences saved.");
        } else if (window.location.hash.includes("#compLatest")) {
            // Go to the latest revison
            wikiEditor.info.isLatestRevision(mw.config.get("wgRelevantPageName"), 0, ()=>{}); // auto filters and redirects for us - 0 is an ID that will never be
        } else if (window.location.hash.includes("#rollbackPreview")) {
            // Rollback preview iframe. NEEDS WORK. DON'T FORGET TO SET common.js back!!!
            $('.mw-revslider-container').html(`
            <style>
            #mw-navigation {
                display: none;
            }

            .mw-indicators {
                display:none;
            }

            .mw-body {margin-left:0;}

            .noprint { display: none; }
            .diff-ntitle {display: none; }
            .diff-otitle {display: none; }
            </style>
            <div style="padding-left:10px;">
                <h2>This is a rollback preview</h2>
                <a href="#" onclick="window.parent.parent.postMessage('closeDialog');">Click here</a> or the cross in the top-right corner to close this preview
            </div>

            <script>
            // We're ready
            window.parent.parent.postMessage('showBrwsrDialog');
            </script>

            <br>
            `);

            $('.mw-revslider-container').attr("style", "border: 3px solid red;");
            

        } else if (window.location.hash.includes("#rollbackFailNoRev")) {
            wikiEditor.visuals.toast.show("Could not rollback as there were no recent revisions by other users. Use the history page to try and manually revert.", false, false, 15000);
        }
        
        if (window.location.href.includes("&diff=") && window.location.href.includes("&oldid=")) {
            // Diff page
            wikiEditor.rollback.loadIcons(); // load rollback icons
        } else if (window.location.href.includes("/wiki/Special:RecentChanges")) {
            // Recent changes page
            // Add redwarn btn
            $(".mw-rcfilters-ui-filterWrapperWidget-bottom").prepend(`
            <div id="openRWP" class="icon material-icons"><span style="cursor: pointer;" onclick="wikiEditor.recentChanges.openPage(window.location.search.substr(1));">how_to_reg</span></div>
            <div class="mdl-tooltip mdl-tooltip--large" for="openRWP">
                Launch RedWarn Patrol with these filters
            </div>
            `); // Register tooltip
            for (let item of document.getElementsByClassName("mdl-tooltip")) {
                wikiEditor.visuals.register(item); 
            }

            wikiEditor.recentChanges.diffLinkAddRedWarn(); // Add redwarn to all links
        }
    });
}
var dialogEngine = {
    "init" : function(){
        $("body").append(`
        <div id="dialogEngineContainer">
        </div>
        `);
        // Add events
        addMessageHandler("closeDialog", ()=>{dialogEngine.dialog.close();}); // closing
    },
    "create" : (content, noPad)=>{ 
       
        $("#dialogEngineContainer").html(`
        <dialog class="mdl-dialog">
            `+ content +`
        </dialog>
        `);


        dialogEngine.dialog = document.querySelector('dialog');

        if (noPad) $("dialog").attr("style", "padding:inherit;"); // if no padding requested

        // Firefox issue fix
        if (! dialogEngine.dialog.showModal) {
            dialogPolyfill.registerDialog(dialogEngine.dialog);
        }
        
        return dialogEngine.dialog;
    }
}
/*
(c) Ed E 2020

NOTICE: All cross-domain addresses in containers MUST BE ABSOLUTE (i.e https:// rather than //)
*/
var mdlContainers = {
    "generateHtml" : innerContent => {
        let content = `
        <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
        <link href="https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&display=swap" rel="stylesheet">
        <link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
        <script defer src="https://code.getmdl.io/1.3.0/material.min.js"></script>`;

        // Themes
        let theme = "";
        if (wikiEditor.config.colTheme){
            theme = wikiEditor.config.colTheme;
        } else {
            theme = "blue-indigo"; // default theme
        }
        
        content += `
        <link rel="stylesheet" href="https://code.getmdl.io/1.3.0/material.`+ theme +`.min.css" />
        <!-- Material dropdown - MIT License, Copyright (c) 2016 CreativeIT https://github.com/CreativeIT/getmdl-select/blob/master/LICENSE.txt -->
        <style>
        .getmdl-select{outline:none}.getmdl-select .mdl-textfield__input{cursor:pointer}.getmdl-select .selected{background-color:#ddd}.getmdl-select .mdl-icon-toggle__label{float:right;margin-top:-30px;color:rgba(0,0,0,0.4);transform:rotate(0);transition:transform 0.3s}.getmdl-select.is-focused .mdl-icon-toggle__label{color:#3f51b5;transform:rotate(180deg)}.getmdl-select .mdl-menu__container{width:100% !important;margin-top:2px}.getmdl-select .mdl-menu__container .mdl-menu{width:100%}.getmdl-select .mdl-menu__container .mdl-menu .mdl-menu__item{font-size:16px}.getmdl-select__fix-height .mdl-menu__container .mdl-menu{overflow-y:auto;max-height:288px !important}.getmdl-select__fix-height .mdl-menu.mdl-menu--top-left{bottom:auto;top:0}
        </style>
        <script defer>
            "use strict";!function(){function e(){getmdlSelect.init(".getmdl-select")}window.addEventListener?window.addEventListener("load",e,!1):window.attachEvent&&window.attachEvent("onload",e)}();var getmdlSelect={_defaultValue:{width:300},_addEventListeners:function(e){var t=e.querySelector("input"),n=e.querySelector('input[type="hidden"]'),l=e.querySelectorAll("li"),a=e.querySelector(".mdl-js-menu"),o=e.querySelector(".mdl-icon-toggle__label"),i="",c="",s="",u=!1,d=function(o){var i=o.textContent.trim();if(t.value=i,l.forEach(function(e){e.classList.remove("selected")}),o.classList.add("selected"),e.MaterialTextfield.change(i),setTimeout(function(){e.MaterialTextfield.updateClasses_()},250),n.value=o.dataset.val||"",c=t.value,s=n.value,"createEvent"in document){var u=document.createEvent("HTMLEvents");u.initEvent("change",!1,!0),a.MaterialMenu.hide(),t.dispatchEvent(u)}else t.fireEvent("onchange")},r=function(){u=!1,t.value=c,n.value=s,e.querySelector(".mdl-menu__container").classList.contains("is-visible")||e.classList.remove("is-focused");var l=document.querySelectorAll(".getmdl-select .mdl-js-menu");[].forEach.call(l,function(e){e.MaterialMenu.hide()});var o=new Event("closeSelect");a.dispatchEvent(o)};document.body.addEventListener("click",r,!1),e.onkeydown=function(l){9==l.keyCode&&(t.value=c,n.value=s,a.MaterialMenu.hide(),e.classList.remove("is-focused"))},t.onfocus=function(e){a.MaterialMenu.show(),a.focus(),u=!0},t.onblur=function(e){e.stopPropagation()},t.onclick=function(t){t.stopPropagation(),a.classList.contains("is-visible")?(a.MaterialMenu.hide(),u=!1):(a.MaterialMenu.show(),r(),e.classList.add("is-focused"),u=!0)},t.onkeydown=function(l){27==l.keyCode&&(t.value=c,n.value=s,a.MaterialMenu.hide(),e.MaterialTextfield.onBlur_(),""!==i&&(e.querySelector(".mdl-textfield__label").textContent=i,i=""))},a.addEventListener("closeSelect",function(l){t.value=c,n.value=s,e.classList.remove("is-focused"),""!==i&&(e.querySelector(".mdl-textfield__label").textContent=i,i="")}),a.onkeydown=function(l){27==l.keyCode&&(t.value=c,n.value=s,e.classList.remove("is-focused"),""!==i&&(e.querySelector(".mdl-textfield__label").textContent=i,i=""))},o&&(o.onclick=function(l){l.stopPropagation(),u?(a.MaterialMenu.hide(),u=!1,e.classList.remove("is-focused"),e.MaterialTextfield.onBlur_(),t.value=c,n.value=s):(r(),e.MaterialTextfield.onFocus_(),t.focus(),a.MaterialMenu.show(),u=!0)}),[].forEach.call(l,function(n){n.onfocus=function(){e.classList.add("is-focused");var l=n.textContent.trim();t.value=l,e.classList.contains("mdl-textfield--floating-label")||""!=i||(i=e.querySelector(".mdl-textfield__label").textContent.trim(),e.querySelector(".mdl-textfield__label").textContent="")},n.onclick=function(){d(n)},n.dataset.selected&&d(n)})},init:function(e){var t=document.querySelectorAll(e);[].forEach.call(t,function(e){getmdlSelect._addEventListeners(e),componentHandler.upgradeElement(e),componentHandler.upgradeElement(e.querySelector("ul"))})}};
        </script>
        <!-- End material dropdown -->
        <body>
        `+innerContent+`
        </body>
        `;
        return content; // return
    },

    "generateContainer" : function(innerContent, width, height) {
        let url = URL.createObjectURL(new Blob([mdlContainers.generateHtml(innerContent)], { type: 'text/html' })); // blob url
        return `<iframe width="`+width+`" height="`+height+`" src="`+ url + `" frameborder="0" scrolling="no"></iframe>`;
    }
}
// Data processed from Twinkle Source at https://github.com/azatoth/twinkle
var rules = [
    {
        "name": "Vandalism",
        "catagory": "Common warnings",
        "template": "uw-vandalism",
        "warningLevels": [
            1,
            2,
            3,
            4,
            5
        ]
    },
    {
        "name": "Disruptive editing",
        "catagory": "Common warnings",
        "template": "uw-disruptive",
        "warningLevels": [
            1,
            2,
            3
        ]
    },
    {
        "name": "Editing tests",
        "catagory": "Common warnings",
        "template": "uw-test",
        "warningLevels": [
            1,
            2,
            3
        ]
    },
    {
        "name": "Removal of content, blanking",
        "catagory": "Common warnings",
        "template": "uw-delete",
        "warningLevels": [
            1,
            2,
            3,
            4,
            5
        ]
    },
    {
        "name": "Generic warning (for template series missing level 4)",
        "catagory": "Common warnings",
        "template": "uw-generic",
        "warningLevels": [
            4
        ]
    },
    {
        "name": "Adding unreferenced defamatory information about living persons",
        "catagory": "Behavior in articles",
        "template": "uw-biog",
        "warningLevels": [
            1,
            2,
            3,
            4,
            5
        ]
    },
    {
        "name": "Addition of defamatory content",
        "catagory": "Behavior in articles",
        "template": "uw-defamatory",
        "warningLevels": [
            1,
            2,
            3,
            4,
            5
        ]
    },
    {
        "name": "Introducing deliberate factual errors",
        "catagory": "Behavior in articles",
        "template": "uw-error",
        "warningLevels": [
            1,
            2,
            3,
            4
        ]
    },
    {
        "name": "Frequent or mass changes to genres without consensus or reference",
        "catagory": "Behavior in articles",
        "template": "uw-genre",
        "warningLevels": [
            1,
            2,
            3,
            4
        ]
    },
    {
        "name": "Image-related vandalism",
        "catagory": "Behavior in articles",
        "template": "uw-image",
        "warningLevels": [
            1,
            2,
            3,
            4,
            5
        ]
    },
    {
        "name": "Using improper humor",
        "catagory": "Behavior in articles",
        "template": "uw-joke",
        "warningLevels": [
            1,
            2,
            3,
            4,
            5
        ]
    },
    {
        "name": "Adding original research, including unpublished syntheses of sources",
        "catagory": "Behavior in articles",
        "template": "uw-nor",
        "warningLevels": [
            1,
            2,
            3,
            4
        ]
    },
    {
        "name": "Censorship of material",
        "catagory": "Behavior in articles",
        "template": "uw-notcensored",
        "warningLevels": [
            1,
            2,
            3
        ]
    },
    {
        "name": "Ownership of articles",
        "catagory": "Behavior in articles",
        "template": "uw-own",
        "warningLevels": [
            1,
            2,
            3,
            4,
            5
        ]
    },
    {
        "name": "Removal of maintenance templates",
        "catagory": "Behavior in articles",
        "template": "uw-tdel",
        "warningLevels": [
            1,
            2,
            3,
            4
        ]
    },
    {
        "name": "Addition of unsourced or improperly cited material",
        "catagory": "Behavior in articles",
        "template": "uw-unsourced",
        "warningLevels": [
            1,
            2,
            3,
            4
        ]
    },
    {
        "name": "Using Wikipedia for advertising or promotion",
        "catagory": "Promotions and spam",
        "template": "uw-advert",
        "warningLevels": [
            1,
            2,
            3,
            4,
            5
        ]
    },
    {
        "name": "Not adhering to neutral point of view",
        "catagory": "Promotions and spam",
        "template": "uw-npov",
        "warningLevels": [
            1,
            2,
            3,
            4
        ]
    },
    {
        "name": "Paid editing without disclosure under the Wikimedia Terms of Use",
        "catagory": "Promotions and spam",
        "template": "uw-paid",
        "warningLevels": [
            1,
            2,
            3,
            4
        ]
    },
    {
        "name": "Adding spam links",
        "catagory": "Promotions and spam",
        "template": "uw-spam",
        "warningLevels": [
            1,
            2,
            3,
            4,
            5
        ]
    },
    {
        "name": "Not assuming good faith",
        "catagory": "Behavior towards other editors",
        "template": "uw-agf",
        "warningLevels": [
            1,
            2,
            3
        ]
    },
    {
        "name": "Harassment of other users",
        "catagory": "Behavior towards other editors",
        "template": "uw-harass",
        "warningLevels": [
            1,
            2,
            3,
            4,
            5
        ]
    },
    {
        "name": "Personal attack directed at a specific editor",
        "catagory": "Behavior towards other editors",
        "template": "uw-npa",
        "warningLevels": [
            1,
            2,
            3,
            4,
            5
        ]
    },
    {
        "name": "Improper use of warning or blocking template",
        "catagory": "Behavior towards other editors",
        "template": "uw-tempabuse",
        "warningLevels": [
            1,
            2
        ]
    },
    {
        "name": "Removing {{afd}} templates",
        "catagory": "Removal of deletion tags",
        "template": "uw-afd",
        "warningLevels": [
            1,
            2,
            3,
            4
        ]
    },
    {
        "name": "Removing {{blp prod}} templates",
        "catagory": "Removal of deletion tags",
        "template": "uw-blpprod",
        "warningLevels": [
            1,
            2,
            3,
            4
        ]
    },
    {
        "name": "Removing file deletion tags",
        "catagory": "Removal of deletion tags",
        "template": "uw-idt",
        "warningLevels": [
            1,
            2,
            3,
            4
        ]
    },
    {
        "name": "Removing speedy deletion tags",
        "catagory": "Removal of deletion tags",
        "template": "uw-speedy",
        "warningLevels": [
            1,
            2,
            3,
            4
        ]
    },
    {
        "name": "Triggering the edit filter",
        "catagory": "Other",
        "template": "uw-attempt",
        "warningLevels": [
            1,
            2,
            3,
            4
        ]
    },
    {
        "name": "Using talk page as forum",
        "catagory": "Other",
        "template": "uw-chat",
        "warningLevels": [
            1,
            2,
            3,
            4
        ]
    },
    {
        "name": "Creating inappropriate pages",
        "catagory": "Other",
        "template": "uw-create",
        "warningLevels": [
            1,
            2,
            3,
            4,
            5
        ]
    },
    {
        "name": "Manual of style",
        "catagory": "Other",
        "template": "uw-mos",
        "warningLevels": [
            1,
            2,
            3,
            4
        ]
    },
    {
        "name": "Page moves against naming conventions or consensus",
        "catagory": "Other",
        "template": "uw-move",
        "warningLevels": [
            1,
            2,
            3,
            4,
            5
        ]
    },
    {
        "name": "Refactoring others' talk page comments",
        "catagory": "Other",
        "template": "uw-tpv",
        "warningLevels": [
            1,
            2,
            3,
            4,
            5
        ]
    },
    {
        "name": "Uploading unencyclopedic images",
        "catagory": "Other",
        "template": "uw-upload",
        "warningLevels": [
            1,
            2,
            3,
            4,
            5
        ]
    },
    {
        "name": "Bad AIV report",
        "catagory": "Single Notice",
        "template": "uw-aiv",
        "warningLevels": [
            0
        ]
    },
    {
        "name": "Creating autobiographies",
        "catagory": "Single Notice",
        "template": "uw-autobiography",
        "warningLevels": [
            0
        ]
    },
    {
        "name": "Adding incorrect categories",
        "catagory": "Single Notice",
        "template": "uw-badcat",
        "warningLevels": [
            0
        ]
    },
    {
        "name": "Adding inappropriate entries to lists",
        "catagory": "Single Notice",
        "template": "uw-badlistentry",
        "warningLevels": [
            0
        ]
    },
    {
        "name": "Being harsh to newcomers",
        "catagory": "Single Notice",
        "template": "uw-bite",
        "warningLevels": [
            0
        ]
    },
    {
        "name": "Conflict of interest",
        "catagory": "Single Notice",
        "template": "uw-coi",
        "warningLevels": [
            0
        ]
    },
    {
        "name": "Introducing controversial material",
        "catagory": "Single Notice",
        "template": "uw-controversial",
        "warningLevels": [
            0
        ]
    },
    {
        "name": "Copying text to another page",
        "catagory": "Single Notice",
        "template": "uw-copying",
        "warningLevels": [
            0
        ]
    },
    {
        "name": "Adding speculative or unconfirmed information",
        "catagory": "Single Notice",
        "template": "uw-crystal",
        "warningLevels": [
            0
        ]
    },
    {
        "name": "Cut and paste moves",
        "catagory": "Single Notice",
        "template": "uw-c&pmove",
        "warningLevels": [
            0
        ]
    },
    {
        "name": "Incorrect edit to a disambiguation page",
        "catagory": "Single Notice",
        "template": "uw-dab",
        "warningLevels": [
            0
        ]
    },
    {
        "name": "Unnecessarily changing date formats",
        "catagory": "Single Notice",
        "template": "uw-date",
        "warningLevels": [
            0
        ]
    },
    {
        "name": "Removing proper sources containing dead links",
        "catagory": "Single Notice",
        "template": "uw-deadlink",
        "warningLevels": [
            0
        ]
    },
    {
        "name": "User should draft in userspace without the risk of speedy deletion",
        "catagory": "Single Notice",
        "template": "uw-draftfirst",
        "warningLevels": [
            0
        ]
    },
    {
        "name": "Not using edit summary",
        "catagory": "Single Notice",
        "template": "uw-editsummary",
        "warningLevels": [
            0
        ]
    },
    {
        "name": "Adding external links to the body of an article",
        "catagory": "Single Notice",
        "template": "uw-elinbody",
        "warningLevels": [
            0
        ]
    },
    {
        "name": "Not communicating in English",
        "catagory": "Single Notice",
        "template": "uw-english",
        "warningLevels": [
            0
        ]
    },
    {
        "name": "Hasty addition of speedy deletion tags",
        "catagory": "Single Notice",
        "template": "uw-hasty",
        "warningLevels": [
            0
        ]
    },
    {
        "name": "Italicize books, films, albums, magazines, TV series, etc within articles",
        "catagory": "Single Notice",
        "template": "uw-italicize",
        "warningLevels": [
            0
        ]
    },
    {
        "name": "Unnecessarily changing between British and American English",
        "catagory": "Single Notice",
        "template": "uw-lang",
        "warningLevels": [
            0
        ]
    },
    {
        "name": "Excessive addition of redlinks or repeated blue links",
        "catagory": "Single Notice",
        "template": "uw-linking",
        "warningLevels": [
            0
        ]
    },
    {
        "name": "Incorrect use of minor edits check box",
        "catagory": "Single Notice",
        "template": "uw-minor",
        "warningLevels": [
            0
        ]
    },
    {
        "name": "Creating non-English articles",
        "catagory": "Single Notice",
        "template": "uw-notenglish",
        "warningLevels": [
            0
        ]
    },
    {
        "name": "We use consensus, not voting",
        "catagory": "Single Notice",
        "template": "uw-notvote",
        "warningLevels": [
            0
        ]
    },
    {
        "name": "Copying from public domain sources without attribution",
        "catagory": "Single Notice",
        "template": "uw-plagiarism",
        "warningLevels": [
            0
        ]
    },
    {
        "name": "Use preview button to avoid mistakes",
        "catagory": "Single Notice",
        "template": "uw-preview",
        "warningLevels": [
            0
        ]
    },
    {
        "name": "Indiscriminate removal of redlinks",
        "catagory": "Single Notice",
        "template": "uw-redlink",
        "warningLevels": [
            0
        ]
    },
    {
        "name": "Reverting self tests",
        "catagory": "Single Notice",
        "template": "uw-selfrevert",
        "warningLevels": [
            0
        ]
    },
    {
        "name": "Wikipedia is not a social network",
        "catagory": "Single Notice",
        "template": "uw-socialnetwork",
        "warningLevels": [
            0
        ]
    },
    {
        "name": "Be bold and fix things yourself",
        "catagory": "Single Notice",
        "template": "uw-sofixit",
        "warningLevels": [
            0
        ]
    },
    {
        "name": "Adding spoiler alerts or removing spoilers from appropriate sections",
        "catagory": "Single Notice",
        "template": "uw-spoiler",
        "warningLevels": [
            0
        ]
    },
    {
        "name": "Talk in article",
        "catagory": "Single Notice",
        "template": "uw-talkinarticle",
        "warningLevels": [
            0
        ]
    },
    {
        "name": "Not signing posts",
        "catagory": "Single Notice",
        "template": "uw-tilde",
        "warningLevels": [
            0
        ]
    },
    {
        "name": "Posting at the top of talk pages",
        "catagory": "Single Notice",
        "template": "uw-toppost",
        "warningLevels": [
            0
        ]
    },
    {
        "name": "Stale userspace draft",
        "catagory": "Single Notice",
        "template": "uw-userspace draft finish",
        "warningLevels": [
            0
        ]
    },
    {
        "name": "Adding video game walkthroughs, cheats or instructions",
        "catagory": "Single Notice",
        "template": "uw-vgscope",
        "warningLevels": [
            0
        ]
    },
    {
        "name": "Place user warning templates when reverting vandalism",
        "catagory": "Single Notice",
        "template": "uw-warn",
        "warningLevels": [
            0
        ]
    },
    {
        "name": "Using inaccurate or inappropriate edit summaries",
        "catagory": "Single Notice",
        "template": "uw-wrongsummary",
        "warningLevels": [
            0
        ]
    },
    {
        "name": "Potential three-revert rule violation; see also uw-ew",
        "catagory": "Single Warning",
        "template": "uw-3rr",
        "warningLevels": [
            6
        ]
    },
    {
        "name": "Affiliate marketing",
        "catagory": "Single Warning",
        "template": "uw-affiliate",
        "warningLevels": [
            6
        ]
    },
    {
        "name": "Use of multiple accounts (assuming good faith)",
        "catagory": "Single Warning",
        "template": "uw-agf-sock",
        "warningLevels": [
            6
        ]
    },
    {
        "name": "Creating attack pages",
        "catagory": "Single Warning",
        "template": "uw-attack",
        "warningLevels": [
            6
        ]
    },
    {
        "name": "Business promotion",
        "catagory": "Single Warning",
        "template": "uw-bizlist",
        "warningLevels": [
            6
        ]
    },
    {
        "name": "Bot username",
        "catagory": "Single Warning",
        "template": "uw-botun",
        "warningLevels": [
            6
        ],
        "note" : "Username notices should not be added for blatent violations. In these cases, click the gavel to report the username to the admins."
    },
    {
        "name": "Canvassing",
        "catagory": "Single Warning",
        "template": "uw-canvass",
        "warningLevels": [
            6
        ]
    },
    {
        "name": "Copyright violation",
        "catagory": "Single Warning",
        "template": "uw-copyright",
        "warningLevels": [
            6
        ]
    },
    {
        "name": "Linking to copyrighted works violation",
        "catagory": "Single Warning",
        "template": "uw-copyright-link",
        "warningLevels": [
            6
        ]
    },
    {
        "name": "Copyright violation (with explanation for new users)",
        "catagory": "Single Warning",
        "template": "uw-copyright-new",
        "warningLevels": [
            6
        ]
    },
    {
        "name": "Removing {{copyvio}} template from articles",
        "catagory": "Single Warning",
        "template": "uw-copyright-remove",
        "warningLevels": [
            6
        ]
    },
    {
        "name": "Edit summary triggering the edit filter",
        "catagory": "Single Warning",
        "template": "uw-efsummary",
        "warningLevels": [
            6
        ]
    },
    {
        "name": "Edit warring (stronger wording)",
        "catagory": "Single Warning",
        "template": "uw-ew",
        "warningLevels": [
            6
        ]
    },
    {
        "name": "Edit warring (softer wording for newcomers)",
        "catagory": "Single Warning",
        "template": "uw-ewsoft",
        "warningLevels": [
            6
        ]
    },
    {
        "name": "Hijacking articles",
        "catagory": "Single Warning",
        "template": "uw-hijacking",
        "warningLevels": [
            6
        ]
    },
    {
        "name": "Creating hoaxes",
        "catagory": "Single Warning",
        "template": "uw-hoax",
        "warningLevels": [
            6
        ]
    },
    {
        "name": "Making legal threats",
        "catagory": "Single Warning",
        "template": "uw-legal",
        "warningLevels": [
            6
        ]
    },
    {
        "name": "Editing while logged out",
        "catagory": "Single Warning",
        "template": "uw-login",
        "warningLevels": [
            6
        ]
    },
    {
        "name": "Usage of multiple IPs",
        "catagory": "Single Warning",
        "template": "uw-multipleIPs",
        "warningLevels": [
            6
        ]
    },
    {
        "name": "Personal info",
        "catagory": "Single Warning",
        "template": "uw-pinfo",
        "warningLevels": [
            6
        ]
    },
    {
        "name": "Recreating salted articles under a different title",
        "catagory": "Single Warning",
        "template": "uw-salt",
        "warningLevels": [
            6
        ]
    },
    {
        "name": "Sockpuppetry",
        "catagory": "Single Warning",
        "template": "uw-socksuspect",
        "warningLevels": [
            6
        ]
    },
    {
        "name": "Userpage vandalism",
        "catagory": "Single Warning",
        "template": "uw-upv",
        "warningLevels": [
            6
        ]
    },
    {
        "name": "Username is against policy",
        "catagory": "Single Warning",
        "template": "uw-username",
        "warningLevels": [
            6
        ],
        "note" : "Username notices should not be added for blatent violations. In these cases, click the gavel to report the username to the admins."
    },
    {
        "name": "Username is against policy, and conflict of interest",
        "catagory": "Single Warning",
        "template": "uw-coi-username",
        "warningLevels": [
            6
        ],
        "note" : "Username notices should not be added for blatent violations. In these cases, click the gavel to report the username to the admins."
    },
    {
        "name": "Userpage or subpage is against policy",
        "catagory": "Single Warning",
        "template": "uw-userpage",
        "warningLevels": [
            6
        ],
        "note" : "Username notices should not be added for blatent violations. In these cases, click the gavel to report the username to the admins."
    }
];
// Used to manage the toast notifications
// init is seperate here as it isn't always needed or used

/*
    EXAMPLE SYNTAX:
    Required somewhere: wikiEditor.visuals.toast.init();
    Then:
    (for no button) wikiEditor.visuals.toast.show("Text", false, false, 2000);
    (for button) wikiEditor.visuals.toast.show("Text", "BtnTxt", function() {
        // your code here
    }, 5000);
*/

wikiEditor.visuals.toast = {
    
    "active" : false,

    "init" : function(){
        if (!wikiEditor.visuals.toast.active) { // If init already done, no need
            $('body').append(`
                <div id="wikiEditor-toast" class="mdl-js-snackbar mdl-snackbar">
                <div class="mdl-snackbar__text"></div>
                <button class="mdl-snackbar__action" type="button"></button>
                </div>
            `); // init
            (function() {
                'use strict';
                window['counter'] = 0;
                var toast = document.querySelector('#wikiEditor-toast');
                wikiEditor.visuals.register(toast); // register comp

                // create function
                wikiEditor.visuals.toast.show = (text, buttonTxt, btnClick, tOut) => {
                    'use strict';
                    if (buttonTxt) {
                        // Show with action and button
                        toast.MaterialSnackbar.showSnackbar({message: text, actionHandler: btnClick, actionText: buttonTxt, timeout: tOut}); 
                    } else {
                        // Show just message
                        toast.MaterialSnackbar.showSnackbar({message: text, timeout: tOut});
                    }
                };
                }());

            // Init done. Register.
            wikiEditor.visuals.toast.active = true;
        }
    },

    "show" : function(text, buttonTxt, btnClick) {} // made in init()

}
// API calls ext.
wikiEditor.info = { // API
    "targetUsername": un=>{
        if (un) {return un;} // return username if defined
        return mw.config.values.wgRelevantUserName},
    "getUsername":  ()=>{return mw.config.values.wgUserName},

    "getConfig": (callback) => {
        if (wikiEditor.config) {callback();} // if config loaded, no need to reload

        let defaultConfig = { // Default config on reset or anything like that
            "lastVersion" : wikiEditor.version
        };


        // gets user config from their page. 
        let user = wikiEditor.info.getUsername();
        $.getJSON("https://en.wikipedia.org/w/api.php?action=query&prop=revisions&titles=User:"+user+"/redwarnConfig.js&rvslots=*&rvprop=content&formatversion=2&format=json", latestR=>{
            // Grab text from latest revision of talk page
            // Check if exists
            let revisionWikitext = "";
            if (!latestR.query.pages[0].missing) { // If page isn't missing, i.e exists
                revisionWikitext = latestR.query.pages[0].revisions[0].slots.main.content;
            } else {
                // Config doesn't exist  we need to make it
                console.log("creating config file");
                wikiEditor.config = defaultConfig;
                wikiEditor.info.writeConfig(callback); // write new config file
                return;
            }

            // Now that's done, verify config file / load it
            try {
                eval(revisionWikitext);
                if (!wikiEditor.config) {throw "no config";}
            } catch (err) {
                // Corrupt config file
                wikiEditor.config = defaultConfig;
                wikiEditor.visuals.toast.show("Your config file is corrupt.");
            }
            callback(); // we done
        });
    },

    "writeConfig": (noRedirect, callback)=> { // CALLBACK ONLY IF NOREDIRECT IS TRUE.
        // Write config to the users page and refresh
        let finalTxt = `
/*<nowiki>
+-----------------------------------------+
|                                         |
|  !!!! DO NOT ADD THIS FILE TO YOUR !!!! |
|      common.js FILE. RedWarn loads      |
|      this script automatically.         |
|                                         |
+-----------------------------------------+

+-----------------------------------------+
|          Generated by RedWarn           |
+-----------------------------------------+

*/
wikiEditor.config = `+ JSON.stringify(wikiEditor.config) +"; //</nowiki>"; // generate config text
        $.post("https://en.wikipedia.org/w/api.php", {
                "action": "edit",
                "format": "json",
                "token" : mw.user.tokens.get("csrfToken"),
                "title" : "User:"+ wikiEditor.info.getUsername() + "/redwarnConfig.js",
                "summary" : "Apply changes to config [[WP:REDWARN|(RedWarn)]]", // summary sign here
                "text": finalTxt
            }).done(dt => {
                // We done. Check for errors, then callback appropriately
                if (!dt.edit) {
                    // Error occured or other issue
                    console.error(dt);
                    wikiEditor.visuals.toast.show("Sorry, there was an error. See the console for more info. Your changes have not been saved.");
                } else {
                    // Success!
                    if (noRedirect) {callback(); return;}; // DO NOT continue if no redirect is requested
                    window.location.hash = "#configChange";
                    window.location.reload(); // we done
                }
            });
    },

    "featureRestrictPermissionLevel": (l, callback, callbackIfNot)=> {
        // Restrict feature to users in this group
        mw.user.getGroups(g=>{
            let hasPerm = g.includes(l);
            if ((l == "confirmed") && !hasPerm) {hasPerm = g.includes("autoconfirmed");} // Due to 2 types of confirmed user, confirmed and autoconfirmed, we have to check both
            if (hasPerm) {
                // Has the permission needed
                if (callback) {
                    callback();
                }
            } else {
                if (callbackIfNot) {
                    // Make no perm callback
                    callbackIfNot();
                } else {
                    // Show no perm toast
                    wikiEditor.visuals.toast.show("Your account doesn't have permission to do that yet.", false, false, 5000);
                }
            }
        });
    },

    "getRelatedPage" : (pg)=> {
        if (pg) {return pg;} // return page if defined
        try {
            let x = mw.util.getParamValue('vanarticle');
            if (x != null) {return x;} else {return "";}
        } catch (er) {
            // If none
            return "error";
        }  
    },

    "parseWikitext" : (wikiTxt, callback) => { // Uses Wikipedia's API to turn Wikitext to string. NEED TO USE POST IF USERPAGE IS LARGE EXT..
    $.post("https://en.wikipedia.org/w/api.php", {
                "action": "parse",
                "format": "json",
                "contentmodel" : "wikitext",
                "prop": "text",
                "pst": true,
                "assert": "user",
                "text": wikiTxt
            }).done(r => {
                let processedResult = r.parse.text['*'].replace(/\/\//g, "https://").replace(/href=\"\/wiki/g, `href="/wiki`); // regex replace w direct urls
                callback(processedResult); // make callback w HTML
            });
    },

    "lastWarningLevel" : (user, callback)=> { // callback(wLevel. thisMonthsNotices, userPg) 0 none 1 notice 2 caution 3 warning 4 final warning
        // Get the last warning level of a user this month
        $.getJSON("https://en.wikipedia.org/w/api.php?action=query&prop=revisions&titles=User_talk:"+user+"&rvslots=*&rvprop=content&formatversion=2&format=json", latestR=>{
            // Grab text from latest revision of talk page
            // Check if exists
            let revisionWikitext = "";
            if (!latestR.query.pages[0].missing) { // If page isn't missing, i.e exists
                revisionWikitext = latestR.query.pages[0].revisions[0].slots.main.content;
            } else {
                // Return that record is clean as no past warnings due to page not existing
                callback(0, "Talk page doesn't exist.", "Talk page doesn't exist."); // exit
                return;
            }
            let wikiTxtLines = revisionWikitext.split("\n");
            // let's continue
            // Returns date in == Month Year == format and matches
            let currentDateHeading = ((d)=>{return "== " + ['January','February','March','April','May','June','July','August','September','October','November','December'][d.getMonth()] + " " + (1900 + d.getYear()) + " =="})(new Date);
            let pageIncludesCurrentDate = wikiTxtLines.includes(currentDateHeading);
            if (!pageIncludesCurrentDate) {
                // No warnings this month
                callback(0, "No notices for this month.", revisionWikitext);
                return;
            }

            let highestWarningLevel = 0; // Set highest to nothing so if there is a date title w nothing in then that will be reported 
            let thisMonthsNotices = ""; // for dialog
            // For each line
            for (let i = wikiTxtLines.indexOf(currentDateHeading) + 1; i < wikiTxtLines.length; i++) {
                if (wikiTxtLines[i].startsWith("==")) {
                    // New section
                    break; // exit the loop
                }

                // Check if it contains logo for each level
                thisMonthsNotices += wikiTxtLines[i]; // Add to this months
                if (wikiTxtLines[i].includes("File:Stop hand nuvola.svg")) { // Level 4 warning
                    // This is the highest warning level. We can leave now
                    highestWarningLevel = 4;
                    break; // exit the loop
                }

                // Not using elseif in case of formatting ext..

                if (wikiTxtLines[i].includes("File:Nuvola apps important.svg")) { // Level 3 warning
                    highestWarningLevel = 3; // No need for if check as highest level exits
                }

                if (wikiTxtLines[i].includes("File:Information orange.svg")) { // Level 2 warning
                    if (highestWarningLevel < 3) {
                        // We can set
                        highestWarningLevel = 2;
                    }
                }

                if (wikiTxtLines[i].includes("File:Information.svg")) { // Level 1 notice
                    if (highestWarningLevel < 2) {
                        // We can set
                        highestWarningLevel = 1;
                    }
                }
            } // End for loop

            callback(highestWarningLevel, thisMonthsNotices, revisionWikitext); // We done

        });
    },// End lastWarningLevel

    "addWikiTextToUserPage" : (user, text, underDate, summary, blacklist, blacklistToast) => {
        // Add text to a page. If underdate true, add it under a date marker
        wikiEditor.visuals.toast.show("Please wait...", false, false, 2500);
        $.getJSON("https://en.wikipedia.org/w/api.php?action=query&prop=revisions&titles=User_talk:"+user+"&rvslots=*&rvprop=content&formatversion=2&format=json", latestR=>{
            // Grab text from latest revision of talk page
            // Check if exists
            let revisionWikitext = "";
            if (!latestR.query.pages[0].missing) { // If page isn't missing, i.e exists
                revisionWikitext = latestR.query.pages[0].revisions[0].slots.main.content;
            } // else we keep to ""
            let wikiTxtLines = revisionWikitext.split("\n");
            let finalTxt = "";

            // Check blacklist (if defined)
            if (blacklist) {
                if (revisionWikitext.includes(blacklist)) {
                    // Don't continue and show toast
                    wikiEditor.visuals.toast.show(blacklistToast, false, false, 5000);
                    return;
                }
            }

            // let's continue
            // Returns date in == Month Year == format and matches
            let currentDateHeading = ((d)=>{return "== " + ['January','February','March','April','May','June','July','August','September','October','November','December'][d.getMonth()] + " " + (1900 + d.getYear()) + " =="})(new Date);
            let pageIncludesCurrentDate = wikiTxtLines.includes(currentDateHeading);
            
            if (underDate) {
                if (pageIncludesCurrentDate) {
                    // Locate and add text in section

                    // Locate where the current date section ends so we can append ours to the bottom
                    let locationOfLastLine = wikiTxtLines.indexOf(currentDateHeading) + 1; // in case of date heading w nothing under it
                    for (let i = wikiTxtLines.indexOf(currentDateHeading) + 1; i < wikiTxtLines.length; i++) {
                        if (wikiTxtLines[i].startsWith("==")) {
                            // New section
                            locationOfLastLine = i - 1; // the line above is therefore the last
                            console.log("exiting loop: " +wikiTxtLines[locationOfLastLine]);
                            break; // exit the loop
                        } else if (i == wikiTxtLines.length - 1) {
                            // End of page, let's break and set location of last line.
                            locationOfLastLine = i;
                            break; // exit loop
                        }
                    }
                    console.log(locationOfLastLine);
                    if (locationOfLastLine == wikiTxtLines.length - 1) {
                        // To prevent to end notices squishing against eachother
                        // Same as without, but we just include the date string at bottom of page
                        wikiTxtLines.push(["\n\n" + text]);
                    } else {
                        wikiTxtLines.splice(locationOfLastLine, 0, ["\n\n" + text]); // Add notice to array at correct position. Note the "" at the start is for a newline to seperate from prev content
                    }
                } else { // Page doesn't have current date
                    // Same as without, but we just include the date string at bottom of page
                    wikiTxtLines.push(["\n" + currentDateHeading + "\n\n" + text]);
                }
            } else {
                // No need to add to date. Just shove at the bottom of the page
                wikiTxtLines.push([text]);
            }

            // Process final string
            wikiTxtLines.forEach(ln => finalTxt = finalTxt + ln + "\n"); // Remap to lines
            console.log(finalTxt);

            // Push edit using CSRF token
            $.post("https://en.wikipedia.org/w/api.php", {
                "action": "edit",
                "format": "json",
                "token" : mw.user.tokens.get("csrfToken"),
                "title" : "User_talk:"+ user,
                "summary" : summary + " [[WP:REDWARN|(RedWarn)]]", // summary sign here
                "text": finalTxt
            }).done(dt => {
                // We done. Check for errors, then callback appropriately
                if (!dt.edit) {
                    // Error occured or other issue
                    console.error(dt);
                    wikiEditor.visuals.toast.show("Sorry, there was an error. See the console for more info. Your message has not been sent.");
                    // Reshow dialog
                    dialogEngine.dialog.showModal();
                } else {
                    // Success! Redirect to complete page
                    let reloadNeeded = window.location.href.includes("https://en.wikipedia.org/wiki/User_talk:"+ user); // if we are already on the talk page we need to refresh as this would just change the hash
                    redirect("https://en.wikipedia.org/wiki/User_talk:"+ user + "#noticeApplied-" + dt.edit.newrevid + "-" + dt.edit.oldrevid); // go to talk page
                    if (reloadNeeded) {location.reload();}
                    // We done
                }
            });
        }); 
    }, // end addTextToUserPage

    "quickWelcome" : un=>{
        // Quickly welcome the current user
        wikiEditor.info.addWikiTextToUserPage(wikiEditor.info.targetUsername(un), "\n"+ wikiEditor.welcome() +" " + wikiEditor.sign() +"\n", false, "Welcome!");
    },

    // Used for rollback
    "isLatestRevision" : (name, revID, callback) => { // callback(username) only if successful!! in other cases, will REDIRECT to latest revison compare page
        // Check if revsion is the latest revision
        $.getJSON("https://en.wikipedia.org/w/api.php?action=query&prop=revisions&titles="+ encodeURIComponent(name) +"&rvslots=*&rvprop=ids%7Cuser&formatversion=2&format=json", r=>{
            // We got the response
            let latestRId = r.query.pages[0].revisions[0].revid;
            let parentRId = r.query.pages[0].revisions[0].parentid;
            let latestUsername = r.query.pages[0].revisions[0].user;
            if (latestRId == revID) {
                // Yup! Send the callback
                callback(latestUsername);
            } else {
                // Nope :(
                // Load the preview page of the latest one
                try {if (dialogEngine.dialog.open) {return;}} catch (error) {} // DO NOT REDIRECT IF DIALOG IS OPEN.
                redirect("https://en.wikipedia.org/w/index.php?title="+ encodeURIComponent(name) +"&diff="+ latestRId +"&oldid="+ parentRId +"&diffmode=source#redirectLatestRevision");
            }
        });
    },

    "latestRevisionNotByUser" : (name, username, callback) => { // CALLBACK revision, summaryText, rId
        // Name is page name, username is bad username
        $.getJSON("https://en.wikipedia.org/w/api.php?action=query&prop=revisions&titles="+ encodeURIComponent(name) +"&rvslots=*&rvprop=ids%7Cuser%7Ccontent&rvexcludeuser="+ username +"&formatversion=2&format=json", r=>{
            // We got the response
            let _r;
            try {
                _r = r.query.pages[0].revisions[0]; // get latest revision
                if (_r == null) { throw "can't be null"; } // if empty error
            } catch (error) {
                // Probably no other edits. Redirect to history page and show the notice
                redirect("https://en.wikipedia.org/w/index.php?title="+ encodeURIComponent(name) +"&action=history#rollbackFailNoRev");
                return; // exit
            }



            console.log("_r");
            let latestContent = _r.slots.main.content;
            let summary = "Rollback recent rev. by [[Special:Contributions/"+ username +"|"+ username +"]] ([[User_talk:"+ username +"|talk]]) to rev. "+ _r.revid +" by " +_r.user;
            callback(latestContent, summary, _r.revid);
        });
    },

    "stripWikiTxt" : wikiTxt=> {
        // VERY BASIC Convert WikiText to string
        try {
            wikiTxt = wikiTxt.replace(/<!--[^>]*-->/g, ""); // Strip html comments
            wikiTxt.match(/\[\[(?:[^\]\]]*)\]\]/g).forEach(t=>{ // match [[*]]
                txtToKeep = (a=>{return a[a.length - 1];})(t.split("|")).replace("]]", ""); // Last | iin the string then rm the end
                wikiTxt = wikiTxt.replace(t, txtToKeep); // now replace
            });
            wikiTxt = wikiTxt.replace(/'''/g, ""); // rm bold
            wikiTxt = wikiTxt.replace(/''/g, ""); // rm italic
        } catch (err) {} // Probably no wikitxt there
         return wikiTxt;
    }
    
};
wikiEditor.rollback = { // Rollback features
    "preview" : () => { // Redirect to the preview of the rollback (compare page)
        // Check if latest, else redirect
        wikiEditor.visuals.toast.show("Please wait...");
        wikiEditor.info.isLatestRevision(mw.config.get("wgRelevantPageName"), mw.util.getParamValue("diff"), un=>{
            // Fetch latest revision not by user
            wikiEditor.info.latestRevisionNotByUser(mw.config.get("wgRelevantPageName"), un, (content, summary, rID) => {
                // Got it! Now open preview dialog

                // Add handler for when page loaded

                addMessageHandler("showBrwsrDialog", c=> {
                    // We ready to show
                    dialogEngine.dialog.showModal();
                });

                let url = "https://en.wikipedia.org/w/index.php?title="+ mw.config.get("wgRelevantPageName") +"&diff="+ rID +"&oldid="+ mw.util.getParamValue("diff") +"&diffmode=source#rollbackPreview";

                dialogEngine.create(mdlContainers.generateContainer(`
                <div id="close" class="icon material-icons" style="float:right;">
                    <span style="cursor: pointer; padding-right:15px;" onclick="window.parent.postMessage('closeDialog');">
                        clear
                    </span>
                </div>
                <div class="mdl-tooltip" for="close">
                    Close
                </div>
                <iframe src="`+ url +`" frameborder="0" style="height:95%;"></iframe>
                `, document.body.offsetWidth-70, document.body.offsetHeight-50)); // DON'T SHOW until we get the loaded message (see above)
                
            });
        });
    },

    "apply" : (reason) => {
        // Apply rollback
        wikiEditor.visuals.toast.show("Please wait...", false, false, 1000);
        wikiEditor.info.isLatestRevision(mw.config.get("wgRelevantPageName"), mw.util.getParamValue("diff"), un=>{
            // Fetch latest revision not by user
            wikiEditor.info.latestRevisionNotByUser(mw.config.get("wgRelevantPageName"), un, (content, summary, rID) => {
                // Got it! Now set page content to summary
                // Push edit using CSRF token
                $.post("https://en.wikipedia.org/w/api.php", {
                    "action": "edit",
                    "format": "json",
                    "token" : mw.user.tokens.get("csrfToken"),
                    "title" : mw.config.get("wgRelevantPageName"),
                    "summary" : summary + ": " + reason + " [[WP:REDWARN|(RedWarn)]]", // summary sign here
                    "text": content,
                    "tags": "undo" // Tag with undo flag
                }).done(dt => {
                    // We done. Check for errors, then callback appropriately
                    if (!dt.edit) {
                        // Error occured or other issue
                        console.error(dt);
                        wikiEditor.visuals.toast.show("Sorry, there was an error, likely an edit conflict. Your rollback has not been applied.");

                    } else {
                        
                        // Success! Now show warning dialog but w correct info
                        wikiEditor.ui.beginWarn(false, un, mw.config.get("wgRelevantPageName"));

                        wikiEditor.visuals.toast.show("Rollback complete.", "DON'T WARN AND VIEW", ()=>{
                            wikiEditor.info.isLatestRevision(mw.config.get('wgRelevantPageName'), 0, ()=>{});
                        }, 5000); // clicking undo takes to the closest revision, has to be here to overlay the dialog
                    }
                });
            });
        });
    },

    "restore" : (revID, reason) => {
        // Restore revision by ID
        wikiEditor.visuals.toast.show("Restoring...", false, false, 4000);
        // Ask API for this revision
        $.getJSON("https://en.wikipedia.org/w/api.php?action=query&prop=revisions&rvprop=user|content&rvstartid="+ revID +"&rvendid="+ revID +"&titles="+ encodeURI(mw.config.get("wgRelevantPageName")) +"&formatversion=2&rvslots=*&format=json", r=>{
            let revUsr = r.query.pages[0].revisions[0].user; // get user
            let content = r.query.pages[0].revisions[0].slots.main.content; // get content
            let summary = "Restoring revision "+ revID + " by " + revUsr; // gen our summary
            // Now we've got that, we just need to submit.
            $.post("https://en.wikipedia.org/w/api.php", {
                    "action": "edit",
                    "format": "json",
                    "token" : mw.user.tokens.get("csrfToken"),
                    "title" : mw.config.get("wgRelevantPageName"),
                    "summary" : summary + ": " + reason + " [[WP:REDWARN|(RedWarn)]]", // summary sign here
                    "text": content,
                    "tags": "undo" // Tag with undo flag
                }).done(dt => {
                    // Request done. Check for errors, then go to the latest revision
                    if (!dt.edit) {
                        // Error occured or other issue
                        console.error(dt);
                        wikiEditor.visuals.toast.show("Sorry, there was an error, likely an edit conflict. This edit has not been restored.");
                    } else {
                        wikiEditor.info.isLatestRevision(mw.config.get('wgRelevantPageName'), 0, ()=>{}); // we done, go to the latest revision
                    }
                });
        });
    },

    "promptRollbackReason" : reason=> {
        wikiEditor.info.isLatestRevision(mw.config.get("wgRelevantPageName"), mw.util.getParamValue("diff"),un=>{ // validate is latest
            // Show dialog then rollback
            // Add submit handler

            addMessageHandler("reason`*", rs=>wikiEditor.rollback.apply(rs.split("`")[1])); // When reason recieved, submit rollback

            // CREATE DIALOG
            // MDL FULLY SUPPORTED HERE (container). 
            dialogEngine.create(mdlContainers.generateContainer(`
            <!-- ROLLBACK REASON DIALOG -->
<form id="newMsgForm" onsubmit="pushRollback();" action="#">
    <div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label" style="width:100%">
        <input class="mdl-textfield__input" type="text" name="rollbackReason" id="rollbackReason" value="`+ reason +`" minlength="5">
        <label class="mdl-textfield__label" for="rollbackReason">Rollback Reason</label>
        <span class="mdl-textfield__error">You must enter a longer rollback reason.</span>
        <div class="mdl-tooltip" for="rollbackReason">
            Enter a reason for this rollback
        </div>
    </div>
</form>
<span style="float:right;">
    <button class="mdl-button mdl-js-button mdl-js-ripple-effect" onclick="window.parent.postMessage('closeDialog', '*');">
        CANCEL
    </button>
    <button id="submitBtn" class="mdl-button mdl-js-button mdl-button--raised mdl-js-ripple-effect mdl-button--accent" onclick="pushRollback();">
        Rollback
    </button>
</span>

<script>
    function pushRollback() {
        var data = $('#newMsgForm').serializeArray().reduce(function(obj, item) {
                obj[item.name] = item.value;
                return obj;
                }, {}); // form data
        
        let reason = data.rollbackReason;
        if (reason.length < 5) {
            return false;
            // Too short
        } else {
            // Submit it
            window.parent.postMessage('reason\\\`' + reason); // Push upstairs
            window.parent.postMessage("closeDialog"); // We done here.
            return false; // prevent redirect
        }
    }

    document.getElementById("rollbackReason").focus(); // focus on text box for quick rollback
</script>
            `, 500, 120)).showModal(); // 500x120 dialog, see rollbackReason.html for code
        });
    },

    "promptRestoreReason" : revID=> {
        // Prompt for reason to restore. very sim to rollback reason
        let reason = ""; // Needed for rollback reason page

        // Add submit handler
        addMessageHandler("reason`*", rs=>wikiEditor.rollback.restore(revID, rs.split("`")[1])); // When reason recieved, submit rollback

        // CREATE DIALOG
        // MDL FULLY SUPPORTED HERE (container). 
        dialogEngine.create(mdlContainers.generateContainer(`
        <!-- ROLLBACK REASON DIALOG -->
<form id="newMsgForm" onsubmit="pushRollback();" action="#">
    <div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label" style="width:100%">
        <input class="mdl-textfield__input" type="text" name="rollbackReason" id="rollbackReason" value="`+ reason +`" minlength="5">
        <label class="mdl-textfield__label" for="rollbackReason">Rollback Reason</label>
        <span class="mdl-textfield__error">You must enter a longer rollback reason.</span>
        <div class="mdl-tooltip" for="rollbackReason">
            Enter a reason for this rollback
        </div>
    </div>
</form>
<span style="float:right;">
    <button class="mdl-button mdl-js-button mdl-js-ripple-effect" onclick="window.parent.postMessage('closeDialog', '*');">
        CANCEL
    </button>
    <button id="submitBtn" class="mdl-button mdl-js-button mdl-button--raised mdl-js-ripple-effect mdl-button--accent" onclick="pushRollback();">
        Rollback
    </button>
</span>

<script>
    function pushRollback() {
        var data = $('#newMsgForm').serializeArray().reduce(function(obj, item) {
                obj[item.name] = item.value;
                return obj;
                }, {}); // form data
        
        let reason = data.rollbackReason;
        if (reason.length < 5) {
            return false;
            // Too short
        } else {
            // Submit it
            window.parent.postMessage('reason\\\`' + reason); // Push upstairs
            window.parent.postMessage("closeDialog"); // We done here.
            return false; // prevent redirect
        }
    }

    document.getElementById("rollbackReason").focus(); // focus on text box for quick rollback
</script>
        `, 500, 120)).showModal(); // 500x120 dialog, see rollbackReason.html for code
    },

    "welcomeRevUsr" :() => {
        // Send welcome to user who made most recent revision
        wikiEditor.visuals.toast.show("Please wait...", false, false, 1000);
        wikiEditor.info.isLatestRevision(mw.config.get("wgRelevantPageName"), mw.util.getParamValue("diff"), un=>{
            // We got the username, send the welcome
            wikiEditor.info.quickWelcome(un);
        });
    },


    "loadIcons" : () => {
        // Add icons to page
        // Icons for current revision
        let currentRevIcons = `
        <div id="rollBackVandal" class="icon material-icons"><span style="cursor: pointer; font-size:28px; padding-right:5px; color:red;" onclick="wikiEditor.rollback.apply('vandalism');">delete_forever</span></div>
        <div class="mdl-tooltip mdl-tooltip--large" for="rollBackVandal">
            Quick rollback vandalism
        </div>

        <div id="rollBackRM" class="icon material-icons"><span style="cursor: pointer; font-size:28px; padding-right:5px; color:orange;" onclick="wikiEditor.rollback.apply('rm content w no good reason or consensus');">format_indent_increase</span></div>
        <div class="mdl-tooltip mdl-tooltip--large" for="rollBackRM">
            Quick rollback removal of content with no good reason or consensus
        </div>

        <div id="rollBackNC" class="icon material-icons"><span style="cursor: pointer; font-size:28px; padding-right:5px; color:gold;" onclick="wikiEditor.rollback.apply('non-constructive edit');">work_outline</span></div>
        <div class="mdl-tooltip mdl-tooltip--large" for="rollBackNC">
            Quick rollback non-constructive edit
        </div>
        
        <div id="rollBack" class="icon material-icons"><span style="cursor: pointer; font-size:28px; padding-right:5px; color:blue;" onclick="wikiEditor.rollback.promptRollbackReason('');">replay</span></div>
        <div class="mdl-tooltip mdl-tooltip--large" for="rollBack">
            Rollback
        </div>
        
        <div id="rollBackAGF" class="icon material-icons"><span style="cursor: pointer; font-size:28px; padding-right:5px; color:green;" onclick="wikiEditor.rollback.promptRollbackReason('revert good faith edits ');">thumb_up</span></div>
        <div class="mdl-tooltip mdl-tooltip--large" for="rollBackAGF">
            Assume Good Faith and Rollback
        </div>
        
        <div id="rollBackPrev" class="icon material-icons"><span style="cursor: pointer; font-size:28px; padding-right:5px;" onclick="wikiEditor.rollback.preview();">compare_arrows</span></div>
        <div class="mdl-tooltip mdl-tooltip--large" for="rollBackPrev">
            Preview Rollback
        </div>

        <div id="wlRU" class="icon material-icons"><span style="cursor: pointer; font-size:28px; padding-right:5px;" onclick="wikiEditor.rollback.welcomeRevUsr();">sentiment_satisfied_alt</span></div>
        <div class="mdl-tooltip mdl-tooltip--large" for="wlRU">
            Quick Welcome User
        </div>
        `;

        // RESTORE THIS VERSION ICONS. DO NOT FORGET TO CHANGE BOTH FOR LEFT AND RIGHT

        let isLatest = $("#mw-diff-ntitle1").text().includes("Latest revision"); // is this the latest revision diff page?
        // On left side (always restore)
        // DO NOT FORGET TO CHANGE BOTH!!
        $('.diff-otitle').prepend(`
        <div id="rOld1" class="icon material-icons"><span style="cursor: pointer; font-size:28px; padding-right:5px; color:purple;"
            onclick="wikiEditor.rollback.promptRestoreReason($('#mw-diff-otitle1 > strong > a').attr('href').split('&')[1].split('=')[1]);"> <!-- the revID on left -->
                history
            </span>
        </div>
        <div class="mdl-tooltip mdl-tooltip--large" for="rOld1">
            Restore this version
        </div>
        `
        ); 

        // On the right side
        $('.diff-ntitle').prepend(isLatest ? currentRevIcons : `
        <div id="rOld2" class="icon material-icons"><span style="cursor: pointer; font-size:28px; padding-right:5px; color:purple;"
            onclick="wikiEditor.rollback.promptRestoreReason($('#mw-diff-ntitle1 > strong > a').attr('href').split('&')[1].split('=')[1]);"> <!-- the revID on right -->
                history
            </span>
        </div>
        <div class="mdl-tooltip mdl-tooltip--large" for="rOld2">
            Restore this version
        </div>
        `); // if the latest rev, show the accurate revs, else, don't 

        // Now register all tooltips
        for (let item of document.getElementsByClassName("mdl-tooltip")) {
            wikiEditor.visuals.register(item); 
        } 
        // That's done :)
    }
};

// Most UI elements
// See also dialog.js (dialogEngine) and mdlContainer.js (mdlContainer)
wikiEditor.ui = {

    "revisionBrowser" : url=> {
        // Show new container for revision reviewing
        dialogEngine.create(mdlContainers.generateContainer(`
        <div id="close" class="icon material-icons" style="float:right;">
            <span style="cursor: pointer; padding-right:15px;" onclick="window.parent.postMessage('closeDialog');">
                clear
            </span>
        </div>
        <div class="mdl-tooltip" for="close">
            Close
        </div>
        <iframe src="`+ url +`" frameborder="0" style="height:95%;"></iframe>
        `, document.body.offsetWidth-70, document.body.offsetHeight-50)).showModal();
    },

    "beginWarn" : (ignoreWarnings, un, pg)=> {
        // Give user a warning (show dialog)
        if ((wikiEditor.info.targetUsername(un) == wikiEditor.info.getUsername()) && !ignoreWarnings) {
            // Usernames are the same, give toast.
            wikiEditor.visuals.toast.show("You can not warn yourself. To test this tool, use a sandbox.", false, false, 7500);
            return; // DO NOT continue.
        }

        // Let's continue
        let finalListBox = "";
        rules.forEach((rule, i) => {
            let style = "";
            if (rule.name.length > 62) {
                // Too long to fit
                style="font-size:14px"
            }
            finalListBox += `<li class="mdl-menu__item" data-val="`+ i +`" onmousedown="refreshLevels(`+i+`);"style="`+style +`">`+ rule.name +`</li>`;
        });

        // Setup preview handling
        addMessageHandler("generatePreview`*", m=>{
            wikiEditor.info.parseWikitext(m.split("`")[1], parsed=>{ // Split to Wikitext and send over to the API to be handled
                dialogEngine.dialog.getElementsByTagName("iframe")[0].contentWindow.postMessage({
                    "action": "parseWikiTxt",
                    "result": parsed}, '*'); // push to container for handling in dialog and add https:// to stop image breaking
            });
        });

        // Add toast handler
        addMessageHandler("pushToast`*", m=>wikiEditor.visuals.toast.show(m.split('`')[1],false,false, 5000));

        // Add submit handler

        addMessageHandler("applyNotice`*", eD=> {
            // i.e applyNotice`user`wikitext`summary
            // TODO: maybe b64 encode?
            let _eD = eD.split("`"); // params
            let user = _eD[1];
            let wikiTxt = _eD[2];
            let summary = _eD[3];

            // MAKE EDIT
            wikiEditor.info.addWikiTextToUserPage(user, wikiTxt, true, summary);
        });

        // Check most recent warning level

        wikiEditor.info.lastWarningLevel(wikiEditor.info.targetUsername(un), (w, usrPgMonth, userPg)=>{
            let lastWarning = [ // Return HTML for last warning level. TODO: add preview section on click
                // NO PAST WARNING
                `
                <span class="material-icons" id="PastWarning" style="cursor:help;position: relative;top: 5px;padding-left: 10px;color:green;">thumb_up</span>
                <div class="mdl-tooltip mdl-tooltip--large" for="PastWarning">
                    <span style="font-size:x-small;">
                    No notices this month.
                    </span>
                </div>
                `,

                // NOTICE
                `
                <span class="material-icons" id="PastWarning" style="cursor:help;position: relative;top: 5px;padding-left: 10px;color:blue;">info</span>
                <div class="mdl-tooltip mdl-tooltip--large" for="PastWarning">
                    <span style="font-size:x-small;">
                    Has been given a Level 1 notice this month.
                    </span>
                </div>
                `,
                // CAUTION
                `
                <span class="material-icons" id="PastWarning" style="cursor:help;position: relative;top: 5px;padding-left: 10px;color:orange;">announcement</span>
                <div class="mdl-tooltip mdl-tooltip--large" for="PastWarning">
                    <span style="font-size:x-small;">
                    Has been given a Level 2 caution this month.
                    </span>
                </div>
                `,
                // Warning- in red. RedWarn, get it? This is the peak of programming humour.
                `
                <span class="material-icons" id="PastWarning" style="cursor:help;position: relative;top: 5px;padding-left: 10px; color:red;">report_problem</span>
                <div class="mdl-tooltip mdl-tooltip--large" for="PastWarning">
                    <span style="font-size:x-small;">
                    Has been given a Level 3 warning this month.
                    </span>
                </div>
                `,

                // Final/Only Warning (dark red) TODO: Click opens admin report pannel.
                `
                <span class="material-icons" id="PastWarning" style="cursor:help;position: relative;top: 5px;padding-left: 10px;color:#a20000;">report</span>
                <div class="mdl-tooltip mdl-tooltip--large" for="PastWarning">
                    <span style="font-size:x-small;">
                    Has been given a Level 4 Final or ONLY warning.<br/>
                    Click here to report to admins.
                    </span>
                </div>
                `
            ][w];

            // CREATE DIALOG
            // MDL FULLY SUPPORTED HERE (container). 
            dialogEngine.create(mdlContainers.generateContainer(`
                <!-- INCLUDED BY COMPILER -->

    <!-- Style needed to prevent huge headings -->
<style>
    #previewUsrPg h2{
        font-size:25px
    }

    #previewUsrPg .mw-editsection {
        display: none; /* Hide edit links */
    }

    #previewUsrPg .toc {
        /* Hide table of contents */
        display: none;
    }
</style>
<div id="noticeFmContainer">
    <h2 style="font-weight: 200;">New Notice</h2>



    <!-- FORM -->
    <form id="newNoticeForm">
    <div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label" style="width:75%;"> <!-- 75% for account standing-->
        <input class="mdl-textfield__input" type="text" id="trgtUsrVisualBox" value="`+ wikiEditor.info.targetUsername(un) +`" readonly>
        <label class="mdl-textfield__label" for="trgtUsrVisualBox">Target</label>
        <div class="mdl-tooltip" for="trgtUsrVisualBox">
            To target a different user, please visit their userpage.
        </div>
    </div>

    <!-- Last warning note -->
    `+ lastWarning +`

    <!-- Preview userpage notices this month btn -->
    <span class="material-icons" id="prevUsrPgMonth" style="cursor:pointer;position: relative;top: 5px;padding-left: 10px;" onclick="showThisMonthsUsrMsgs();">assignment_late</span>
    <div class="mdl-tooltip mdl-tooltip--large" for="prevUsrPgMonth">
        <span style="font-size:x-small;">
            See notices for this month
        </span>
    </div>

    <!-- Preview userpage btn -->
    <span class="material-icons" id="prevUsrPg" style="cursor:pointer;position: relative;top: 5px;padding-left: 10px;" onclick="showUsrPg();">assignment_ind</span>
    <div class="mdl-tooltip mdl-tooltip--large" for="prevUsrPg">
        <span style="font-size:x-small;">
            Preview Userpage
        </span>
    </div>

    <div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label getmdl-select" style="width:100%">
        <input type="text" value="" class="mdl-textfield__input" id="template" style="font-size:14px;" onkeypress="searchReasons();" autocomplete="off">
        <input type="hidden" value="" name="template">
        <i class="mdl-icon-toggle__label material-icons">keyboard_arrow_down</i>
        <label for="template" class="mdl-textfield__label">Reason</label>
        <ul for="template" class="mdl-menu mdl-menu--bottom-left mdl-js-menu" style="overflow-y: scroll;height: 300px">
            `+ finalListBox +`
        </ul>
    </div>

    <!-- Select which level of warning -->
    <span style="padding-right: 30;">Notice Level: </span>

    <span id="warningRadioButtons">
    <!-- LEVEL 1 -->
    <label id="l1Lbl" class="mdl-radio mdl-js-radio mdl-js-ripple-effect" for="level1" style="padding-right: 10;">
        <input type="radio" id="level1" class="mdl-radio__button" name="warnLevel" value="1" checked>
        <span class="mdl-radio__label">
        <span class="material-icons">info</span>
        </span>
    </label>
    <div class="mdl-tooltip mdl-tooltip--large" for="l1Lbl">
        Notice<br />
        <span style="font-size:x-small;">
        Level 1<br />
        Assumes Good Faith</span>
    </div>

    <!-- Level 2 -->
    <label id="l2Lbl" class="mdl-radio mdl-js-radio mdl-js-ripple-effect" for="level2" style="padding-right: 10;">
        <input type="radio" id="level2" class="mdl-radio__button" name="warnLevel" value="2">
        <span class="mdl-radio__label">
            <span class="material-icons">announcement</span>
        </span>
    </label>
    <div class="mdl-tooltip mdl-tooltip--large" for="l2Lbl">
        Caution<br />
        <span style="font-size:x-small;">
        Level 2<br />
        No Faith Assumption.</span>
    </div>

    <!-- Level 3 -->
    <label id="l3Lbl" class="mdl-radio mdl-js-radio mdl-js-ripple-effect" for="level3" style="padding-right: 10;">
        <input type="radio" id="level3" class="mdl-radio__button" name="warnLevel" value="3">
        <span class="mdl-radio__label"><span class="material-icons">report_problem</span></span>
    </label>
    <div class="mdl-tooltip mdl-tooltip--large" for="l3Lbl">
        Warning<br />
        <span style="font-size:x-small;">
        Level 3<br />
        Assumes bad faith, cease and desist.</span>
    </div>


    <!-- Level 4 -->
    <label id="l4Lbl" class="mdl-radio mdl-js-radio mdl-js-ripple-effect" for="level4" style="padding-right: 10;">
        <input type="radio" id="level4" class="mdl-radio__button" name="warnLevel" value="4">
        <span class="mdl-radio__label"><span class="material-icons">report</span></span>
    </label>
    <div class="mdl-tooltip mdl-tooltip--large" for="l4Lbl">
        Final Warning<br />
        <span style="font-size:x-small;">
        Level 4<br />
        Bad faith, last warning.</span>
    </div>


    <!-- Level 4im -->
    <label id="l4imLbl" class="mdl-radio mdl-js-radio mdl-js-ripple-effect" for="level4im" style="padding-right: 10;">
        <input type="radio" id="level4im" class="mdl-radio__button" name="warnLevel" value="4im">
        <span class="mdl-radio__label"><span class="material-icons">new_releases</span></span>
    </label>
    <div class="mdl-tooltip mdl-tooltip--large" for="l4imLbl">
        ONLY Warning<br />
        <span style="font-size:x-small;">
        Level 4im<br />
        Excessive and continuous disruption.</span>
    </div>

    <!-- NO Level 4 (and explaination)-->
    <label id="l4noLbl" class="mdl-radio mdl-js-radio mdl-js-ripple-effect" for="level4no" style="padding-right: 10; display:none;">
        <input type="radio" id="level4no" class="mdl-radio__button" name="warnLevel" value="" disabled>
        <span class="mdl-radio__label"><span class="material-icons" style="cursor: help;">report_off</span></span>
    </label>
    <div class="mdl-tooltip mdl-tooltip--large" for="l4noLbl">
        No final warning.<br />
        <span style="font-size:x-small;">
        A final warning cannot be issued under this reason. To issue a level 4 warning, choose the "Generic Warning" option.</span>
    </div>

    <!-- end of radio buttons -->
    </span>

    <!-- SINGLE NOTICE ONLY -->
    <span id="singleNoticeOnly" style="cursor: help;display: none;">

        <span id="sNoticeTt"><strong>Reminder</strong></span>
        <div class="mdl-tooltip mdl-tooltip--large" for="sNoticeTt">
            Reminder<br />
            <span style="font-size:x-small;">
            Single Notice<br />
            Serves to remind other editors about minor mistakes
            </span>
        </div>
    </span>
    <!-- END SINGLE NOTICE -->

    <!-- SINGLE WARNING ONLY -->
    <span id="singleWarnOnly" style="cursor:help;display: none;">

        <span id="sWarnTt"><strong>Policy Violation Warning</strong></span>
        <div class="mdl-tooltip mdl-tooltip--large" for="sWarnTt">
            Policy Violation Warning<br />
            <span style="font-size:x-small;">
            Single Warning<br />
            Serves to advise editors of policy breaches that, if repeated, are likely to result in a block.
            </span>
        </div>
    </span>
    <!-- END SINGLE WARNING -->

    <span id="ordInfo">
        <div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label" style="width:100%">
            <input class="mdl-textfield__input" name="relatedPage" type="text" id="relatedPage" value="`+ wikiEditor.info.getRelatedPage(pg).replace(/_/g, ' ') +`">
            <label class="mdl-textfield__label" for="relatedPage">Related Page</label>
            <div class="mdl-tooltip" for="relatedPage">
                Optionally, enter the page that this notice relates to.
            </div>
        </div>

        <div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label" style="width:100%">
            <input class="mdl-textfield__input" name="extraInfo" type="text" id="extraInfo" value="">
            <label class="mdl-textfield__label" for="extraInfo">Additional info</label>
            <div class="mdl-tooltip" for="extraInfo">
                Optionally, add additional info that will be appended to the end of the notice.
            </div>
        </div>
    </span>


    <span id="specialInfo" style="display: none;">
        <div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label" style="width:100%">
            <input class="mdl-textfield__input" name="specialInfo" type="text" id="specialInfo" value="">
            <label class="mdl-textfield__label" for="specialInfo">Special Information</label>
            <div class="mdl-tooltip mdl-tooltip--large" for="specialInfo" id="specialInfoTt">
                Placeholder :)
            </div>
        </div>
    </span>

    <!-- END FORM -->
    </form>


    <span id="previewContainer">
        <!-- EDIT BUTTON -->

        <span id="editBtn" class="material-icons" style="font-size: 16px;padding-bottom: 3px;float: right;padding-right: 5px;cursor: pointer;" onclick="$('#previewContainer').hide();$('#editorContainer').show();">
            create
        </span>

        <div id="preview" style="height: 150px; overflow-y: auto; width:100%;"> <!-- do not use max-height as this moves the buttons and makes it hard for muscle memory-->
            
        </div> <!-- Used to show preview-->
    </span>

    <span id="editorContainer" style="display:none;">
        <span id="previewBtn" class="material-icons" style="font-size: 16px;padding-bottom: 3px;float: right;padding-right: 5px;cursor: pointer;" onclick="$('#previewContainer').show();$('#editorContainer').hide();grabPreview(true);">
            visibility
        </span>
        <div id="editor">
            <textarea id="wikiTxt" style="height: 150px; max-height: 150px; overflow-y: auto; width:100%;"></textarea>
        </div> <!-- Used to edit Wikitext -->
    </span>

    <!-- SUBMIT BUTTONS --> <br/>
    <span style="float:right;">
        <button class="mdl-button mdl-js-button mdl-js-ripple-effect" onclick="window.parent.postMessage('closeDialog', '*');">
            CANCEL
        </button>
        <button id="submitBtn" class="mdl-button mdl-js-button mdl-button--raised mdl-js-ripple-effect mdl-button--accent" onclick="submitEdit();">
            SEND NOTICE
        </button>
    </span>
</div>

<!-- Userpage month preview -->
<div id="usrPgPrevMth" style="display: none;">
    <span class="material-icons" id="prevUsrPg" style="cursor:pointer;position: relative;top: 5px;padding-bottom: 10px;" onclick="returnTonoticeForm();">arrow_back</span>
    <div class="mdl-tooltip mdl-tooltip--large" for="prevUsrPg">
        <span style="font-size:x-small;">
            Back to Notice Editor
        </span>
    </div>

    <div id="previewUsrPgMth" style="height: 95%; overflow:auto;">
        <i>Generating preview...</i>
        <div class="mdl-progress mdl-js-progress mdl-progress__indeterminate"></div>
        <pre id="uMloadingPre" style="white-space: pre-wrap;">`+ usrPgMonth +`</pre>
    </div>
    
</div>

<!-- Userpage preview -->
<div id="usrPgPrev" style="display: none;">
    <span class="material-icons" id="prevUsrPg" style="cursor:pointer;position: relative;top: 5px;padding-bottom: 10px;" onclick="returnTonoticeForm();">arrow_back</span>
    <div class="mdl-tooltip mdl-tooltip--large" for="prevUsrPg">
        <span style="font-size:x-small;">
            Back to Notice Editor
        </span>
    </div>
    <div id="previewUsrPg" style="height: 95%; overflow:auto;">
        <i>Generating preview...</i>
        <div class="mdl-progress mdl-js-progress mdl-progress__indeterminate"></div>
        <pre id="uloadingPre" style="white-space: pre-wrap;">`+ userPg +`</pre>
    </div>
</div>

<script>
    function showThisMonthsUsrMsgs() {
        $("#noticeFmContainer").hide();
        $("#usrPgPrev").hide();
        $("#usrPgPrevMth").show();
        // Generate preview if needed (i.e. loading pre still there)
        if ($("#uMloadingPre").length > 0) {
            let wikiTxt = $("#uMloadingPre").text();
            window.parent.postMessage('generatePreview\\\`'+ wikiTxt, '*');
        }
    }

    function showUsrPg() {
        $("#noticeFmContainer").hide();
        $("#usrPgPrev").show();
        $("#usrPgPrevMth").hide();
        // Generate preview if needed (i.e. loading pre still there)
        if ($("#uloadingPre").length > 0) {
            let wikiTxt = $("#uloadingPre").text();
            window.parent.postMessage('generatePreview\\\`'+ wikiTxt, '*');
        }
    }

    function returnTonoticeForm() {
        $("#noticeFmContainer").show();
        $("#usrPgPrevMth").hide();
        $("#usrPgPrev").hide();
    }

        // Handle incoming data
    window.onmessage = function(e){
        if (e.data.action == 'parseWikiTxt') {
            if ($("#usrPgPrevMth:visible").length > 0) {
                // user page month preview
                $("#previewUsrPgMth").html(e.data.result); // set content
                return; // exit
            } else if ($("#usrPgPrev:visible").length > 0) {
                // User page preview
                $("#previewUsrPg").html(e.data.result); // set content
                return; // exit
            }

            // Normal preview
            $("#preview").html(e.data.result); // Set preview to content
        }
    };

    // Search reasons
    // Very basic but it works TODO: work on, including selecting best match on ENTER
    function searchReasons() {
        let toSearch = document.getElementById("template").value.toLowerCase();
        $(".mdl-menu__item").each((x,y)=>{
            if (y.innerText.toLowerCase().includes(toSearch)) {
                $(y).show();
            } else {
                $(y).hide();
            }
        });
    }



    var grabPreview = (fromCustomTxt)=> {
        // Generate preview
        // Wikitext grab
        if (fromCustomTxt) {
            // Edited using Wikitext editor
            var wikiTxt = document.getElementById("wikiTxt").value;
            if (!wikiTxt.includes("`+ wikiEditor.sign() +`")) {
                // Not signed, warn
                pushToast("Don't forget to sign your notice!");
            }
            window.parent.postMessage('generatePreview\\\`'+ wikiTxt, '*');
        } else {
            if ($('#editorContainer:visible').length > 0) {
                // Editor is open, we don't want to overwrite by accident.
                pushToast("Warning: This will overwrite your changes. Switch back to preview to confirm.");
                return;
            }
            // Get preview as usual
            getTemplateName(name=>{
            var wikiTxt = "{{subst:"+ name +"}} " + "`+ wikiEditor.sign() +`";
            document.getElementById("wikiTxt").value = wikiTxt; // set edit box
            window.parent.postMessage('generatePreview\\\`'+ wikiTxt, '*');
            }); 
        } 
    }

    var rules = `+ JSON.stringify(rules) +`; // get rules from host
    var refreshLevels = i=> {
       if (rules[i].warningLevels.includes(0)) {
           // Single notice
           $("#warningRadioButtons").hide();
           $("#singleNoticeOnly").show();
           $("#singleWarnOnly").hide();
       } else if (rules[i].warningLevels.includes(6)) {
           // Single warning
           $("#warningRadioButtons").hide();
           $("#singleNoticeOnly").hide();
           $("#singleWarnOnly").show();
       } else {
           // Normal warning
           $("#warningRadioButtons").show();
           $("#singleNoticeOnly").hide();
           $("#singleWarnOnly").hide();

           if (!rules[i].warningLevels.includes(1)) {
            // No l1 warning
            $("#l1Lbl").hide();
           } else {
            $("#l1Lbl").show();
           }

           if (!rules[i].warningLevels.includes(2)) {
            // No l2 warning
            $("#l2Lbl").hide();
           } else {
            $("#l2Lbl").show();
           }
           if (!rules[i].warningLevels.includes(3)) {
            // No l3 warning
            $("#l3Lbl").hide();
           } else {
            $("#l3Lbl").show();
           }

           // LEVEL 4
           if (!rules[i].warningLevels.includes(4)) {
            // No final warning
            $("#l4Lbl").hide();
            $("#l4noLbl").show();
           } else {
            $("#l4Lbl").show();
            $("#l4noLbl").hide();
           }
           if (!rules[i].warningLevels.includes(5)) {
            // No ONLY warning
            $("#l4imLbl").hide();
           } else {
            $("#l4imLbl").show();
           }
       }

       if (rules[i].note != null) {
           // A disclaimer toast needs to be shown
           pushToast(rules[i].note);
       }
       // TODO!!
       let noneStandard = {
                'uw-agf-sock': 'Optional username of other account (without User:) ',
                'uw-bite': "Username of 'bitten' user (without User:) ",
                'uw-socksuspect': 'Username of sock master, if known (without User:) ',
                'uw-username': 'Username violates policy because... ',
                'uw-aiv': 'Optional username that was reported (without User:) '
        }; // These all take a different thing and only have 1 input

        if (noneStandard[rules[i].template] != null) {
            // Requires special input
            $("#specialInfo").show();
            $("#ordInfo").hide();
            $("#specialInfoTt").html(noneStandard[rules[i].template]); // Set tool tip text
        } else {
            // Doesn't require
            $("#specialInfo").hide();
            $("#ordInfo").show();
        }
    }

    function pushToast(text) {window.parent.postMessage('pushToast\\\`' + text);} // Push toast to host

    var getTemplateName = (callback)=> { // CALLBACK IS ONLY CALLED IF SUCCESSFUL
        let currentTemplate = "";
        let currentLevel = "";
        var data = $('#newNoticeForm').serializeArray().reduce(function(obj, item) {
            obj[item.name] = item.value;
            return obj;
            }, {}); // form data

        // LEVEL
        if ($("input:radio:visible:checked").length == 0) { // If no visble radio buttons checked
            if ($("#singleNoticeOnly:visible").length > 0 || $("#singleWarnOnly:visible").length > 0) {
                // No warning level needed, leaving for readability
            } else {
                // No radio button pressed, not a single warning/notice
                pushToast("Please select a notice level");
                return;
            }
        } else {
            // Radio button
            currentLevel = data.warnLevel; // Set to selected radiobutton value
        }

        // TEMPLATE
        if (data.template == "") {
            // No reason selected
            pushToast("Please select a reason");
            return;
        }

        currentTemplate = rules[data.template].template; // assemble
        if ($("#specialInfo:visible").length > 0) {
            callback(currentTemplate + currentLevel + "|" + data.specialInfo); // callback w data for special
        } else {
            callback(currentTemplate + currentLevel + "|" + data.relatedPage + "|" + "'' "+ data.extraInfo +"''"); // callback, extra info italics
        }
        
        
    };

    function submitEdit() {
        // Add notice to page
        getTemplateName(r=>{ // acts as validation
            // SEND IT
            var data = $('#newNoticeForm').serializeArray().reduce(function(obj, item) {
                obj[item.name] = item.value;
                return obj;
                }, {}); // form data

            var wikiTxt = document.getElementById("wikiTxt").value;
            if (!wikiTxt.includes("`+ wikiEditor.sign() +`")) {
                // Not signed, warn
                pushToast("Sign your notice!");
                return; // Do not continue
            }
            window.parent.postMessage('applyNotice\\\`' + document.getElementById("trgtUsrVisualBox").value + '\\\`' + wikiTxt + '\\\`' + "New Notice RE: "+ rules[data.template].name); // Push upstairs and commit
            window.parent.postMessage("closeDialog"); // We done here. Top will refresh or reshow if error occurs.
        });
    }


    // Check for change in form data
    // then 500ms since last change, update preview
    var oldData = "";
    var updatePreviewTOut;
    setInterval(()=>{
        var data = $('#newNoticeForm').serializeArray().reduce(function(obj, item) {
            obj[item.name] = item.value;
            return obj;
            }, {}); // form data
        if (JSON.stringify(data) === oldData) {
            // No change
        } else {
            oldData = JSON.stringify(data); // set
            $("#submitBtn").hide(); // hide until template refresh
            // Change. Set timeout for 500ms
            try {
                clearTimeout(updatePreviewTOut); // clear timeout so it resets after last change
            } catch (e) {}
            updatePreviewTOut = setTimeout(()=>{
                grabPreview();
                $("#submitBtn").show();
            }, 500); // show previews after 500ms since last change
        }
       
    }, 100);
</script>
            `, 500, 630)).showModal(); // 500x630 dialog, see warnUserDialog.html for code
        });
            
    }, // end beginWarn

    "newMsg" : un=>{
        // New message dialog
        // Setup preview handling
        addMessageHandler("generatePreview`*", m=>{
            wikiEditor.info.parseWikitext(m.split("`")[1], parsed=>{ // Split to Wikitext and send over to the API to be handled
                dialogEngine.dialog.getElementsByTagName("iframe")[0].contentWindow.postMessage({
                    "action": "parseWikiTxt",
                    "result": parsed}, '*'); // push to container for handling in dialog and add https:// to stop image breaking
            });
        });

        // Add toast handler
        addMessageHandler("pushToast`*", m=>wikiEditor.visuals.toast.show(m.split('`')[1],false,false,15000));

        // Add submit handler

        addMessageHandler("applyNotice`*", eD=> {
            // i.e applyNotice`user`wikitext`summary
            // TODO: maybe b64 encode?
            let _eD = eD.split("`"); // params
            let user = _eD[1];
            let wikiTxt = _eD[2];
            let summary = _eD[3];

            // MAKE EDIT
            wikiEditor.info.addWikiTextToUserPage(user, wikiTxt, false, summary); // This requires title.
        });

        // CREATE DIALOG
        // MDL FULLY SUPPORTED HERE (container). 
        dialogEngine.create(mdlContainers.generateContainer(`
        <!-- Style for title preview -->
<style>
    h2 {
        font-size: 20px;
        line-height: 0px;
    }
    .mw-editsection {
        display: none;
    }
</style>
<h2 style="font-weight: 200;font-size:45px;line-height: 48px;">Compose New Message</h2>
<form id="newMsgForm">
    <div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label" style="width:100%">
        <input class="mdl-textfield__input" type="text" id="trgtUsrVisualBox" value="`+ wikiEditor.info.targetUsername(un) +`" readonly>
        <label class="mdl-textfield__label" for="trgtUsrVisualBox">Target</label>
        <div class="mdl-tooltip" for="trgtUsrVisualBox">
            To target a different user, please visit their userpage.
        </div>
    </div>
<span id="previewContainer" style="display:none;">
    <!-- EDIT BUTTON -->

    <span id="editBtn" class="material-icons" style="font-size: 16px;padding-bottom: 3px;float: right;padding-right: 5px;cursor: pointer;" onclick="$('#previewContainer').hide();$('#editorContainer').show();">
        create
    </span>

    <div id="preview" style="height: 150px; overflow-y: auto; width:100%;"> <!-- do not use max-height as this moves the buttons and makes it hard for muscle memory-->
        
    </div> <!-- Used to show preview-->
</span>

<span id="editorContainer">
    <span id="previewBtn" class="material-icons" style="font-size: 16px;padding-bottom: 3px;float: right;padding-right: 5px;cursor: pointer;" onclick="$('#previewContainer').show();$('#editorContainer').hide();grabPreview();">
        visibility
    </span>
    <div id="editor">
        <textarea id="wikiTxt" name="wikiTxt" style="height: 150px; max-height: 150px; overflow-y: auto; width:100%;">
== Your Message Title ==
Your message here. `+ wikiEditor.sign() +`
        </textarea>
    </div> <!-- Used to edit Wikitext -->
</span>
</form>

<span style="float:right;">
    <button class="mdl-button mdl-js-button mdl-js-ripple-effect" onclick="window.parent.postMessage('closeDialog', '*');">
        CANCEL
    </button>
    <button id="submitBtn" class="mdl-button mdl-js-button mdl-button--raised mdl-js-ripple-effect mdl-button--accent" onclick="sendMessage();">
        SEND MESSAGE
    </button>
</span>

<script>
        // Handle incoming data
    window.onmessage = function(e){
        if (e.data.action == 'parseWikiTxt') {
            $("#preview").html(e.data.result); // Set preview to content
        }
    };

    function pushToast(text) {window.parent.postMessage('pushToast\\\`' + text);} // Push toast to host

    function grabPreview() {
        var wikiTxt = document.getElementById("wikiTxt").value;
        if (!wikiTxt.includes("`+ wikiEditor.sign() +`")) {
            // Not signed, warn
            pushToast("Don't forget to sign your message!");
        }
        window.parent.postMessage('generatePreview\\\`'+ wikiTxt, '*');
    }

    function sendMessage() {
        // Send it!
        var data = $('#newMsgForm').serializeArray().reduce(function(obj, item) {
                obj[item.name] = item.value;
                return obj;
                }, {}); // form data

        var wikiTxt = data.wikiTxt;
        if (!wikiTxt.includes("`+ wikiEditor.sign() +`")) {
            // Not signed, warn
            pushToast("Sign your message with '`+ wikiEditor.sign() +`'");
            return; // Do not continue
        }
        window.parent.postMessage('applyNotice\\\`' + document.getElementById("trgtUsrVisualBox").value + '\\\`' + wikiTxt + '\\\`' + "New message"); // Push upstairs and commit
        window.parent.postMessage("closeDialog"); // We done here. Top will refresh or reshow if error occurs.
    }
</script>
        `, 500, 390)).showModal(); // 500x390 dialog, see newMsg.html for code
    },

    "registerContextMenu" : () => { // Register context menus for right-click actions
        // More docs at https://swisnl.github.io/jQuery-contextMenu/demo/trigger-custom.html

        // USER TALK ACTIONS
        $(()=>{
            $.contextMenu({
                selector: 'a[href*="/wiki/User_talk:"], a[href*="/wiki/User:"], a[href*="/wiki/Special:Contributions/"]', // Select all appropriate user links
                callback: (act, info)=>{
                    // CALLBACK
                    let hrefOfSelection = $(info.$trigger[0]).attr("href"); // href of userpage or contribs
                    let targetUsername = "";
                    if (hrefOfSelection.includes("/wiki/User_talk:") || hrefOfSelection.includes("/wiki/User:")) {
                        // This is easy because w should just be ablt to spit at last :
                        targetUsername = (a=>{return a[a.length - 1]})(hrefOfSelection.split(":")).split("/")[0]; // wacky function returns last. Split at slash [0] in case it is a link to a user subpage.
                    } else {
                        // Contribs link, go split at last slash
                        targetUsername = (a=>{return a[a.length - 1]})(hrefOfSelection.split("/"));
                    }
                    
                    // Do the action for each action now.
                    ({
                        "usrPg" : un=>redirect("https://en.wikipedia.org/wiki/User:"+ un, true),  // Open user page in new tab

                        "tlkPg" : un=>redirect("https://en.wikipedia.org/wiki/User_talk:"+ un, true),  // Open talk page in new tab

                        "contribs" : un=>redirect("https://en.wikipedia.org/wiki/Special:Contributions/"+ un, true),  // Redirect to contribs page in new tab

                        "accInfo" : un=>redirect("https://en.wikipedia.org/wiki/Special:CentralAuth?target="+ un, true),  // Redirect to Special:CentralAuth page in new tab

                        "sendMsg" : un=>wikiEditor.ui.newMsg(un), // show new msg dialog

                        "quickWel" : un=>wikiEditor.info.quickWelcome(un), // Submit quick welcome

                        "newNotice" : un=>wikiEditor.ui.beginWarn(false, un) // show new warning dialog
                    })[act](targetUsername);
                    
                },
                items: {
                    "usrPg": {name: "User Page"},
                    "tlkPg": {name: "Talk Page"},
                    "sendMsg": {name: "Send message"},
                    "newNotice": {name: "New Notice"},
                    "quickWel": {name: "Quick Welcome"},
                    "contribs": {name: "Contributions"},
                    "accInfo": {name: "Account Info"},
                    "adminReport": {name: "Report to Admin"}
                }
            });
        }); // END USER ACTIONS CONTEXT MENU


        // VOID NOTICE CONTEXT MENU (extendedconfirmed ONLY)
        wikiEditor.info.featureRestrictPermissionLevel("extendedconfirmed", ()=>$(()=>{
            $.contextMenu({
                selector: 'p:has(a[href*="wiki/File:Stop_hand_nuvola.svg"]), p:has(a[href*="wiki/File:Information_orange.svg"]), p:has(a[href*="wiki/File:Information.svg"]), p:has(a[href*="wiki/File:Nuvola_apps_important.svg"])', // Select all appropriate paragraphs containing a notice
                callback: (act, info)=>{
                    // CALLBACK
                    let textToMatch = $(info.$trigger[0]).text(); // Text to match w api
                    if (!$(info.$trigger[0]).html().includes("/wiki/User_talk:")) { // No sig. Likely multi-line
                        // We can't void this as it is multi-line. Maybe something to add in future?
                        wikiEditor.visuals.toast.show("This type of notice can't be automatically voided. You will need to remove this notice manually.", false, false, 7500);
                        return; //exit
                    }
                    
                    console.log(textToMatch);
                    // Now we make the request and read line by line
                    $.getJSON("https://en.wikipedia.org/w/api.php?action=query&prop=revisions&titles="+mw.config.get("wgRelevantPageName")+"&rvslots=*&rvprop=content&formatversion=2&format=json", latestR=>{
                        // Grab text from latest revision of talk page
                        // Check if exists
                        
                        if (latestR.query.pages[0].missing) { // If page doesn't exist, error because something defo went wrong
                            wikiEditor.visuals.toast.show("The notice could not be automatically removed due to an error.", false, false, 5000);
                            return; // exit
                        }

                        wikiEditor.visuals.toast.show("Please wait...", false, false, 2000);
                        let revisionWikitext = latestR.query.pages[0].revisions[0].slots.main.content;
                        let wikiTxtLines = revisionWikitext.split("\n");
                        // let's continue
                        
                        let hasBeenMatched = false;
                        let finalStr = "";
                        wikiTxtLines.forEach((element, i) => {
                            let compStr = wikiEditor.info.stripWikiTxt(element);
                            if (compStr.toLowerCase().includes(textToMatch.toLowerCase().trim())) {
                                // Match! Don't add this one normally. Add our sig and that it is now void. MUST REMOVE PIC (as the textTomatch did) in order to stop Redwarn showing as warning still
                                hasBeenMatched = true;
                                finalStr += "{{strikethrough|" + textToMatch + "}}<br>'''The above notice was placed in error and is now void.''' " + wikiEditor.sign() + " \n";
                            } else {
                                finalStr += element + "\n";
                            }
                        });
                        if (!hasBeenMatched) {
                            // For whatever reason, we cannot remove this, no match.
                            wikiEditor.visuals.toast.show("This type of notice can't be automatically voided. You will need to remove this notice manually.", false, false, 7500);
                            return; //exit
                        }

                        // Let's continue and apply edit
                        $.post("https://en.wikipedia.org/w/api.php", {
                            "action": "edit",
                            "format": "json",
                            "token" : mw.user.tokens.get("csrfToken"),
                            "title" : mw.config.get("wgRelevantPageName"),
                            "summary" : "Void notice made in error [[WP:REDWARN|(RedWarn)]]", // summary sign here
                            "text": finalStr
                        }).done(dt => {
                            // We done. Check for errors, then callback appropriately
                            if (!dt.edit) {
                                // Error occured or other issue
                                console.error(dt);
                                wikiEditor.visuals.toast.show("Sorry, there was an error. This notice has not been voided.");
                            } else {
                                // Success! Redirect to complete page
                                window.location.hash = "#noticeApplied-" + dt.edit.newrevid + "-" + dt.edit.oldrevid; 
                                location.reload();
                                // We done
                            }
                        });
                        // END CALLBACK
                    });
                    
                },
                items: {
                    "rm": {name: "Void this notice"}
                }
            });
        }), ()=>{}); // END REMOVE NOTICE CONTEXT MENU

        // NON-CONTRUCTIVE QUICKROLLBACK BUTTON CONTEXT MENU
        $(()=>{
            $.contextMenu({
                selector: '#rollBackNC', // Select non-constructive edit button
                callback: (act, info)=>{
                    // CALLBACK
                    // Do the action for each action now.

                    ({
                        "rbTestEdits" : ()=>wikiEditor.rollback.apply('test edit.')  // Submit quick rollback
                    })[act]();
                    
                },
                items: {
                    "rbTestEdits": {name: "Quick Rollback Test Edit"}
                }
            });
        }); // NON-CONTRUCTIVE QUICKROLLBACK BUTTON CONTEXT MENU

        // TODO: add more, like quick welcome options ext.. and right-click on article link to begin rollback ext.


    }, // end context menus


    "requestSpeedyDelete" : (pg)=>{
        // Open Speedy Deletion dialog for first selection, i.e I'm requesting the speedy deletion of..
        // Programming this is proving to be very boring.
        addMessageHandler("csdR`*", rs=>{
            // Reason recieved.
            let reason = eval(rs.split("`")[1]);
            let reasonTitle = reason.title;
            let additionalInfoReq = reason.input != ""; // if special info needed
            let additionalInfo = "";
            if (additionalInfoReq) {
                if (rs.split("`")[2] == "undefined") {
                    // No reason specified
                    additionalInfo = "Not specified.";
                } else {
                    additionalInfo = rs.split("`")[2]; // set to the additional info
                }
            }
            console.log(`Deleting under: `+ reasonTitle +`
            `+ reason.input + additionalInfo + ` (redwarn)
            `);
        }); 

        let finalStr = ``;
        for (const key in speedyDeleteReasons) {
            speedyDeleteReasons[key].forEach((e,i)=>{
                let style = "";
                if ((key + e.title).length > 62) {
                    // Too long to fit
                    style="font-size:10px;";
                }
                finalStr += `<li class="mdl-menu__item" data-val='speedyDeleteReasons["`+ key + `"][`+ i +`]' onmousedown="refreshLevels('speedyDeleteReasons[\\\'`+ key + `\\\'][`+ i +`]');" style="`+ style +`">`+ key + e.title +`</li>`;;
            });
        }
        // CREATE DIALOG
        // MDL FULLY SUPPORTED HERE (container). 
        dialogEngine.create(mdlContainers.generateContainer(`
        <!-- SPEEDY DELETION DIALOG -->

<h2 style="font-weight: 200;font-size:45px;line-height: 48px;">Request Speedy Deletion</h2>
<div class="cntContainer" style="height:350px; overflow:auto;">
    <form id="newMsgForm" onsubmit="pushRollback();" action="#">
        <!-- GENERAL REASONS ONLY UNDER ANY PAGE -->
        <div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label getmdl-select" style="width:100%">
            <input type="text" value="" class="mdl-textfield__input" id="reason" style="font-size:14px;" onkeypress="searchReasons();" autocomplete="off">
            <input type="hidden" value="" name="reason">
            <i class="mdl-icon-toggle__label material-icons">keyboard_arrow_down</i>
            <label for="template" class="mdl-textfield__label">I'm requesting the speedy deletion of...</label>
            <ul for="template" class="mdl-menu mdl-menu--bottom-left mdl-js-menu" style="overflow-y: scroll;height: 250px">
                `+ finalStr +`
            </ul>
        </div>
        <p id="desP">Please select a valid reason.</p>
        <div id="textBcontainer" style="display:none;">
            <div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label" style="width:100%">
                <input class="mdl-textfield__input" type="text" id="customInput" name="customInput">
                <label class="mdl-textfield__label" for="customInput" id="customInputLabel">Custom Reason</label>
                <div class="mdl-tooltip" for="customInput" id="customInputTt">
                    Enter this info correctly
                </div>
            </div>
        </div>
    </form>
    <span style="
        color: red;
        font-size: small;
    ">
    Before nominating a page for speedy deletion, consider whether it could be improved, reduced to a stub, merged or redirected elsewhere,
        reverted to a better previous revision, or handled in <a href="/wiki/Wikipedia:Deletion_policy#Alternatives_to_deletion" target="_blank">some other way</a>. A page
        is eligible for speedy deletion only if all of its revisions are also eligible.<br /><br />
    <b>Are you sure a speedy deletion request is the best option and the data you have entered is correct?</b>
        </span><br />
    <span style="float:right;">
        <button class="mdl-button mdl-js-button mdl-js-ripple-effect" onclick="window.parent.postMessage('closeDialog', '*');">
            CANCEL
        </button>
        <button id="submitBtn" class="mdl-button mdl-js-button mdl-button--raised mdl-js-ripple-effect mdl-button--accent" onclick="pushRollback();">
            YES, PROPOSE SPEEDY DELETION
        </button>
    </span>
</div>

<script>
    function pushRollback() {
        var data = $('#newMsgForm').serializeArray().reduce(function(obj, item) {
                obj[item.name] = item.value;
                return obj;
                }, {}); // form data
        // Submit it
        console.log(data);
        window.parent.postMessage("csdR\\\`"+data.reason + '\\\`' + data.customInput); // Push upstairs
        window.parent.postMessage("closeDialog"); // We done here.
        
    }
    // Search reasons
    // Very basic but it works TODO: work on, including selecting best match on ENTER
    function searchReasons() {
        let toSearch = document.getElementById("reason").value.toLowerCase();
        $(".mdl-menu__item").each((x,y)=>{
            if (y.innerText.toLowerCase().includes(toSearch)) {
                $(y).show();
            } else {
                $(y).hide();
            }
        });
    }

    var refreshLevels = i=>{
        // Show the correct description and textbox
        let speedyDeleteReasons = `+ JSON.stringify(speedyDeleteReasons) +`;
        let reason = eval(i);
        console.log(reason);
        // Info box
        $("#desP").html('<span class="material-icons" style="float: left;padding-right: 5px; cursor:default;">info</span>' + reason.helpText);
        

        if (reason.input != "") {
            // Requires a custom input, show
            $("#customInputLabel").text(reason.input);
            $("#textBcontainer").show(); // Make the textbox visible
            try {
                if (reason.inputTooltip.length > 0) {
                // Tool tip too
                $("#customInputTt").show();
                $("#customInputTt").text(reason.inputTooltip);
                } else {
                    // No tooltip
                    $("#customInputTt").hide();
                }
            } catch (error) {
                // No tooltip
                $("#customInputTt").hide();
            }
        } else {
            $("#textBcontainer").hide();
        }
       
    }
</script>
        `, 500, 450)).showModal(); // 500x300 dialog, see speedyDeletionp1.html for code
    },

    "openPreferences" : () => { // Open Preferences page
        addMessageHandler("config`*", rs=>{
            // New config recieved
            let config = JSON.parse(atob(rs.split("`")[1])); // b64 encoded json string
            //Write to our config
            for (const key in config) {
                if (config.hasOwnProperty(key)) {
                    const element = config[key];
                    wikiEditor.config[key] = element; // add or change value
                }
            }

            // Push change
            wikiEditor.info.writeConfig();
        }); 
        // Open preferences page with no padding, full screen
        dialogEngine.create(mdlContainers.generateContainer(`
        <!-- PREFERENCES PAGE -->
<div class="mdl-layout mdl-js-layout mdl-layout--fixed-header">
  <header class="mdl-layout__header">
    <div class="mdl-layout__header-row">
      <!-- Title -->
      <span class="mdl-layout-title" style="width: calc(100% - 60px);">RedWarn Preferences</span>
      <div id="apply" class="icon material-icons" style="float:right;">
        <span style="cursor: pointer; padding-right:15px;" onclick="saveConfig();">
            save
        </span>
        </div>
        <div class="mdl-tooltip" for="apply">
            Apply Changes
        </div>

        <div id="close" class="icon material-icons" style="float:right;">
        <span style="cursor: pointer; padding-right:15px;" onclick="window.parent.postMessage('closeDialog');">
            clear
        </span>
        </div>
        <div class="mdl-tooltip" for="close">
            Don't apply changes and Close
        </div>
    </div>
    <!-- Tabs -->
    <div class="mdl-layout__tab-bar mdl-js-ripple-effect">
      <a href="#scroll-tab-1" class="mdl-layout__tab is-active">Appearance</a>
      <a href="#scroll-tab-2" class="mdl-layout__tab">Behavior</a>
      <!--
      <a href="#scroll-tab-3" class="mdl-layout__tab">Tab 3</a>
      <a href="#scroll-tab-4" class="mdl-layout__tab">Tab 4</a>
      <a href="#scroll-tab-5" class="mdl-layout__tab">Tab 5</a>
      <a href="#scroll-tab-6" class="mdl-layout__tab">Tab 6</a>-->
    </div>
  </header>
  <main class="mdl-layout__content" style="padding-left: 5%;">
    <form id="config">
        <section class="mdl-layout__tab-panel is-active" id="scroll-tab-1">
        <div class="page-content">
            <!-- APPEARANCE TAB -->
            <h4>Appearance</h4>
            <span style="font-size: 18px;padding-bottom: 20px;">UI Colour Scheme:</span><br>
            <label class="mdl-radio mdl-js-radio mdl-js-ripple-effect" for="option-1">
                <input type="radio" id="option-1" class="mdl-radio__button" name="colTheme" value="blue-indigo" checked>
                <span class="mdl-radio__label">WikiBlue (default)</span>
            </label>
            &nbsp;
            <label class="mdl-radio mdl-js-radio mdl-js-ripple-effect" for="option-2">
                <input type="radio" id="option-2" class="mdl-radio__button" name="colTheme" value="amber-yellow">
                <span class="mdl-radio__label">Sunshine</span>
            </label>
            &nbsp;
            <label class="mdl-radio mdl-js-radio mdl-js-ripple-effect" for="option-3">
                <input type="radio" id="option-3" class="mdl-radio__button" name="colTheme" value="purple-deep_purple">
                <span class="mdl-radio__label">Purple Power</span>
            </label>
            &nbsp;
            <label class="mdl-radio mdl-js-radio mdl-js-ripple-effect" for="option-4">
                <input type="radio" id="option-4" class="mdl-radio__button" name="colTheme" value="blue_grey-red">
                <span class="mdl-radio__label">Red and Dull</span>
            </label>
            &nbsp;
            <i>Previews coming soon.</i>

            <hr>
            <h4>Patrol Appearance</h4>

            <!-- SIDEBAR SIZE -->
            <span style="font-size: 18px;padding-bottom: 20px;cursor:help;" id="ptrsidebarSize">Sidebar size:</span><br>
            <div class="mdl-tooltip mdl-tooltip--large" for="ptrsidebarSize">
                The size of the sidebar where recent changes are shown.<br>
                Use Small is your screen is low resolution, otherwise, changing this option is not recommended.
            </div>
            <label class="mdl-radio mdl-js-radio mdl-js-ripple-effect" for="ptrSidebarSmall">
                <input type="radio" id="ptrSidebarSmall" class="mdl-radio__button" name="ptrSidebar" value="250">
                <span class="mdl-radio__label">Small</span>
            </label>
            &nbsp;
            <label class="mdl-radio mdl-js-radio mdl-js-ripple-effect" for="ptrSidebarMed">
                <input type="radio" id="ptrSidebarMed" class="mdl-radio__button" name="ptrSidebar" value="500" checked>
                <span class="mdl-radio__label">Medium (default)</span>
            </label>
            &nbsp;
            <label class="mdl-radio mdl-js-radio mdl-js-ripple-effect" for="ptrSidebarLg">
                <input type="radio" id="ptrSidebarLg" class="mdl-radio__button" name="ptrSidebar" value="750">
                <span class="mdl-radio__label">Large</span>
            </label><br><br>

            <!-- ADDITION COL -->
            <span style="font-size: 18px;padding-bottom: 20px;cursor:help;" id="ptraddCol">Colour of additions:</span><br>
            <div class="mdl-tooltip mdl-tooltip--large" for="ptraddCol">
                The background colour for changes that increase the size of a page.
            </div>
            <label class="mdl-radio mdl-js-radio mdl-js-ripple-effect" for="ptraddcolGreen">
                <input type="radio" id="ptraddcolGreen" class="mdl-radio__button" name="ptrAddCol" value="0,255,0" checked>
                <span class="mdl-radio__label">Green (default)</span>
            </label>
            &nbsp;
            <label class="mdl-radio mdl-js-radio mdl-js-ripple-effect" for="ptraddcolRed">
                <input type="radio" id="ptraddcolRed" class="mdl-radio__button" name="ptrAddCol" value="255,0,0">
                <span class="mdl-radio__label">Red</span>
            </label>
            &nbsp;
            <label class="mdl-radio mdl-js-radio mdl-js-ripple-effect" for="ptraddcolBlue">
                <input type="radio" id="ptraddcolBlue" class="mdl-radio__button" name="ptrAddCol" value="0,195,255">
                <span class="mdl-radio__label">Blue</span>
            </label>
            &nbsp;
            <label class="mdl-radio mdl-js-radio mdl-js-ripple-effect" for="ptraddcolYellow">
                <input type="radio" id="ptraddcolYellow" class="mdl-radio__button" name="ptrAddCol" value="255,255,0">
                <span class="mdl-radio__label">Yellow</span>
            </label> <br><br>

            <!-- RM COL -->
            <span style="font-size: 18px;padding-bottom: 20px;cursor:help;" id="ptrrmCol">Colour of removals:</span><br>
            <div class="mdl-tooltip mdl-tooltip--large" for="ptrrmCol">
                The background colour for changes that decrease the size of a page.
            </div>
            <label class="mdl-radio mdl-js-radio mdl-js-ripple-effect" for="ptrrmcolGreen">
                <input type="radio" id="ptrrmcolGreen" class="mdl-radio__button" name="ptrRmCol" value="0,255,0">
                <span class="mdl-radio__label">Green</span>
            </label>
            &nbsp;
            <label class="mdl-radio mdl-js-radio mdl-js-ripple-effect" for="ptrrmcolRed">
                <input type="radio" id="ptrrmcolRed" class="mdl-radio__button" name="ptrRmCol" value="255,0,0" checked>
                <span class="mdl-radio__label">Red (default)</span>
            </label>
            &nbsp;
            <label class="mdl-radio mdl-js-radio mdl-js-ripple-effect" for="ptrrmcolBlue">
                <input type="radio" id="ptrrmcolBlue" class="mdl-radio__button" name="ptrRmCol" value="0,195,255">
                <span class="mdl-radio__label">Blue</span>
            </label>
            &nbsp;
            <label class="mdl-radio mdl-js-radio mdl-js-ripple-effect" for="ptrrmcolYellow">
                <input type="radio" id="ptrrmcolYellow" class="mdl-radio__button" name="ptrRmCol" value="255,255,0">
                <span class="mdl-radio__label">Yellow</span>
            </label>
            
            <!-- END APPEARANCE TAB-->
        </div>
        </section>
        <section class="mdl-layout__tab-panel" id="scroll-tab-2">
        <div class="page-content">
            <!-- BEHAVIOR SETTINGS -->
            <h5>Behavior settings coming soon! Please leave suggestions for options you'd like to see in RedWarn's talk page.</h5>
            <!-- END BEHAVIOR SETTINGS -->
        </div>
        </section>
        <section class="mdl-layout__tab-panel" id="scroll-tab-3">
        <div class="page-content"><!-- Your content goes here --></div>
        </section>
        <section class="mdl-layout__tab-panel" id="scroll-tab-4">
        <div class="page-content"><!-- Your content goes here --></div>
        </section>
        <section class="mdl-layout__tab-panel" id="scroll-tab-5">
        <div class="page-content"><!-- Your content goes here --></div>
        </section>
        <section class="mdl-layout__tab-panel" id="scroll-tab-6">
        <div class="page-content"><!-- Your content goes here --></div>
        </section>
    </form>
    <i>To apply your changes, click the save icon in the top right corner.</i>
  </main>
</div>

<script>
    var config = `+ JSON.stringify(wikiEditor.config) +`;
    function saveConfig() {
        var data = $('form').serializeArray().reduce(function(obj, item) {
                obj[item.name] = item.value;
                return obj;
                }, {}); // form data
        window.parent.postMessage("config\\\`"+ btoa(JSON.stringify(data))); // send to the big boss
    }

    function loadFromConfig() {
        $.each(config, function(key, value) {  
            var ctrl = $('[name='+key+']');  
                switch(ctrl.prop("type")) { 
                    case "radio": case "checkbox":   
                        ctrl.each(function() {
                            if($(this).attr('value') == value) $(this).attr("checked",value);
                        });   
                        break;  
                    default:
                        ctrl.val(value); 
                }  
        });  
    }

    loadFromConfig();
</script>
        `, document.body.offsetWidth, document.body.offsetHeight), true).showModal();
    }
}
// Processed from Twinkle source. See User Guide for more info.
var speedyDeleteReasons = {
    
    "anything, under ": [
        {
            "title": "G1: Nonsense.",
            "helpText": "Pages consisting purely of incoherent text or gibberish with no meaningful content or history. This does not include poor writing, partisan screeds, obscene remarks, vandalism, fictional material, material not in English, poorly translated material, implausible theories, or hoaxes. In short, if you can understand it, G1 does not apply.",
            "input": "",
            "inputMaxLength": 0,
            "inputTooltip": ""
        },
        {
            "title": "G2: Test page",
            "helpText": "A page created to test editing or other Wikipedia functions. Pages in the User namespace are not included, nor are valid but unused or duplicate templates (although criterion T3 may apply).",
            "input": "",
            "inputMaxLength": 0,
            "inputTooltip": ""
        },
        {
            "title": "G3: Pure vandalism",
            "helpText": "Plain pure vandalism (including redirects left behind from pagemove vandalism)",
            "input": "",
            "inputMaxLength": 0,
            "inputTooltip": ""
        },
        {
            "title": "G3: Blatant hoax",
            "helpText": "Blatant and obvious hoax, to the point of vandalism",
            "input": "",
            "inputMaxLength": 0,
            "inputTooltip": ""
        },
        {
            "title": "G4: Recreation of material deleted via a deletion discussion",
            "helpText": "A copy, by any title, of a page that was deleted via an XfD process or Deletion review, provided that the copy is substantially identical to the deleted version. This clause does not apply to content that has been \"userfied\", to content undeleted as a result of Deletion review, or if the prior deletions were proposed or speedy deletions, although in this last case, other speedy deletion criteria may still apply",
            "input": "Page where the deletion discussion took place: ",
            "inputMaxLength": 60,
            "inputTooltip": "Must start with \"Wikipedia:\""
        },
        {
            "title": "G5: Created by a banned or blocked user",
            "helpText": "Pages created by banned or blocked users in violation of their ban or block, and which have no substantial edits by others",
            "input": "Username of banned user (if available): ",
            "inputTooltip": "Should not start with \"User:\""
        },
        {
            "title": "G6: Move",
            "helpText": "Making way for an uncontroversial move like reversing a redirect"
        },
        {
            "title": "G6: Deletion discussion was closed as \"delete\"",
            "helpText": "A deletion discussion (at AfD, FfD, RfD, TfD, CfD, or MfD) was closed as \"delete\", but the page wasn't actually deleted.",
            "input": "Page where the deletion discussion was held: ",
            "inputMaxLength": 40,
            "inputTooltip": "Must start with \"Wikipedia:\""
        },
        {
            "title": "G6: Copy-and-paste page move",
            "helpText": "This only applies for a copy-and-paste page move of another page that needs to be temporarily deleted to make room for a clean page move.",
            "input": "Original page that was copy-pasted here: "
        },
        {
            "title": "G6: Housekeeping and non-controversial cleanup",
            "helpText": "Other routine maintenance tasks",
            "input": "Rationale: ",
            "inputMaxLength": 60
        },
        {
            "title": "G7: Author requests deletion, or author blanked",
            "helpText": "Any page for which deletion is requested by the original author in good faith, provided the page's only substantial content was added by its author. If the author blanks the page, this can also be taken as a deletion request.",
            "input": "Optional explanation: ",
            "inputMaxLength": 60,
            "inputTooltip": "Perhaps linking to where the author requested this deletion."
        },
        {
            "title": "G8: Pages dependent on a non-existent or deleted page",
            "helpText": "such as talk pages with no corresponding subject page; subpages with no parent page; file pages without a corresponding file; redirects to non-existent targets; or categories populated by deleted or retargeted templates. This excludes any page that is useful to the project, and in particular: deletion discussions that are not logged elsewhere, user and user talk pages, talk page archives, plausible redirects that can be changed to valid targets, and file pages or talk pages for files that exist on Wikimedia Commons.",
            "input": "Optional explanation: ",
            "inputMaxLength": 60
        },
        {
            "title": "G8: Subpages with no parent page",
            "helpText": "This excludes any page that is useful to the project, and in particular: deletion discussions that are not logged elsewhere, user and user talk pages, talk page archives, plausible redirects that can be changed to valid targets, and file pages or talk pages for files that exist on Wikimedia Commons.",
            "input": "",
            "inputMaxLength": 0,
            "inputTooltip": ""
        },
        {
            "title": "G10: Attack page",
            "helpText": "Pages that serve no purpose but to disparage or threaten their subject or some other entity (e.g., \"John Q. Doe is an imbecile\"). This includes a biography of a living person that is negative in tone and unsourced, where there is no NPOV version in the history to revert to. Administrators deleting such pages should not quote the content of the page in the deletion summary!",
            "input": "",
            "inputMaxLength": 0,
            "inputTooltip": ""
        },
        {
            "title": "G10: Wholly negative, unsourced BLP",
            "helpText": "A biography of a living person that is entirely negative in tone and unsourced, where there is no neutral version in the history to revert to.",
            "input": "",
            "inputMaxLength": 0,
            "inputTooltip": ""
        },
        {
            "title": "G11: Unambiguous advertising or promotion",
            "helpText": "Pages which exclusively promote a company, product, group, service, or person and which would need to be fundamentally rewritten in order to become encyclopedic. Note that an article about a company or a product which describes its subject from a neutral point of view does not qualify for this criterion; an article that is blatant advertising should have inappropriate content as well",
            "input": "",
            "inputMaxLength": 0,
            "inputTooltip": ""
        },
        {
            "title": "G12: Unambiguous copyright infringement",
            "helpText": "Either: (1) Material was copied from another website that does not have a license compatible with Wikipedia, or is photography from a stock photo seller (such as Getty Images or Corbis) or other commercial content provider; (2) There is no non-infringing content in the page history worth saving; or (3) The infringement was introduced at once by a single person rather than created organically on wiki and then copied by another website such as one of the many Wikipedia mirrors"
        },
        {
            "title": "G13: Page in draft namespace or userspace AfC submission, stale by over 6 months",
            "helpText": "Any rejected or unsubmitted AfC submission in userspace or any non-redirect page in draft namespace, that has not been edited for more than 6 months. Blank drafts in either namespace are also included.",
            "input": "",
            "inputMaxLength": 0,
            "inputTooltip": ""
        },
        {
            "title": "G14: Unnecessary disambiguation page",
            "helpText": "This only applies for orphaned disambiguation pages which either: (1) disambiguate only one existing Wikipedia page and whose title ends in \"(disambiguation)\" (i.e., there is a primary topic); or (2) disambiguate no (zero) existing Wikipedia pages, regardless of its title. It also applies to orphan \"Foo (disambiguation)\" redirects that target pages that are not disambiguation or similar disambiguation-like pages (such as set index articles or lists)",
            "input": "",
            "inputMaxLength": 0,
            "inputTooltip": ""
        }
    ],

    "a user page, under ": [
        {
            "title": "U1: User request",
            "helpText": "Personal subpages, upon request by their user. In some rare cases there may be administrative need to retain the page. Also, sometimes, main user pages may be deleted as well. See Wikipedia:User page for full instructions and guidelines",
            "input": "Explain why this user talk page should be deleted (required): ",
            "inputMaxLength": 60,
            "inputTooltip": "User talk pages are deleted only in highly exceptional circumstances. See WP:DELTALK."
        },
        {
            "title": "U2: Nonexistent user",
            "helpText": "User pages of users that do not exist (Check Special:Listusers)",
            "input": "",
            "inputMaxLength": 0,
            "inputTooltip": ""
        },
        {
            "title": "U3: Non-free galleries",
            "helpText": "Galleries in the userspace which consist mostly of \"fair use\" or non-free files. Wikipedia's non-free content policy forbids users from displaying non-free files, even ones they have uploaded themselves, in userspace. It is acceptable to have free files, GFDL-files, Creative Commons and similar licenses along with public domain material, but not \"fair use\" files",
            "input": "",
            "inputMaxLength": 0,
            "inputTooltip": ""
        },
        {
            "title": "U5: Blatant WP:NOTWEBHOST violations",
            "helpText": "Pages in userspace consisting of writings, information, discussions, and/or activities not closely related to Wikipedia's goals, where the owner has made few or no edits outside of userspace, with the exception of plausible drafts and pages adhering to WP:UPYES.",
            "input": "",
            "inputMaxLength": 0,
            "inputTooltip": ""
        },
        {
            "title": "G11: Promotional user page under a promotional user name",
            "helpText": "A promotional user page, with a username that promotes or implies affiliation with the thing being promoted. Note that simply having a page on a company or product in one's userspace does not qualify it for deletion. If a user page is spammy but the username is not, then consider tagging with regular G11 instead.",
            "input": "",
            "inputMaxLength": 0,
            "inputTooltip": ""
        },
        {
            "title": "G13: AfC draft submission or a blank draft, stale by over 6 months",
            "helpText": "Any rejected or unsubmitted AfC draft submission or a blank draft, that has not been edited in over 6 months (excluding bot edits).",
            "input": "",
            "inputMaxLength": 0,
            "inputTooltip": ""
        }
    ],

    "an article, under ": [
        {
            "title": "A1: No context. Articles lacking sufficient context to identify the subject of the article.",
            "helpText": "Example: \"He is a funny man with a red car. He makes people laugh.\" This applies only to very short articles. Context is different from content, treated in A3, below.",
            "input": "",
            "inputMaxLength": 0,
            "inputTooltip": ""
        },
        {
            "title": "A2: Foreign language articles that exist on another Wikimedia project",
            "helpText": "If the article in question does not exist on another project, the template {{notenglish}} should be used instead. All articles in a non-English language that do not meet this criteria (and do not meet any other criteria for speedy deletion) should be listed at Pages Needing Translation (PNT) for review and possible translation",
            "input": "Interwiki link to the article on the foreign-language wiki: ",
            "inputTooltip": "For example, fr:Bonjour"
        },
        {
            "title": "A3: No content whatsoever",
            "helpText": "Any article consisting only of links elsewhere (including hyperlinks, category tags and \"see also\" sections), a rephrasing of the title, and/or attempts to correspond with the person or group named by its title. This does not include disambiguation pages",
            "input": "",
            "inputMaxLength": 0,
            "inputTooltip": ""
        },
        {
            "title": "A5: Transwikied articles",
            "helpText": "Any article that has been discussed at Articles for Deletion (et al), where the outcome was to transwiki, and where the transwikification has been properly performed and the author information recorded. Alternately, any article that consists of only a dictionary definition, where the transwikification has been properly performed and the author information recorded",
            "input": "Link to where the page has been transwikied: ",
            "inputTooltip": "For example, https://en.wiktionary.org/wiki/hello or [[wikt:hello]]"
        },
        {
            "title": "A7: Unremarkable",
            "helpText": "An article about a real person, group of people, band, club, company, web content, individual animal, tour, or party that does not assert the importance or significance of its subject. If controversial, or if a previous AfD has resulted in the article being kept, the article should be nominated for AfD instead",
            "input": "",
            "inputMaxLength": 0,
            "inputTooltip": ""
        },
        {
            "title": "A7: Unremarkable person",
            "helpText": "An article about a real person that does not assert the importance or significance of its subject. If controversial, or if there has been a previous AfD that resulted in the article being kept, the article should be nominated for AfD instead",
            "input": "",
            "inputMaxLength": 0,
            "inputTooltip": ""
        },
        {
            "title": "A7: Unremarkable musician(s) or band",
            "helpText": "Article about a band, singer, musician, or musical ensemble that does not assert the importance or significance of the subject",
            "input": "",
            "inputMaxLength": 0,
            "inputTooltip": ""
        },
        {
            "title": "A7: Unremarkable club",
            "helpText": "Article about a club that does not assert the importance or significance of the subject",
            "input": "",
            "inputMaxLength": 0,
            "inputTooltip": ""
        },
        {
            "title": "A7: Unremarkable company or organization",
            "helpText": "Article about a company or organization that does not assert the importance or significance of the subject",
            "input": "",
            "inputMaxLength": 0,
            "inputTooltip": ""
        },
        {
            "title": "A7: Unremarkable website or web content",
            "helpText": "Article about a web site, blog, online forum, webcomic, podcast, or similar web content that does not assert the importance or significance of its subject",
            "input": "",
            "inputMaxLength": 0,
            "inputTooltip": ""
        },
        {
            "title": "A7: Unremarkable individual animal",
            "helpText": "Article about an individual animal (e.g. pet) that does not assert the importance or significance of its subject",
            "input": "",
            "inputMaxLength": 0,
            "inputTooltip": ""
        },
        {
            "title": "A7: Unremarkable organized event",
            "helpText": "Article about an organized event (tour, function, meeting, party, etc.) that does not assert the importance or significance of its subject",
            "input": "",
            "inputMaxLength": 0,
            "inputTooltip": ""
        },
        {
            "title": "A9: Unremarkable musical recording where artist's article doesn't exist",
            "helpText": "An article about a musical recording which does not indicate why its subject is important or significant, and where the artist's article has never existed or has been deleted",
            "input": "",
            "inputMaxLength": 0,
            "inputTooltip": ""
        },
        {
            "title": "A10: Recently created article that duplicates an existing topic",
            "helpText": "A recently created article with no relevant page history that does not aim to expand upon, detail or improve information within any existing article(s) on the subject, and where the title is not a plausible redirect. This does not include content forks, split pages or any article that aims at expanding or detailing an existing one.",
            "input": "Article that is duplicated: "
        },
        {
            "title": "A11: Obviously made up by creator, and no claim of significance",
            "helpText": "An article which plainly indicates that the subject was invented/coined/discovered by the article's creator or someone they know personally, and does not credibly indicate why its subject is important or significant",
            "input": "",
            "inputMaxLength": 0,
            "inputTooltip": ""
        }
    ],
    
    "a talk page, under ": [
        {
            "title": "G8: Talk pages with no corresponding subject page",
            "helpText": "This excludes any page that is useful to the project - in particular, user talk pages, talk page archives, and talk pages for files that exist on Wikimedia Commons.",
            "input": "",
            "inputMaxLength": 0,
            "inputTooltip": ""
        }
    ],
    "a file, under ": [
        {
            "title": "F1: Redundant file",
            "helpText": "Any file that is a redundant copy, in the same file format and same or lower resolution, of something else on Wikipedia. Likewise, other media that is a redundant copy, in the same format and of the same or lower quality. This does not apply to files duplicated on Wikimedia Commons, because of licence issues; these should be tagged with {{Now Commons|Image:newname.ext|date=1 May 2020}} or {{Now Commons|date=1 May 2020}} instead",
            "input": "File this is redundant to: ",
            "inputTooltip": "The \"File:\" prefix can be left off."
        },
        {
            "title": "F2: Corrupt, mising, or empty file",
            "helpText": "Before deleting this type of file, verify that the MediaWiki engine cannot read it by previewing a resized thumbnail of it. This also includes empty (i.e., no content) file description pages for Commons files",
            "input": "",
            "inputMaxLength": 0,
            "inputTooltip": ""
        },
        {
            "title": "F2: Unneeded file description page for a file on Commons",
            "helpText": "An image, hosted on Commons, but with tags or information on its English Wikipedia description page that are no longer needed. (For example, a failed featured picture candidate.)",
            "input": "",
            "inputMaxLength": 0,
            "inputTooltip": ""
        },
        {
            "title": "F3: Improper license",
            "helpText": "Files licensed as \"for non-commercial use only\", \"non-derivative use\" or \"used with permission\" that were uploaded on or after 2005-05-19, except where they have been shown to comply with the limited standards for the use of non-free content. This includes files licensed under a \"Non-commercial Creative Commons License\". Such files uploaded before 2005-05-19 may also be speedily deleted if they are not used in any articles",
            "input": "",
            "inputMaxLength": 0,
            "inputTooltip": ""
        },
        {
            "title": "F4: Lack of licensing information",
            "helpText": "Files in category \"Files with unknown source\", \"Files with unknown copyright status\", or \"Files with no copyright tag\" that have been tagged with a template that places them in the category for more than seven days, regardless of when uploaded. Note, users sometimes specify their source in the upload summary, so be sure to check the circumstances of the file.",
            "input": "",
            "inputMaxLength": 0,
            "inputTooltip": ""
        },
        {
            "title": "F5: Unused non-free copyrighted file",
            "helpText": "Files that are not under a free license or in the public domain that are not used in any article, whose only use is in a deleted article, and that are very unlikely to be used on any other article. Reasonable exceptions may be made for files uploaded for an upcoming article.",
            "input": "",
            "inputMaxLength": 0,
            "inputTooltip": ""
        },
        {
            "title": "F6: Missing fair-use rationale",
            "helpText": "Any file without a fair use rationale may be deleted seven days after it is uploaded. Boilerplate fair use templates do not constitute a fair use rationale.",
            "input": "",
            "inputMaxLength": 0,
            "inputTooltip": ""
        },
        {
            "title": "F7: Clearly invalid fair-use tag",
            "helpText": "This is only for files with a clearly invalid fair-use tag, such as a {{Non-free logo}} tag on a photograph of a mascot.",
            "input": "Optional explanation: ",
            "inputMaxLength": 60
        },
        {
            "title": "F7: Fair-use media from a commercial image agency which is not the subject of sourced commentary",
            "helpText": "Non-free images or media from a commercial source (e.g., Associated Press, Getty), where the file itself is not the subject of sourced commentary, are considered an invalid claim of fair use and fail the strict requirements of WP:NFCC.",
            "input": "Optional explanation: ",
            "inputMaxLength": 60
        },
        {
            "title": "F8: File available as an identical or higher-resolution copy on Wikimedia Commons",
            "helpText": "Provided the following conditions are met: 1: The file format of both images is the same. 2: The file's license and source status is beyond reasonable doubt, and the license is undoubtedly accepted at Commons. 3: All information on the file description page is present on the Commons file description page. That includes the complete upload history with links to the uploader's local user pages. 4: The file is not protected, and the file description page does not contain a request not to move it to Commons. 5: If the file is available on Commons under a different name than locally, all local references to the file must be updated to point to the title used at Commons. 6: For {{c-uploaded}} files: They may be speedily deleted as soon as they are off the Main Page",
            "input": "Filename on Commons: ",
            "inputTooltip": "This can be left blank if the file has the same name on Commons as here. The \"File:\" prefix is optional."
        },
        {
            "title": "F9: Unambiguous copyright infringement",
            "helpText": "The file was copied from a website or other source that does not have a license compatible with Wikipedia, and the uploader neither claims fair use nor makes a credible assertion of permission of free use. Sources that do not have a license compatible with Wikipedia include stock photo libraries such as Getty Images or Corbis. Non-blatant copyright infringements should be discussed at Wikipedia:Files for deletion"
        },
        {
            "title": "F10: Useless non-media file",
            "helpText": "Files uploaded that are neither image, sound, nor video files (e.g. .doc, .pdf, or .xls files) which are not used in any article and have no foreseeable encyclopedic use",
            "input": "",
            "inputMaxLength": 0,
            "inputTooltip": ""
        },
        {
            "title": "F11: No evidence of permission",
            "helpText": "If an uploader has specified a license and has named a third party as the source/copyright holder without providing evidence that this third party has in fact agreed, the item may be deleted seven days after notification of the uploader",
            "input": "",
            "inputMaxLength": 0,
            "inputTooltip": ""
        },
        {
            "title": "G8: File description page with no corresponding file",
            "helpText": "This is only for use when the file doesn't exist at all. Corrupt files, and local description pages for files on Commons, should use F2; implausible redirects should use R3; and broken Commons redirects should use R4.",
            "input": "",
            "inputMaxLength": 0,
            "inputTooltip": ""
        }
    ],
    
    "a category, under ": [
        {
            "title": "C1: Empty categories",
            "helpText": "Categories that have been unpopulated for at least seven days. This does not apply to categories being discussed at WP:CFD, disambiguation categories, and certain other exceptions. If the category isn't relatively new, it possibly contained articles earlier, and deeper investigation is needed",
            "input": "",
            "inputMaxLength": 0,
            "inputTooltip": ""
        },
        {
            "title": "G8: Categories populated by a deleted or retargeted template",
            "helpText": "This is for situations where a category is effectively empty, because the template(s) that formerly placed pages in that category are now deleted. This excludes categories that are still in use.",
            "input": "",
            "inputMaxLength": 0,
            "inputTooltip": ""
        },
        {
            "title": "G8: Redirects to non-existent targets",
            "helpText": "This excludes any page that is useful to the project, and in particular: deletion discussions that are not logged elsewhere, user and user talk pages, talk page archives, plausible redirects that can be changed to valid targets, and file pages or talk pages for files that exist on Wikimedia Commons.",
            "input": "",
            "inputMaxLength": 0,
            "inputTooltip": ""
        }
    ],
    
    "a template, under ": [
        {
            "title": "T2: Templates that are blatant misrepresentations of established policy",
            "helpText": "This includes \"speedy deletion\" templates for issues that are not speedy deletion criteria and disclaimer templates intended to be used in articles",
            "input": "Optional explanation: ",
            "inputMaxLength": 60
        },
        {
            "title": "T3: Duplicate templates or hardcoded instances",
            "helpText": "Templates that are either substantial duplications of another template or hardcoded instances of another template where the same functionality could be provided by that other template",
            "input": "Template this is redundant to: ",
            "inputTooltip": "The \"Template:\" prefix is not needed."
        }
    ],
    "a portal, under ": [
        {
            "title": "P1: Portal that would be subject to speedy deletion if it were an article",
            "helpText": "You must specify a single article criterion that applies in this case (A1, A3, A7, or A10).",
            "input": "Article criterion that would apply: "
        },
        {
            "title": "P2: Underpopulated portal (fewer than three non-stub articles)",
            "helpText": "Any Portal based on a topic for which there is not a non-stub header article, and at least three non-stub articles detailing subject matter that would be appropriate to discuss under the title of that Portal",
            "input": "",
            "inputMaxLength": 0,
            "inputTooltip": ""
        }
    ],

    "a redirect, under ": [
        {
            "title": "R2: Redirect from mainspace to any other namespace.",
            "helpText": "Excluding the Category:, Template:, Wikipedia:, Help: and Portal: namespaces. This does not include the pseudo-namespace shortcuts. If this was the result of a page move, consider waiting a day or two before deleting the redirect",
            "input": "",
            "inputMaxLength": 0,
            "inputTooltip": ""
        },
        {
            "title": "R3: Recently created redirect from an implausible typo or misnomer",
            "helpText": "However, redirects from common misspellings or misnomers are generally useful, as are redirects in other languages",
            "input": "",
            "inputMaxLength": 0,
            "inputTooltip": ""
        },
        {
            "title": "R4: File namespace redirect with a name that matches a Commons page",
            "helpText": "The redirect should have no incoming links (unless the links are cleary intended for the file or redirect at Commons).",
            "input": "",
            "inputMaxLength": 0,
            "inputTooltip": ""
        },
        {
            "title": "G6: Redirect to malplaced disambiguation page",
            "helpText": "This only applies for redirects to disambiguation pages ending in (disambiguation) where a primary topic does not exist.",
            "input": "",
            "inputMaxLength": 0,
            "inputTooltip": ""
        },
        {
            "title": "G8: Redirects to non-existent targets",
            "helpText": "This excludes any page that is useful to the project, and in particular: deletion discussions that are not logged elsewhere, user and user talk pages, talk page archives, plausible redirects that can be changed to valid targets, and file pages or talk pages for files that exist on Wikimedia Commons.",
            "input": "",
            "inputMaxLength": 0,
            "inputTooltip": ""
        }
    ],
    "write a ": [
        {
            "title": "custom reason",
            "helpText": "At least one of the other deletion criteria must still apply to the page, and you must make mention of this in your rationale. This is not a \"catch-all\" for when you can't find any criteria that fit.",
            "input": "Rationale: ",
            "inputMaxLength": 60
        }
    ]
};

    $( document ).ready( function () {
        // Init when page loaded
      try {
        initwikiEdit();
      } catch (err) {
        mw.notify("Sorry, an error occured while loading RedWarn.");
        console.error(err);
      }
    } );
// </nowiki>