User:Bluedeck/etc/sandbox/box1560746440782

注意:保存之后,你必须清除浏览器缓存才能看到做出的更改。Google ChromeFirefoxMicrosoft EdgeSafari:按住⇧ Shift键并单击工具栏的“刷新”按钮。参阅Help:绕过浏览器缓存以获取更多帮助。

const accepted_languages = (function(){

    const PREVENT_INFINITE_LOOP = 1000;

    class Language_proximity_group {

        parent:     Language_proximity_group|null;
        desc:       string;
        sub_groups: Array<Language_proximity_group>;
        languages:  Array<string>;

        constructor(desc : string) {

            this.parent = null;
            this.desc = desc;
            this.sub_groups = [];
            this.languages = [];
        }

        all() : Array<string> {

            let all_codes = [...this.languages];

            for(let i=0; i<this.sub_groups.length; i++)
                all_codes.push(...this.sub_groups[i].all())

            return all_codes;
        }

        all_and_desc() : Array<string> {

            let all_codes = [this.desc, ...this.languages];

            for(let i=0; i<this.sub_groups.length; i++)
                all_codes.push(...this.sub_groups[i].all_and_desc())

            return all_codes;
        }

        has(lang_code : string) : boolean {

            return this.all().indexOf(lang_code) !== -1;
        }

        add_one(item : string|Language_proximity_group) : Language_proximity_group {

            if(typeof item === "string" && this.languages.indexOf(item) === -1)
                this.languages.push(item);
            else if(typeof item === "object" /* && item !== null (ensured by type system) */ && item.constructor === Language_proximity_group && this.sub_groups.indexOf(item) === -1)
                item.parent = this, this.sub_groups.push(item);
            else
                throw new TypeError("cannot add this item " + typeof item + ". add only language code and proximity groups.");
            return this;
        }

        add(...args : Array<string|Language_proximity_group>) : Language_proximity_group {

            for(let i=0; i<args.length; i++) {

                this.add_one(args[i]);
            }
            return this;
        }

        // return the group in which the language code resides.
        group_of(code : string) :Language_proximity_group|null {

            if(this.languages.indexOf(code) !== -1) {

                // desired language is in this group
                return this;
            }
            else if(this.desc === code) {

                // this is the desired group
                return this;
            }
            else if(this.all_and_desc().indexOf(code) !== -1) {

                // desired language is not in this group, but in a sub group of this group
                for(let i=0; i<this.sub_groups.length; i++) {

                    let search_result = this.sub_groups[i].group_of(code);
                    if(search_result !== null)
                        return search_result;
                }
                // search has finished but all subgroups returned null, which should not happen since the desired language is alleged to be in one of these sub groups, as per the opening condition of this if-else ladder.
                console.log("reporting a [peculiarity 8211], from .group_of() in Language_proximity_group(). Lang code alleged to be in one subgroup but never found. this line is never intended to be executed.");
                return null;
            }
            else {

                // desired language is not in this group
                return null
            }
        }
    }

    // define friends

    const group_north_am_eng =     new Language_proximity_group("en_n_a"   ).add("en_us", "en_ca");
    const group_aus_nz_eng =       new Language_proximity_group("en_au_nz" ).add("en_au", "en_nz");
    const group_dominant_english = new Language_proximity_group("en_native").add(group_north_am_eng, "en_uk", group_aus_nz_eng);
    const group_english =          new Language_proximity_group("en"       ).add(group_dominant_english, "en_sg", "en_hk");
    const group_french =           new Language_proximity_group("fr"       ).add("fr_fr", "fr_ch");
    const group_german =           new Language_proximity_group("de"       ).add("de_de", "de_at", "de_ch", "de_li");
    const group_spanish =          new Language_proximity_group("es"       ).add("es_es", "es_mx", "es_pr", "es_cl");
    const group_portuguese =       new Language_proximity_group("pt"       ).add("pt_pt", "pt_br");
    const group_dutch =            new Language_proximity_group("nl"       ).add("nl_nl");
    const group_polish =           new Language_proximity_group("pl"       ).add("pl_pl");
    const group_swedish =          new Language_proximity_group("sv"       ).add("sv_se", "sv_fi");
    const group_finnish =          new Language_proximity_group("fi"       ).add("fi_fi");
    const group_norwegian =        new Language_proximity_group("no"       ).add("no_no");
    const group_nordic =           new Language_proximity_group("nordic"   ).add(group_swedish, group_finnish, group_norwegian);
    const group_european =         new Language_proximity_group("european" ).add(group_english, group_french, group_german, group_spanish, group_nordic, group_portuguese, group_dutch, group_polish);

    const group_zh_hans =          new Language_proximity_group("zh_hans"  ).add("zh_cn", "zh_sg");
    const group_zh_hant =          new Language_proximity_group("zh_hant"  ).add("zh_hk", "zh_tw", "zh_mo");
    const group_zh =               new Language_proximity_group("zh"       ).add(group_zh_hans, group_zh_hant);
    const group_japanese =         new Language_proximity_group("ja"       ).add("ja_jp");
    const group_korean =           new Language_proximity_group("ko"       ).add("ko_kr");
    const group_cjk =              new Language_proximity_group("asian_cjk").add(group_zh, group_japanese, group_korean);
    const group_arabic =           new Language_proximity_group("ar"       ).add("ar_sa", "ar_eg");
    const group_asian =            new Language_proximity_group("asian"    ).add(group_cjk, group_arabic);

    const group_world =            new Language_proximity_group("world"    ).add(group_asian, group_european);

    const country_code_dict : any = {
        "au" : "en_au",  // english    of commonwealth of australia
        "at" : "de_at",  // deutsch    of austria
        "ca" : "en_ca",  // english    of canada
        "ch" : "de_ch",  // deutsch    of swiss confederation
        "cl" : "es_cl",  // espanol    of chile
        "cn" : "zh_cn",  // zhongwen   of people's republic of china
        "de" : "de_de",  // deutsch    of fed republic of germany
        "es" : "es_es",  // espanol    of spain
        "fi" : "fi_fi",  // finnish    of finland
        "fr" : "fr_fr",  // francais   of french republic
        "hk" : "zh_hk",  // zhongwen   of hong kong s.a.r.
        "jp" : "ja_jp",  // nihongo    of japan
        "kr" : "ko_kr",  // korean     of republic of korea (south korea)
        "li" : "de_li",  // deutsch    of principality of liechtenstein
        "mx" : "es_mx",  // espanol    of mexico
        "no" : "no_no",  // norwegien  of norway
        "nz" : "en_us",  // english    of new-zealand
        "pr" : "es_pr",  // espanol    of puerto rico
        "pt" : "pt_pt",  // portuguese of portugal
        "tw" : "zh_tw",  // zhongwen   of taiwan, republic of china
        "sg" : "zh_cn",  // zhongwen   of singapore
        "se" : "sv_se",  // svenska    of sweden
        "uk" : "en_uk",  // english    of the united kingdom of g.b. and n.i.
        "us" : "en_us",  // english    of the u.s. of a.
    };

    const customary_code_dict : any = {
        "zh_hans": "zh_cn",
        "zh_hant": "zh_hk",
        "zh_classic": "zh_classic",
        "xxx": "xxx",
    };

    // how to use: call this function with raw language code to get the language code that has locale in it. like this: accepted_languages("jp") > "ja_jp" accepted_languages("ch") > "de_ch"
    // ... this function returns the precise locale related to your input, and returns null if your input is not recognizable.
    // ... but sometimes you don't really have the exact locale interface. e.g. you have de_de but not de_ch nor de_li. then you can apply restraints by setting the second parameter.
    // ... i.e. if you set your restraints, this function will always return one of your "accepted languages", while trying its best deviating from the input language as little as possible.
    // ... examples.
    // ... accepted_languages("ca") > "en_ca". (because ca is canada and canada is dominantly english)
    // ... accepted_languages("ca", ["unrecognized", "en_ca", "en_us", "en_uk", "en_au", "fr_fr", "ja_jp"]) > "en_ca" (because the result should be canada and it's accepted)
    // ... accepted_languages("ca", ["unrecognized", "en_us", "en_uk", "en_au", "fr_fr", "ja_jp"])          > "en_us" (because they're both north american, so when en_ca is not accepted, en_us is the next best thing)
    // ... accepted_languages("ca", ["unrecognized", "en_uk", "en_au", "fr_fr", "ja_jp"])                   > "en_uk" (because they're both from dominantly native english nations)
    // ... accepted_languages("ca", ["unrecognized", "en_au", "fr_fr", "ja_jp"])                            > "en_au" (ditto)
    // ... accepted_languages("ca", ["unrecognized", "fr_fr", "ja_jp"])                                     > "fr_fr" (because they're both european languages.)

    function accepted_languages(raw_lang_code : string, all_acceptable_codes : Array<string>|null = null) {

        const processed_lang_code = raw_lang_code.toLowerCase().replace(/[^a-z]/g, "_");
        const processed_lang_code_short = processed_lang_code.slice(0,2);
        const processed_lang_code_group = group_world.group_of(processed_lang_code);
        const processed_lang_code_short_group = group_world.group_of(processed_lang_code_short);
        let best_match;

        // try language code from group
        if(processed_lang_code_group && processed_lang_code_group.has(processed_lang_code))
            best_match =  processed_lang_code;
        else if(processed_lang_code_group && processed_lang_code_group.all()[0])
            best_match = processed_lang_code_group.all()[0];
        else if(processed_lang_code_short_group && processed_lang_code_short_group.has(processed_lang_code_short))
            best_match = processed_lang_code_short;

        // try customary language code
        else if(processed_lang_code in customary_code_dict)
            best_match = customary_code_dict[processed_lang_code];

        // try with country code
        else if(processed_lang_code in country_code_dict)
            best_match = country_code_dict[processed_lang_code];
        else if(processed_lang_code_short in country_code_dict)
            best_match = country_code_dict[processed_lang_code_short];
        else
            best_match = null;

        if(all_acceptable_codes === null || all_acceptable_codes.length === 0 || all_acceptable_codes.indexOf(best_match) !== -1)
            return best_match;
        else { // assert all_acceptable_codes instanceof Array, assert all_acceptable_codes does not contain best match, assert all_acceptable_codes has at least 1 entry

            let current_group = group_world.group_of(best_match);
            if(current_group === null) { // best match is null or best match language not in group world.

                return all_acceptable_codes[0];
            }
            else {
                // this was originally a while(true) loop, but I changed it into a for loop that will force quit if it runs more than a specified length,
                // just as a fail safe in case the current_group.parent somehow referred to itself.
                for(let i=0; i<PREVENT_INFINITE_LOOP; i++) {
                    let close_match_group = current_group.all();
                    for(let i=0; i<close_match_group.length; i++) {
                        if (all_acceptable_codes.indexOf(close_match_group[i]) !== -1)
                            return close_match_group[i];

                    }
                    current_group = current_group.parent
                    if(current_group === null)
                        return all_acceptable_codes[0]
                    else
                        continue
                }
                // control flow is not supposed to reach here because the way Language_proximity_group is set up.
                console.log('reporting [peculiarity 0879], forced jump out of a suspected 1k loop that should not happen. This indicates that a faulty language proximity group has one/multiple parent pointer(s) that formed a circle.');
                return all_acceptable_codes[0];
            }
        }
    }

    return accepted_languages;
})();

