Chú ý: Sau khi lưu trang này, phải xóa bộ nhớ đệm (cache) của trình duyệt để những thay đổi hiện ra

  • Firefox / Safari: Nhấn giữ phím Shift trong khi nhấn Tải lại (Reload), hoặc nhấn tổ hợp Ctrl-F5 hay Ctrl-R (⌘R trên Mac)
  • Google Chrome: Nhấn tổ hợp Ctrl-Shift-R (⇧⌘R trên Mac)
  • Internet Explorer / Edge: Nhấn giữ phím Ctrl trong khi nhấn Làm tươi (Refresh), hoặc nhấn tổ hợp Ctrl-F5
  • Opera: Nhấn tổ hợp Ctrl-F5.
/* Sectionizer 1.0 for the Vietnamese Wiktionary
 * Inspired by [[w:en:User:Magnus Manske/less edit clutter.js]]
 * Created: 17 March 2008, [[wikt:vi:User:Mxn|Minh Nguyen]]
 * Last modified: 21 October 2011, [[wikt:vi:User:Mxn|Minh Nguyen]]
 */

/*
Copyright (c) 2008 Minh Nguyen.

Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
*/

var wiktSectionizer;
function Sectionizer() {
    // Pre-existing HTML form (<form>)
    if (!document.getElementById("editform")) return;
    
    // Pre-existing HTML edit box (<textarea>)
    this.edit_box = document.getElementById("wpTextbox1");
    if (!this.edit_box) return;
    
    // HTML insert panel
    this.insert_list;
    this.insert_btn;
    
    // HTML section menu
    this.sect_list;
    this.remove_btn;
    this.up_btn;
    this.down_btn;
    
    // Mapping from ISO 639-2 codes to language names in Vietnamese;
    // alphabetical by language name
    this.langs = {
        afr: "tiếng Afrikaans", amu: "tiếng Amuzgo", ang: "tiếng Anh cổ",
        ara: "tiếng Ả Rập", ast: "tiếng Asturia", bos: "tiếng Bosnia",
        cat: "tiếng Catalan", cdo: "tiếng Mân Đông", ces: "tiếng Séc",
        cjy: "tiếng Tấn", cmn: "tiếng Quan Thoại", cpx: "tiếng Phổ Hiền",
        czh: "tiếng Huy", czo: "tiếng Mân Trung", dan: "tiếng Đan Mạch",
        deu: "tiếng Đức", dng: "tiếng Đông Can", ell: "tiếng Hy Lạp",
        eng: "tiếng Anh", epo: "Quốc tế ngữ", est: "tiếng Estonia",
        eus: "tiếng Basque", fao: "tiếng Faroe", fas: "tiếng Ba Tư",
        fin: "tiếng Phần Lan", fra: "tiếng Pháp", fry: "tiếng Frysk",
        gan: "tiếng Cống", gle: "tiếng Ireland", glg: "tiếng Galicia",
        grn: "tiếng Guaraní", hak: "tiếng Khách Gia", haw: "tiếng Hawaii",
        heb: "tiếng Do Thái", hin: "tiếng Hindi", hrv: "tiếng Croat",
        hsn: "tiếng Tương", hun: "tiếng Hung", ido: "tiếng Ido",
        ina: "tiếng Bổ trợ Quốc tế", ind: "tiếng Indonesia", isl: "tiếng Băng Đảo",
        ita: "tiếng Ý", jbo: "tiếng Lojban", jpn: "tiếng Nhật",
        kor: "tiếng Triều Tiên", kur: "tiếng Kurd", lat: "tiếng Latinh",
        mar: "tiếng Marathi", mlt: "tiếng Malta", mnp: "tiếng Mân Bắc",
        msa: "tiếng Mã Lai", nan: "tiếng Mân Nam", nap: "tiếng Napoli",
        nep: "tiếng Nepal", nld: "tiếng Hà Lan", nno: "tiếng Na Uy (Nynorsk)",
        nob: "tiếng Na Uy (Bokmål)", nor: "tiếng Na Uy", oci: "tiếng Oc",
        pap: "tiếng Papiamento", pol: "tiếng Ba Lan", por: "tiếng Bồ Đào Nha",
        qya: "tiếng Quenya", "roa-ptg": "tiếng Bồ Đào Nha cổ", ron: "tiếng Romana",
        rus: "tiếng Nga", sga: "tiếng Ireland cổ", slk: "tiếng Slovak",
        sna: "tiếng Shona", spa: "tiếng Tây Ban Nha", sqi: "tiếng Albani",
        sux: "tiếng Sumer", swa: "tiếng Swahili", swe: "tiếng Thụy Điển",
        tat: "tiếng Tatar", tgl: "tiếng Tagalog", tha: "tiếng Thái",
        tpi: "tiếng Tok Pisin", tpn: "tiếng Tupi", tur: "tiếng Thổ Nhĩ Kỳ",
        vie: "tiếng Việt", wuu: "tiếng Ngô", yue: "tiếng Quảng Đông",
        zho: "tiếng Trung Quốc", Brai: "chữ Braille", Cham: "chữ Chăm",
        Hani: "chữ Hán", Hans: "chữ Hán giản thể", Hant: "chữ Hán phồn thể",
        Hang: "chữ Hangul", Hira: "chữ Hiragana", Grek: "chữ Hy Lạp",
        Cyrl: "chữ Kirin", Latn: "chữ Latinh", Jpan: "chữ Nhật hỗn hợp",
        "vie-n": "chữ Nôm", Runr: "chữ Rune", Xsux: "chữ Sumer",
        Teng: "chữ Tengwar", Thai: "chữ Thái"
    };
    
    // List of data objects for each section; each object contains "name", "id",
    // and "orig_source", "source", and "moved" fields
    this.sect_objs = [];
    
    // Current section ID (usually the ISO 639-2 code)
    this.cur_sect;
    
    // Capitalize the first letter in a string
    this.capitalize = function (str) {
        return str ? str.substring(0, 1).toUpperCase() + str.substring(1) : str;
    };
    
    // Generate a list of data objects representing each language section
    this.get_sections = function (src) {
        var sect_objs = [];
        
        var tmpls = "";
        for (lang in this.langs) tmpls += "|" + lang;
        tmpls = tmpls.substring(1);
        
        src = src.replace(new RegExp("\{\{-(" + tmpls + ")-\}\}\r?\n?", "g"),
                          "<<>$1<>>");
        src = "<<>_head<>>" + src;
        var sects = src.split(/<<>(?=[^<]+<>>)/);
        var start = 0 + (sects[0].length == "");
        
        // Entry head
        var head = {
            name: "[Đoạn đầu]", id: "_head", source: "", orig_source: "",
            moved: false
        };
        head.source = sects[start].substring("_head<>>".length);
        head.orig_source = head.source;
        sect_objs.push(head);
        
        // Entry body
        for (var i = start + 1; i < sects.length; i++) {
            var m = sects[i].match(/^([^<]+)<>>/);
            if (!m || !m[1]) continue;
            var name = this.langs[m[1]];
            var o_src = sects[i].substring(m[0].length);
            var o = {
                id: m[1], name: name || "(" + m[1] + ")",
                source: o_src, orig_source: o_src, moved: false
            };
            sect_objs.push(o);
        }
        
        // Entry foot
        var foot = {
            name: "[Đoạn cuối]", id: "_foot", source: "", orig_source: "",
            moved: false
        };
        var last = sect_objs[sect_objs.length - 1];
//            var last_re = /^(?:\s*\{\{(?!(?:-[^\}]+-)\})[^\}]+\}\}\s*|\[\[\s*(?:[A-Za-z\-]+|Thể[ _]+loại)\s*:[^\]]+\]\]\s*)$/mi;
        var last_re =
            /^(?:\[\[\s*)(?!Image|Hình)(?:[A-Za-z\-]+|Thể[ _]+loại)\s*:[^\]]+\]\]\s*$/mi;
        var foot_pos = last.source.search(last_re);
        if (foot_pos >= 0) {
            foot.source = last.source.substring(foot_pos);
            foot.orig_source = foot.source;
            last.source = last.source.substring(0, foot_pos);
            last.orig_source = last.source;
        }
        sect_objs.push(foot);
        
        return sect_objs;
    };
    
    // Create an HTML menu item for the given section object
    this.create_section_item = function (sect) {
        var item = document.createElement("option");
        item.setAttribute("value", sect.id);
        var name = this.capitalize(sect.name);
        item.appendChild(document.createTextNode(name));
        return item;
    };
    
    // Returns true if the section is special (_head or _foot); false otherwise
    this.is_special = function (pos) {
        return this.sect_objs[pos] && this.sect_objs[pos].id &&
            this.sect_objs[pos].id[0] == "_";
    };
    
    // Enable and disable the action buttons as necessary
    this.update_buttons = function () {
        var sects = this.sect_objs;
        var index = sects.indexOf(this.cur_sect);
        
        var is_first = index == 0;
        var is_last = index == this.sect_objs.length - 1;
        
        var this_is_special = this.is_special(index);
        this.remove_btn.disabled = this_is_special;
        this.up_btn.disabled = is_first || this_is_special ||
            this.is_special(index - 1);
        this.down_btn.disabled = is_last || this_is_special ||
            this.is_special(index + 1);
    };
    
    // Attach the given function as an event to the given element
    this.add_event = function (elem, evt, func) {
        if (!elem) return;
        if (elem.addEventListener) elem.addEventListener(evt, func, false);
        else if (elem.attachEvent) elem.attachEvent("on" + evt, func);
    };
    
    // Create an HTML section menu that can added to the page
    this.create_section_list = function () {
        var list = document.createElement("select");
        list.id = "wiktSectList";
        list.setAttribute("size", 15);
        list.setAttribute("style", "width: 100%;");
        this.add_event(list, "change", function () {
            var index = wiktSectionizer.sect_list.selectedIndex;
            var sect_obj = wiktSectionizer.sect_objs[index];
            wiktSectionizer.switch_sections(sect_obj);
        });
        
        for (var i = 0; i < this.sect_objs.length; i++) {
            list.appendChild(this.create_section_item(this.sect_objs[i]));
        }
        
        return list;
    };
    
    // Returns true if the given section's position or contents have changed;
    // false otherwise
    this.has_changed = function (sect) {
        return sect.orig_source != sect.source || sect.moved;
    };
    
    // Update the section list's items to reflect whether the given section's
    // position or contents have changed
    this.update_changed = function (sect) {
        var changed = this.has_changed(sect);
        var opts = this.sect_list.childNodes;
        for (var i = 0; i < opts.length; i++) {
            if (opts[i].value == sect.id) {
                if (changed) this.add_class(opts[i], "wiktSectChanged");
                else this.remove_class(opts[i], "wiktSectChanged");
            }
        }
    };
    
    // Insert a section with the given ISO 639-2 code at the given position
    this.insert = function (code, pos) {
        var sect = {
            id: code, name: this.langs[code] || code,
            source: "", orig_source: "", moved: true
        };
        this.sect_objs.splice(pos, 0, sect);
        var item = this.create_section_item(sect);
        this.sect_list.insertBefore(item, this.sect_list.options[pos]);
        this.switch_sections(sect);
        this.update_changed(sect);
    };
    
    // Create an HTML menu and a button for inserting a new section
    this.create_insert_list = function () {
        
        // Menu
        var list = document.createElement("select");
        list.id = "wiktSectInsertList";
        list.setAttribute("style", "width: 100%;");
        
        // Menu items
        var sects = "";
        for (var i = 0; i < this.sect_objs.length; i++) {
            sects += "|" + this.sect_objs[i].id;
        }
        for (var code in this.langs) {
            var item = this.create_section_item({
                id: code, name: this.langs[code] || code
            });
            list.appendChild(item);
            if (sects.indexOf(code) >= 0) item.disabled = true;
        }
        this.insert_list = list;
        
        // Insert button
        var btn = document.createElement("input");
        btn.id = "wiktSectInsertBtn";
        btn.type = "button";
        btn.value = "+";
        btn.title = "Chèn phần";
        this.add_event(btn, "click", function () {
            var insert_index = wiktSectionizer.insert_list.selectedIndex;
            var item = wiktSectionizer.insert_list.childNodes[insert_index];
            if (item.disabled) return;
            item.disabled = true;
            
            var sect_index = wiktSectionizer.sect_list.selectedIndex;
            if (item) item.disabled = true;
            if (sect_index + 1 < wiktSectionizer.sect_list.length) sect_index++;
            wiktSectionizer.insert(wiktSectionizer.insert_list.value,
                                   sect_index);
        });
        this.insert_btn = btn;
        
        var panel = document.createElement("div");
        panel.id = "wiktSectInsert";
        panel.appendChild(list);
        panel.appendChild(btn);
        return panel;
    };
    
    // Remove the section at the given index from the entry, but first, have the
    // user confirm the removal if the section is not empty
    this.remove = function (pos) {
        var sect = this.sect_objs[pos];
        if ((sect.source != "" || sect.orig_source != "") &&
            !confirm("Bạn có muốn dời phần này và cả nội dung không? " +
                     "Không có thể lấy lại nội dung này.")) {
            return;
        }
        
        this.switch_sections(this.sect_objs[pos + 1]);
        this.sect_objs.splice(pos, 1);
        this.sect_list.remove(pos);
        
        var insert_index = -1;
        var insert_opts = wiktSectionizer.insert_list.childNodes;
        for (var i = 0; i < insert_opts.length; i++) {
            if (insert_opts[i].value == sect.id) insert_index = i;
        }
        if (insert_index < 0) return;
        var item = wiktSectionizer.insert_list.childNodes[insert_index];
        item.disabled = false;
    };
    
    // Move the section at the given position by the given delta
    this.move = function (pos, by) {
        var item = this.sect_list.options[pos];
        if (by > 0) by++;
        var next = this.sect_list.options[pos + by];
        this.sect_list.removeChild(item);
        this.sect_list.insertBefore(item, next);
        if (by > 0) by--;
        var sect = this.sect_objs.splice(pos, 1)[0];
        this.sect_objs.splice(pos + by, 0, sect);
        sect.moved = true;
        this.update_changed(sect);
        this.update_buttons();
    };
    
    // Create an HTML panel for rearranging and removing sections
    this.create_actions = function () {
        // Remove button
        var remove = document.createElement("input");
        remove.id = "wiktSectRemoveBtn";
        remove.type = "button";
        remove.value = "\u2212";
        remove.title = "Dời phần";
        this.add_event(remove, "click", function () {
            wiktSectionizer.remove(wiktSectionizer.sect_list.selectedIndex);
        });
        this.remove_btn = remove;
        
        // Move Up button
        var up = document.createElement("input");
        up.id = "wiktSectUpBtn";
        up.type = "button";
        up.value = "\u25b2";
        up.title = "Mang phần lên";
        this.add_event(up, "click", function () {
            wiktSectionizer.move(wiktSectionizer.sect_list.selectedIndex, -1);
        });
        this.up_btn = up;
        
        // Move Down button
        var down = document.createElement("input");
        down.id = "wiktSectDownBtn";
        down.type = "button";
        down.value = "\u25bc";
        down.title = "Mang phần xuống";
        this.add_event(down, "click", function () {
            wiktSectionizer.move(wiktSectionizer.sect_list.selectedIndex, 1);
        });
        this.down_btn = down;
        
        var panel = document.createElement("div");
        panel.id = "wiktSectActions";
        panel.appendChild(remove);
        panel.appendChild(document.createTextNode("\xa0"));
        panel.appendChild(up);
        panel.appendChild(down);
        return panel;
    };
    
    // Create an HTML sidebar that can be added to the page
    this.create_section_bar = function () {
        // Heading
        var heading = document.createElement("h4");
        heading.appendChild(document.createTextNode("Các phần"));
        
        // Menu
        this.sect_list = this.create_section_list();
        
        // Entire sidebar
        var bar = document.createElement("div");
        bar.id = "wiktSectBar";
        bar.appendChild(heading);
        bar.appendChild(this.create_insert_list());
        bar.appendChild(this.sect_list);
        bar.appendChild(this.create_actions());
        return bar;
    };
    
    // Returns true if the given element has the given class
    this.has_class = function (elem, cls) {
        if (!elem || !elem.className) return false;
        var classes = elem.className.split(/\s+/);
        return classes.indexOf(cls) >= 0;
    };
    
    // Add the given class to the given HTML element
    this.add_class = function (elem, cls) {
        if (!elem) return;
        if (!elem.className) {
            elem.className = cls;
            return;
        }
        
        var classes = elem.className.split(/\s+/);
        if (classes.indexOf(cls) >= 0) return;
        classes.push(cls);
        elem.className = classes.join(" ");
    };
    
    // Remove the given class from the given HTML element
    this.remove_class = function (elem, cls) {
        if (!elem || !elem.className) return;
        
        var classes = elem.className.split(/\s+/);
        var pos = classes.indexOf(cls);
        if (pos < 0) return;
        classes.splice(pos, 1);
        elem.className = classes.join(" ");
    };
    
    // Save the contents of the current section into the section's data object
    // and update the style of the current section's menu item
    this.save_section = function () {
        if (!this.cur_sect) return;
        var val = this.edit_box.value;
        if (val && val[val.length - 1] != "\n") val += "\n";
        this.cur_sect.source = val;
        this.update_changed(this.cur_sect);
    };
    
    // Switch to the given section and update the interface accordingly
    this.switch_sections = function (sect) {
        if (!this.sect_objs || this.sect_objs.length < 1) return;
        if (!sect) {
            if (this.sect_objs[1] && this.sect_objs[1].id != "_foot") {
                sect = this.sect_objs[1];
            }
            else sect = this.sect_objs[0];
        }
        
        // Save contents of current section and update current section
        this.save_section();
        this.cur_sect = sect;
        
        // Change selection in sidebar
        this.sect_list.selectedIndex = this.sect_objs.indexOf(sect);
        this.update_buttons();
        
        // Update text in edit box
//        this.edit_box.value = sect.orig_source;
        this.edit_box.value = "<*** Không có thể lùi sửa sau khi đổi qua phần khác! ***>";
        this.edit_box.value = sect.source;
    };
    
    // Concatenate all the sections together just before navigating away from
    // the page
    this.rejoin_sections = function () {
        this.edit_box.disabled = true;
        this.save_section();
        var src = "";
        for (var i = 0; i < this.sect_objs.length; i++) {
            var sect = this.sect_objs[i];
            if (sect.id[0] != "_") src += "{{-" + sect.id + "-}}\n";
            src += sect.source;
        }
        this.sect_list.disabled = true;
        this.edit_box.value = src;
        this.edit_box.disabled = false;
    };
    
    // Generate an edit summary based on the sections that have been modified
    this.summarize = function () {
        var sum_box = document.getElementById("wpSummary");
        if (!sum_box) return;
        this.save_section();
        
        var codes = [];
        var opts = this.sect_list.childNodes;
        for (var i = 0; i < opts.length; i++) {
            if (this.has_class(opts[i], "wiktSectChanged") &&
                !this.is_special(i)) {
                codes.push(opts[i].value);
            }
        }
        var sum = sum_box.value;
        if (codes.length < 1 || sum) return;
        
        for (var i = 0; i < codes.length; i++) {
            var name = this.capitalize(this.langs[codes[i]]) || codes[i];
            sum += "/* " + name + " */ ";
        }
        sum_box.value = sum;
    };
    
    // Have each action button (Save, Preview, etc.) trigger a rejoining of each
    // section
    this.hook_buttons = function () {
        var btn_ids = ["wpSave", "wpPreview", "wpDiff"];
        var rejoin = function () {
            wiktSectionizer.rejoin_sections();
            wiktSectionizer.summarize();
        };
        for (var i = 0; i < btn_ids.length; i++) {
            var btn = document.getElementById(btn_ids[i]);
            this.add_event(btn, "click", rejoin);
        }
    };
    
    // Create the interface
    this.create = function (sect_id) {
        this.edit_box.disabled = true;
        this.sect_objs = this.get_sections(this.edit_box.value);
        var body = document.getElementById("bodyContent");
        var edit_form = document.getElementById("editform");
        var box_next = this.edit_box.nextSibling;
        edit_form.removeChild(this.edit_box);
        
        var edit_box_div = document.createElement("div");
        edit_box_div.style.marginRight = "13em";
        edit_box_div.appendChild(this.edit_box);
        edit_form.insertBefore(edit_box_div, box_next);
        edit_form.insertBefore(this.create_section_bar(), edit_box_div);
        this.edit_box.style.width = "100%";
        
        this.hook_buttons();
        
        sect_id = sect_id.split(/\|/, 2);
        var sect_code = sect_id[0];
        var sect_num = sect_id[1] || 1;
        var sect;
        for (var i = 0; i < this.sect_objs.length; i++) {
            var o = this.sect_objs[i];
            if (o.id == sect_code && --sect_num < 1) sect = o;
        }
        this.switch_sections(sect);
        
        this.edit_box.disabled = false;
    };
    
    // Returns the ISO 639-2 code of the first section to display
    this.initial_section = function () {
        var param_str = window.location.search;
        if (!param_str || param_str[0] != "?") return "";
        var param_list = param_str.split("&");
        for (var i = 0; i < param_list.length; i++) {
            var param = param_list[i];
            param = param.split("=", 2);
            if (param && param[0] == "sectionid") return param[1];
        }
        return "";
    };
    
    // Initialize the interface
    this.init = function () {
        this.create(this.initial_section());
    };
    this.init();
}

