MediaWiki:CatNav.js

/* allow a category navigation based on a list of categories (instead of the usual single category navigation) $(function {

// check if the page is Special:CatNav if (mw.config.get("wgPageName") == "Special:CatNav") {

/* ================================== *\       	# core objects \* ================================== */

//var catnav = { var catnav = { fn: {}, settings: { rows: 3, // default number of rows itemsInNavigator: Math.floor(($("#mw-content-text").width - 100) / 154) // number of columns determined by the width of the user's screen }       };        catnav.data = { details: {}, // details about pages current: [], selectedPage: null }

/* ================================== *\       	# functions \* ================================== */

/* functions for getting info about categories */

// get members of a given category catnav.fn.catMembers = function(cat, ns, arr, cont, cb) { /*           catnav.fn.getMembersOfCat("Foo", 0, [], "", function(data) {            	console.log(data);            }); */           var req = new XMLHttpRequest; req.open("get", "/api.php?action=query&format=json&list=categorymembers&cmtitle=Category:" + encodeURIComponent(cat) + "&cmnamespace=" + ns + "&cmcontinue=" + encodeURIComponent(cont) + "&cmlimit=max&cb=" + new Date.getTime); req.onload = function { var data = JSON.parse(this.responseText); if (data.hasOwnProperty("query")) { var a = data.query.categorymembers; for (var i in a) { arr.push(data.query.categorymembers[i].title); }                   if (typeof data["query-continue"] === "object") { return catnav.fn.catMembers(cat, ns, arr, data["query-continue"].categorymembers.cmcontinue, cb); } else { cb(arr); }               } else { catnav.fn.error(1, "Đã có lỗi xảy ra khi tìm trang trong thể loại " + cat + ". Thông tin lỗi như sau: \nError code: \nError info: " + data.error.info + " Hãy đảm bảo là bạn đã nhập đúng thông tin. Không để dòng trống."); }           }            req.send; }

// get members of multiple categories catnav.fn.catMembersMulti = function(catstr, ns, cb) { var cats = catstr.split("|"), completed = 0, allcats = {};