const pick_best_fit_from_chamber = function(chamber : any, lang_code : string) {
    const best_fit_lang_code = accepted_languages(lang_code, Object.keys(chamber));
    return chamber[best_fit_lang_code];
};

const lang_code = (function(){
    try{
        return window["mw"].config.values.wgUserLanguage
    }
    catch(e){
        return "en-us"
    }
})();

function mlc(chamber : any) : string {

    return pick_best_fit_from_chamber(chamber, lang_code)
}

/***
 * =======================================================
 * ================= END OF DEPENDENCIES =================
 * =======================================================
 * 
 */

const lang_dict = {
    "get-original": {
        "en_us": "Get original wikitext",
        "ja_jp": "素のウィキテキストを取得",
        "zh_tw": "取得原始維基源碼",
        "zh_cn": "取得原始维基源码",
    },
    "fetching-data": {
        "en_us": "Loading...",
        "ja_jp": "読み込み中...",
        "zh_tw": "正在加載...",
        "zh_cn": "正在加载...",
    },
    "done": {
        "en_us": "Done",
        "ja_jp": "処理完了",
        "zh_tw": "處理完畢",
        "zh_cn": "处理完毕",
    },
    "portable-wikitext": {
        "en_us": "Original wikitext",
        "ja_jp": "素のウィキテキスト",
        "zh_tw": "原始維基源碼",
        "zh_cn": "原始维基源码",
    }
}