function SectionLinker() {
    var param_str = window.location.search;
    if (param_str) {
        if (param_str[0] != "?") return;
        var param_list = param_str.split("&");
        for (var i = 0; i < param_list.length; i++) {
            var param = param_list[i];
            param = param.split("=", 2);
            if (!param) continue;
            if (param[0] == "diff" || param[0] == "oldid") return;
        }
    }
    
    this.get_number = function (heading) {
        var prev = heading.previousSibling;
        while (prev && prev.nodeType != 1) prev = prev.previousSibling;
        if (!prev || prev.tagName != "a") return 0;
        var num = prev.id.match(/.+_(\d+)/);
        if (!num || !num[1] || !num[1] == "1") return 0;
        return parseInt(num[1]);
    }
    
    this.create_link = function (heading) {
        if (heading.childNodes.length < 1) return;
        
        var title = heading.title;
        var title_m = title.match(/Mã ISO: (.+)/);
        var code = title_m[1] || title;
        
        var a = document.createElement("a");
        a.href = wgScript + "?title=" + wgPageName +
            "&action=edit&sectionid=" + code;
        var num = this.get_number(heading);
        if (num) a += "|" + num;
        a.title = "Sửa đổi phần “" + (heading.textContent || heading.text) +
            "”";
        a.appendChild(document.createTextNode("sửa"));
        
        var span = document.createElement("span");
        span.className = "editsection sectionized";
        span.appendChild(document.createTextNode("["));
        span.appendChild(a);
        span.appendChild(document.createTextNode("]"));
        
        var space = document.createTextNode(" ");
        heading.insertBefore(space, heading.childNodes[0]);
        heading.insertBefore(span, space);
    }
    
    this.init = function () {
        var headings = document.getElementsByTagName("h2");
        for (var i = 0; i < headings.length; i++) {
            if (headings[i].className &&
                headings[i].className.indexOf("lang-heading") >= 0) {
                this.create_link(headings[i]);
            }
        }
    };
    this.init();
}

$(function () {
    if (wgNamespaceNumber != 0) return;
    if (wgAction == "submit" || wgAction == "edit") {
        wiktSectionizer = new Sectionizer();
    }
    else if (wgAction == "view" || wgAction == "purge") {
        var wiktSectionLinker = new SectionLinker();
    }
});