function query { if (completed == cats.length) { // all requests have been made cb(allcats); } else { catnav.fn.catMembers(cats[completed], ns, [], "", function(data) {                       allcats[cats[completed]] = data;                        completed++;                        query;                    }); }           }            query; }

// search for members that exist in all specified categories catnav.fn.joinMembers = function(data, isCommonMembers, fn) { var smallestCat, finalList = []; if (isCommonMembers === true) { // mode for the "find-in-categories" list: only list a page if it has all the listed categories smallestCat = data[catnav.fn.getSmallestCat(data)]; // start from smallest category - should take less time for (var i in smallestCat) { var itemLeSmall = smallestCat[i], // current item for check isSharedCommon = true; // if 'isCommonMembers == true', only list pages that are listed in all categories // otherwise, list all pages anyway for (var cat in data) { if (cat != smallestCat && data[cat].indexOf(itemLeSmall) == -1) { // the page 'itemLeSmall' is not categorized in one of these cats isSharedCommon = false; break; }                   }                    if (isSharedCommon) { finalList.push(itemLeSmall); }               }            } else { // mode for the "unwanted categories" list: list any categorized page that is categorized at least once for (var cat in data) { for (var i in data[cat]) { var currCat = data[cat][i]; if (finalList.indexOf(finalList) == -1) { // although we categorize all pages, we still don't want a category to repeat- it's time consuming and pointless finalList.push(currCat); }                   }                }            }            fn(finalList); }

// find the category with the fewest members catnav.fn.getSmallestCat = function(data) { var small = { cat: null, length: Infinity }           for (var cat in data) { if (data[cat].length < small.length) { small.cat = cat; small.length = data[cat].length; }           }            return small.cat; // return name of property with the smallest number of items }

// divide to pages - take a long list of titles, and split them to groups, each with no more than 'n' titles catnav.fn.divideToPages = function(titles) { var result = []; while (titles.length > 0) { result.push(titles.splice(0, catnav.settings.itemsInNavigator * catnav.fn.getNumberOfRows)); }           return result; }

// get number of rows catnav.fn.getNumberOfRows = function { var rows = catnav.settings.rows, specified = $("#catnav-rows").val; if ($.isNumeric(specified) && specified > 0 && specified == Math.round(specified)) { // specified number of rows is a valid positive integer rows = specified; }           return rows; }

/* functions for getting info about pages */

// implement new members catnav.fn.implementNewTitles = function(titles) { // 'titles' is an array of page titles var asPages = catnav.fn.divideToPages(titles); // divide the 'titles' array to a list of smaller groups of titles if (asPages.length > 0) { catnav.data.current = asPages; catnav.fn.error(0, null); // hide error catnav.fn.updatePagesListNav; catnav.fn.gotoPage(0); } else { catnav.fn.error(1, "Không tìm thấy trang với các thể loại được liệt kê!"); // show error }       }

// update pages' numbers catnav.fn.updatePagesListNav = function { /*var container = $(' '), a; for (var i = 0; i < catnav.data.current.length; i++) { a = $('').html("(" + (Number(i) + 1) + ")"); $(container).append(a); }           $("#catnav-pagenav").html($(container).html).find("a").click(function {            	catnav.fn.gotoPage($(this).attr("data-catnav-page"));            });*/ var a,               pagenav = $("#catnav-pagenav"); $(pagenav).html(""); for (var i = 0; i < catnav.data.current.length; i++) { a = $('').html("(" + (Number(i) + 1) + ")"); $(pagenav).append(a); $(a).click(function {                   catnav.fn.gotoPage(Number($(this).attr("data-catnav-page")));                }); }       }

// go to page 'n + 1' catnav.fn.gotoPage = function(n) { catnav.data.selectedPage = n;           $("#catnav-pagenav .catnav-pagenav-selected").removeClass("catnav-pagenav-selected"); $("#catnav-pagenav a").eq(n).addClass("catnav-pagenav-selected"); catnav.fn.queryPages(catnav.data.current[n]); }

// get info about pages (url, thumb, etc.) catnav.fn.queryPages = function(titles) { var req = new XMLHttpRequest, missingTitles = []; for (var i in titles) { if (!catnav.data.details.hasOwnProperty(titles[i])) { missingTitles.push(titles[i]); }           }            req.open("get", "/api/v1/Articles/Details?&abstract=0&width=140&height=140&titles=" + encodeURIComponent(missingTitles.join(","))); req.onload = function { catnav.fn.parsePagesQuery(JSON.parse(this.responseText)); catnav.fn.updateMarkup(titles); }           if (missingTitles.length > 0) { req.send; } else { // info about those pages has already loaded catnav.fn.updateMarkup(titles); }       }

// process info about pages from json catnav.fn.parsePagesQuery = function(data) { for (var pageid in data.items) { var a = data.items[pageid], title = decodeURIComponent(a.url.substr(6)).replace(/_/g, " "); // a.title doesn't provide the namespace - easiest method is to do this catnav.data.details[title] = { title: title, url: a.url, img: a.thumbnail }           }        }

// update markup catnav.fn.updateMarkup = function(titles) { var container = $(' '); for (var i in titles) { var a = catnav.data.details[titles[i]], item = $(']/g, function(m) { return "&#" + m.charCodeAt(0) + ";"; }) + '">  ');               $(item).find("span").text(a.title);                $(container).append(item);            }            $("#catnav-container").html($(container).html);            window.q = container;        }

// error message catnav.fn.error = function(bool, msg) { // if 'bool' show message, otherwise hide // 'msg' is the new html content $("#catnav-noneerror").html(msg)[bool ? "show" : "hide"] }           /* ================================== *\            	# css and markup \* ================================== */

/* css */ mw.util.addCSS(           '#catnav {\n' +            '\twidth: 100%;\n' +            '\tmargin: 0;\n' +            '\tpadding: 0;\n' +            '}\n' +            '#catnav-container {\n' +            '\tdisplay: flex;\n' +            '\tflex-wrap: wrap;\n' +            '\tmargin: 10px auto 20px auto;\n' +            '\tmargin: 10px 50px 20px 50px;\n' +            '}\n' +            '#catnav-container .catnav-item {\n' +            '\tdisplay: inline-block;\n' +            '\twidth: 140px;\n' +            '\theight: 140px;\n' +            '\tmargin: 3px;\n' +            '\tpadding: 2px;\n' +            '\tposition: relative;\n' +            '\toverflow: hidden;\n' +            '\tborder: 2px solid navy;\n' +            '\tborder-radius: 10px;\n' +            '}\n' +            '#catnav-container .catnav-item .catnav-item-label {\n' +            '\tmax-width: 90px;\n' +            '\theight: 18px;\n' + '\tpadding: 0 4px;\n' + '\tposition: absolute;\n' + '\tbottom: 0;\n' + '\tright: 0;\n' + '\toverflow: hidden;\n' + '\tbackground: #006cb0;\n' + '\tborder-top-left-radius: 7px;\n' + '\tcolor: #fff;\n' + '\tfont-size: 14px;\n' + '\tline-height: 18px;\n' + '}\n' + '#catnav-container img {\n' + '\tborder-radius: 7px;\n' + '}\n' + '#catnav-container .catnav-item-noimage {\n' + '\tborder-color: #c00;\n' + '}\n' + '#catnav-container .catnav-item-noimage .catnav-item-label {\n' + '\tbackground: #c00;\n' + '}\n' + '#catnav-pagenav {\n' + '\tpadding: 3px 7px;\n' + '\ttext-align: center;\n' + '\tfont-size: 18px;\n' + '\tline-height: 18px;\n' + '}\n' + '#catnav-pagenav a:not(.catnav-pagenav-selected) {\n' + '\tcolor: #a6d1ec;\n' + '\ttext-shadow: 1px 1px 0 navy;\n' + '}\n' + '#catnav-pagenav a ~ a {\n' + '\tmargin-left: 10px;\n' + '}\n' + '#catnav-pagenav a.catnav-pagenav-selected {\n' + '\tcolor: black;\n' + '\tcursor: pointer;\n' + '\tfont-weight: bold;\n' + '}\n' + '#catnav textarea {\n' + '\twidth: 100%;\n' + '\twidth: calc(100% - 6px);\n' + '\theight: 60px;\n' + '\tresize: none;\n' + '}\n' + '#catnav table {\n' + '\twidth: 100%;\n' + '\tmargin: 0;\n' + '\tpadding: 0;\n' + '}\n' + '#catnav #catnav-rows {\n' + '\twidth: 30px;\n' + '}\n' + '#catnav th {\n' + '\twidth: 50%;\n' + '}\n' + '#catnav label {\n' + '\tfont-size: smaller;\n' + '}\n' + '#catnav #catnav-noneerror {\n' + '\tcolor: #c00;\n' + '\tfont-weight: bold;\n' + '}'       );

/* markup */ // interface markup $("#mw-content-text").html(           ' \n' +            '\t \n' +            '\t\tTrang này cho phép bạn lọc bỏ và chỉ tìm những trang từ các thể loại bạn muốn. Nhập thể loại bạn muốn lọc vào ô tìm kiếm ở phía dưới, rồi nhấn nút Tìm kiếm ở phía dưới để lấy danh sách các trang:\n' +            '\t \n' +            '\t \n' +            '\t \n' +            '\t\tSố dòng liệt kê: \n' +            '\t \n' +            '\t\n' +            '\tChỉ tìm trong trang miền chính \n' +            '\t \n' +            '\t\tKhông tìm thấy trang nào!\n' +            '\t \n' +            '\t \n' +            '\t \n' +            '\t \n' +            '\t \n' +            '\t \n' +            '\t \n' +            ' '        ); // update titles $("head title, #WikiaPageHeader h1").html("CatNav");

/* ================================== *\       	# triggers \* ================================== */       $("#catnav-go").click(function {            var incCats = $("#catnav #catnav-textarea-include").val.replace(/\n/g, "|"), // included categories                disCats = $("#catnav #catnav-textarea-disclude").val.replace(/\n/g, "|"), // discluded categories                nsStr = $("#catnav-ns")[0].checked ? "0" : "";            // get included categories (object: key => categoryname, val => array of listed pages in that category)            catnav.fn.catMembersMulti(incCats, nsStr, function(incData) { // sort the pages into a single array catnav.fn.joinMembers(incData, true, function(incTitles) {                   if (disCats.length == 0) {                        // no unwated categories requested - update immediately                        catnav.fn.implementNewTitles(incTitles);                    } else {                        // unwated categories requiested - get their categorized pages                        catnav.fn.catMembersMulti(disCats, nsStr, function(disData) { // sort the pages into a single array catnav.fn.joinMembers(disData, false, function(disTitles) {                               for (var i in disTitles) {                                    if (incTitles.indexOf(disTitles[i]) > -1) {                                        // unwanted page detected - remove from 'incTitles'                                        incTitles.splice(incTitles.indexOf(disTitles[i]), 1)                                    }                                }                                catnav.fn.implementNewTitles(incTitles);                            }); });                   }                });            });        });    } });