class NotImplementedError extends Error {}

const shadow_dom_emulated_style_sheet_classname_prefix = `bluedeck-dom-attach-endpoint-blib-inverse-7da8e2387efed3afecf8646d3138792b-61e34505a712c5ea11704af9b2ab6857-${Date.now()}-`

function shadow_dom_classname(naive_classname : string) {

    return shadow_dom_emulated_style_sheet_classname_prefix + naive_classname
}

const button_class_name = shadow_dom_classname("65a492779c6e34e6e9b67cf7c250a3b4-d09b123b8b268823b39963438b0d305c")
const textarea_class_name = shadow_dom_classname("9c109a05988139fb7c85647e63c07a6f-8d2755bbbcc55eb8e6146e53693a2159")

const stylesheet_text = `

.${button_class_name} {

    border-radius: 99em;
    border: 2px solid #fff;
    background: transparent;
    padding: 0.1em 0.6em;
    color: #fff;
}

.${button_class_name}:active {

    background: rgba(255,255,255,0.2);
}

.${button_class_name}:disabled {
    
    opacity: 0.7;
}

.${textarea_class_name} {
    font-size: 14px;
    width: 100%;
    height: 14em;
    margin-bottom: 2em;
    padding: 0.75em;
    white-space: pre;
    transition: background 1.75s;
    background: rgb(255,255,215)
}

`

const style_ele : HTMLStyleElement = document.createElement("style")
style_ele.innerHTML = stylesheet_text

document.head.appendChild(style_ele)

const button_outpost_classname_identifier = "bluedeck-dom-attach-endpoint-blib-inverse-23b613580bdf0518380bb6f762685e1c-604471d83ca088bbb3c58b8d5df312e7"
const textarea_outpost_classname_identifier = "bluedeck-dom-attach-endpoint-blib-inverse-d6cdf46ebbf2b58950b91edfcec63587-4eb2060a815398e4165f94f6e7af32df"
const button_outpost_elements_list = Array.from(document.getElementsByClassName(button_outpost_classname_identifier))
const textarea_outpost_elements_list = Array.from(document.getElementsByClassName(textarea_outpost_classname_identifier))

const button_trigger_element_collection : HTMLButtonElement[] = []
button_outpost_elements_list.forEach(div_element => {
    const new_button_trigger = button_trigger_factory()
    div_element.innerHTML = ""
    div_element.appendChild(new_button_trigger) 
    button_trigger_element_collection.push(new_button_trigger)
})

function button_trigger_factory() : HTMLButtonElement {
    const new_button : HTMLButtonElement = document.createElement("button")
    new_button.innerHTML = mlc(lang_dict["get-original"])
    new_button.addEventListener("click", handle_click)
    new_button.classList.add(button_class_name)
    return new_button
}

async function get_this_page_wikitext() : Promise<string> {
    const page_name : string = window["mw"].config.values.wgPageName
    const request_uri : string = `/w/index.php?title=${encodeURIComponent(page_name)}&action=raw`
    const response : Response = await fetch(request_uri)
    const response_text : string = await response.text()
    return response_text
}

async function handle_click() : Promise<void> {

    ui_disable_button_triggers()
    const this_page_wikitext = await get_this_page_wikitext()
    const text_after_blib_inverse = blib_inverse_workload(this_page_wikitext)
    ui_button_triggers_indicate_completion()
    ui_display_inverse_result(text_after_blib_inverse)
}

function blib_inverse_workload(incoming_text : string) : string {
    
    var card_start : number = incoming_text.indexOf("{{user:bluedeck/infr/library.card.js");
    var card_end : number = incoming_text.substr(card_start).indexOf("}}") + card_start;

    if(card_start !== -1 && card_end !== -1)
        incoming_text = incoming_text.substr(0, card_start) + incoming_text.substr(card_end + 2);

    return incoming_text
        .replace(/\[\[\:分類\:/g, "[[分類:")
        .replace(/\[\[\:分类\:/g, "[[分类:")
        .replace(/\[\[\:Category\:/g, "[[Category:")
        .replace(/\{\{\(\(\}\}/g, "{{");
}

function ui_disable_button_triggers() : void {
    button_trigger_element_collection.forEach(button_element => {
        button_element.disabled = true
        button_element.innerHTML = mlc(lang_dict["fetching-data"])
    }) 
}

function ui_button_triggers_indicate_completion() : void {
    button_trigger_element_collection.forEach(button_element => {
        button_element.innerHTML = mlc(lang_dict["done"])
    })
}

function ui_display_inverse_result(inverse_result : string) : void {

    textarea_outpost_elements_list.forEach(div_element => {
        div_element.innerHTML = `<div style="padding: 0.12em 0.14em; border-radius: 0.3em; background: #eee; opacity: 0.83; font-size: 90%">${mlc(lang_dict["portable-wikitext"])}</div>`
        const new_textarea : HTMLTextAreaElement = textarea_element_factory(inverse_result)
        div_element.appendChild(new_textarea)
        setTimeout(() => {new_textarea.style.background = "#fff"}, 150)
    })
}

function textarea_element_factory(initial_content : string) : HTMLTextAreaElement {
    const new_textarea : HTMLTextAreaElement = document.createElement("textarea")
    new_textarea.value = initial_content
    new_textarea.classList.add(textarea_class_name)
    return new_textarea
}