MediaWiki:Gadget-OpenStreetMap.js

/**
 * Đưa khung bản đồ OpenStreetMap tiếng Việt vào các bài địa danh có thẻ
 * {{coord|display=title}}.
 * 
 * Xem [[Trợ giúp:OpenStreetMap]] và <http://wiki.osm.org/wiki/Vi:Main_Page>.
 */
mw.loader.using("mediawiki.util", function () {

var wpMapDefaults = {
    //* {string} Phiên bản Leaflet
    leafletVersion: "1.7.1",
    //* {number} Chiều cao bản đồ (điểm ảnh)
    height: 350,
    //* {string} Địa chỉ hình tượng ([[Tập tin:WMA button2b.png]])
    iconUrl: "//upload.wikimedia.org/wikipedia/commons/thumb/5/55/WMA_button2b.png/17px-WMA_button2b.png",
    
    /**
     * {string} Định dạng của các địa chỉ hình ảnh bản đồ. Ngoài các placeholder
     * chuẩn của Leaflet, kịch bản này hỗ trợ các placeholder sau:
     * 
     * <dl>
     * <dt>{layer}</dt><dd>tên của lớp bản đồ</dd>
     * </dl>
     * 
     * @see https://leafletjs.com/reference-1.7.1.html#tilelayer
     */
    layerUrlFormat: location.protocol + "//maps.wikimedia.org/{layer}/{z}/{x}/{y}{r}.png?lang={lang}",
    
    // Địa phương hóa
    
    //* {string} Mã ngôn ngữ của các địa danh trên bản đồ
    language: mw.config.get("wgContentLanguage"),
    //* {string} Tooltip của hình tượng hiện/ẩn bản đổ
    iconTooltip: "Xem vị trí này trên bản đồ tương tác",
    //* {string} Phím tắt (không bao gồm Ctrl v.v.) hiện/ẩn bản đồ
    iconAccessKey: undefined,   // thí dụ "y"
    
    zoomIn: "Phóng to",
    zoomOut: "Thu nhỏ",
    
    layerBase: "Bản đồ",
    layerArticles: "Bài viết",
    
    /**
     * {string} Định dạng của lời ghi công những người đóng góp vào
     * OpenStreetMap. Chuỗi có thể có các placeholder sau:
     * 
     * <dl>
     * <dt>{osm}</dt>
     * <dd>liên kết đến #creditOsmUrl có văn bản #creditOsm</dd>
     * <dt>{osmLicense}</dt>
     * <dd>liên kết đến #creditOsmUrlLicenseUrl có văn bản #creditOsmLicense</dd>
     * <dt>{help}</dt>
     * <dd>liên kết đến #helpArticle có văn bản #help</dd>
     * </dl>
     */
    credit: "© những nguời đóng góp vào {osm} ({osmLicense}, {help})",
    
    creditOsm: "OpenStreetMap",
    creditOsmUrl: "//www.openstreetmap.org/",
    creditOsmLicense: "ODbL + CC BY-SA",
    creditOsmLicenseUrl: "//www.openstreetmap.org/copyright/vi",
    
    help: "Trợ giúp",
    helpArticle: "Trợ giúp:OpenStreetMap",
	
	// WIWOSM
	
	layerShapes: "Hình dạng",
    creditShapes: "WIWOSM",
	creditShapesUrl: "//wiki.openstreetmap.org/wiki/WIWOSM?uselang=vi",
    
    // Bản đồ biển xa lộ Mỹ
    
    layerShields: "Biển xa lộ",
    shieldsUrlFormat: "http://{s}.tile.openstreetmap.us/osmus_shields/{z}/{x}/{y}.png",
    creditShields: "OSMUS",
    creditShieldsUrl: "http://bl.ocks.org/ToeBee/raw/6119134/",
    
    tunnelUrl: location.protocol + "//tilde.toolforge.org/poitunnel.html",
};
wpMapConfig = $.extend(window.wpMapConfig, wpMapDefaults);

/**
 * Tải các kịch bản và bảng kiểu của thư viện bản đồ Leaflet. Hàm này chạy một
 * cách bất đồng bộ.
 * 
 * @param done  {function}  Hàm sẽ được gọi sau khi kịch bản của thư viện được
 *                          tải xong.
 */
function loadLeaflet(done) {
//    var root = "//osm.toolforge.org/libs/leaflet/" + wpMapConfig.leafletVersion + "/dist/";
    var root = "//tilde.toolforge.org/leaflet-" + wpMapConfig.leafletVersion + "/";
    mw.loader.load(root + "leaflet.css", "text/css");
    $.getScript(root + "leaflet.js").done(done);
}

/**
 * Cho ra định dạng của các địa chỉ hình bản đồ trong lớp được cho vào.
 * 
 * @param name  {string}    Tên của lớp bản đồ.
 * @param lang	{string}	Ngôn ngữ của các nhãn trên lớp bản đồ.
 * @returns {string}    Định dạng của địa chỉ hình bản đồ.
 */
function layerUrl(name, lang) {
    return wpMapConfig.layerUrlFormat.replace("{layer}", name).replace("{lang}", lang);
}

/**
 * Phân tích mảng chứa các chuỗi tham số thành một đối tượng tham số.
 * 
 * @param components    {array}     Các thành phần của tọa độ.
 * @param width         {number}    Chiều rộng hiện tại của bản đồ.
 * @returns {object}    Từ điển có tọa độ và các tham số.
 */
function parsedParameters(components, width) {
    //* Phân tích một thành phần của tọa độ.
    var parseCoord = function (pos, neg) {
        var i, coord = 0;
        for (i = 0; i < components.length; i++) {
            var component = components[i].toUpperCase();
            if (!component || component == pos || component == neg) {
                if (component == neg) coord *= -1;
                break;
            }
            
            coord += parseFloat(component, 10) / Math.pow(60, i);
        }
        components.splice(0, i + 1);
        return coord;
    }, i;
    
    // Phân tích vĩ độ và kinh độ.
    var lat = parseCoord("N", "S");
    var lng = parseCoord("E", "W");
    
    // Đưa các tham số khác vào từ điển.
    var params = {
        center: new L.LatLng(lat, lng),
    };
    for (i = 0; i < components.length; i++) {
        var param = components[i].split(":");
        params[param[0]] = param[1];
    }
    
    // Phân tích các tham số tỷ lệ theo [[:en:Template:Coord#type:T]] và
    // [[OpenStreetMap:Zoom levels]].
    var zoomsByType = {
        country: 6, satellite: 6,
        adm1st: 9,
        adm2nd: 11,
        adm3rd: 12, mountain: 12, isle: 12, waterbody: 12, river: 12,
        city: 12,   // city(dân số) là 11–14, tùy dân số
        forest: 13, glacier: 13, event: 13,
        airport: 14,
        edu: 16, pass: 16, railwaystation: 16, landmark: 16,
    };
    var type = params.type ? (params.type.match(/(\w+)(?:\(([\d,]+)\))?/) || []) : [];
    params.zoom = zoomsByType[type[1]] || 11;
    if (type[1] == "city" && type[2]) {
        var pop = params.pop = parseInt(type[2].replace(",", ""), 10);
        if (pop >= 1e7) params.zoom = 11;
        else if (pop >= 1e5) params.zoom = 12;
        else if (pop >= 1e3) params.zoom = 13;
        else params.zoom = 14;
    }
    
    // 1:...
    var scalesByZoom = [5e8, 2.5e8, 1.5e8, 7e7, 3.5e7, 1.5e7, 1e7, 4e6, 2e6, 1e6,
                        1e5, 2.5e5, 1.5e5, 7e4, 3.5e4, 1.5e4, 8e3, 4e3, 2e3];
    if (params.scale) {
        params.scale = parseFloat(params.scale);
        for (i = scalesByZoom.length - 1; i >= 0; i--) {
            if (params.scale <= scalesByZoom[i]) {
                params.zoom = i;
                break;
            }
        }
    }
    
    // Mét / điểm ảnh
    var mppByZoom = [156412, 78206, 39103, 19551, 9776, 4888, 2444, 1222,
                     610.984, 305.492, 152.746, 76.373, 38.187, 19.093, 9.547,
                     4.773, 2.387, 1.193, 0.596];
    if (params.dim) {
        var dim = parseFloat(params.dim, 10);
        var unit = params.dim.indexOf("km") < 0 ? 1 : 1e3;
        dim *= unit;
        for (i = mppByZoom.length - 1; i >= 0; i--) {
            if (dim <= mppByZoom[i] * wpMapConfig.height) {
                params.zoom = i;
                break;
            }
        }
        if (width) {
            for (i = mppByZoom.length - 1; i >= 0; i--) {
                if (dim <= mppByZoom[i] * width) {
                    params.zoom = Math.round((params.zoom + i) / 2);
                    break;
                }
            }
        }
    }
    
    // [[en:ISO 3166-1 alpha-2]]
    if (params.region) {
    	params.country = params.region.match(/^\w\w(?=-|$)/);
    	if (params.country) params.country = params.country[0];
    }
    
    return params;
}
//window.parsedParameters = parsedParameters;                                     // debug

/**
 * Chuyển đổi các điểm EPSG:3857 (Mercator hình cầu) thành tọa độ.
 */
function unprojectLayerPoints(points) {
	if (typeof(points[0]) !== "number") {
		for (var i = 0; i < points.length; i++) {
			unprojectLayerPoints(points[i]);
		}
		return;
	}
	
	var pt = new L.Point(points[0], points[1]);
	var coord = L.Projection.SphericalMercator.unproject(pt);
	points[0] = coord.lng;
	points[1] = coord.lat;
}

/**
 * Tạo ra đối tượng bản đồ và thiết lập nội dung.
 * 
 * @param id        {string}    ID của phần tử sẽ trở thành bản đồ.
 * @param params    {object}    Từ điển các tham số của bản đồ.
 * @returns {object}    Bản đồ Leaflet.
 */
function createMap(id, params) {
	// Tải các lớp OpenStreetMap.
    var osmAttrib = wpMapConfig.credit.replace("{osm}", "<a id='openstreetmap-credit' href='" +
                                               wpMapConfig.creditOsmUrl + "'>" + wpMapConfig.creditOsm + "</a>")
                                      .replace("{osmLicense}", "<a href='" + wpMapConfig.creditOsmLicenseUrl +
                                               "'>" + wpMapConfig.creditOsmLicense + "</a>")
                                      .replace("{help}", "<a href='" + mw.util.getUrl(wpMapConfig.helpArticle) +
                                               "'>" + wpMapConfig.help + "</a>");
    var base = new L.TileLayer(layerUrl("osm-intl", wpMapConfig.language), {
        attribution: osmAttrib,
    });
	var shapes = new L.GeoJSON(undefined, {
		style: function (feature) {
			return {
				opacity: 0.5,
				fillColor: "white",
				clickable: false,
			};
		},
	});
    var articles = new L.LayerGroup();
    
    // <a href='/wiki/Wikipedia:Quyền_tác_giả'>Wikipedia</a>
    
    // Tạo bản đồ.
    var map = new L.Map(id, {
        center: params.center,
        zoom: params.zoom,
        layers: [base, shapes, articles],
    });
    
    // Việt hóa các điều khiển.
    map.attributionControl.setPrefix("<a href='https://leafletjs.com/'>Leaflet</a>");
    $(".leaflet-control-zoom-in").attr("title", wpMapConfig.zoomIn);
    $(".leaflet-control-zoom-out").attr("title", wpMapConfig.zoomOut);
    
    // Tạo điều khiển để bật/tắt các lớp.
    var bases = {};
    bases[wpMapConfig.layerBase] = base;
    var overlays = {};
    
    // Thiết lập lớp biển xa lộ Mỹ.
    if (["US", "CA", "MX"].indexOf(params.country) >= 0) {
        var shieldsAttrib = ("<a id='shields-credit' href='" +
                             wpMapConfig.creditShieldsUrl + "'>" +
                             wpMapConfig.creditShields + "</a>");
        var shields = new L.TileLayer(wpMapConfig.shieldsUrlFormat, {
            attribution: shieldsAttrib,
        });
        overlays[wpMapConfig.layerShields] = shields;
    }
    
    overlays[wpMapConfig.layerShapes] = shapes;
    overlays[wpMapConfig.layerArticles] = articles;
    map.addControl(new L.Control.Layers(bases, overlays));
    
    // Liên kết đến vị trí hiện tại trên trang chủ OpenStreetMap.
    var updateOsmLink = function (link) {
        if (!link.length) return;
        var center = map.getCenter();
        var base = link.attr("href").match(/^[^?]+/);
        link.attr("href", base + "?lat=" + center.lat + "&lon=" + center.lng +
                  "&zoom=" + map.getZoom());
    };
    var updateOsmLinks = function () {
        updateOsmLink($("#openstreetmap-credit"));
        updateOsmLink($("#shields-credit"));
    };
    updateOsmLinks();
    map.on("moveend", updateOsmLinks);
    
    // Thiết lập lớp ghim và lớp hình dạng.
    var tunnel = $("#poi-tunnel");
    if (!tunnel.length) {
        tunnel = $("<iframe id='poi-tunnel' src='" + wpMapConfig.tunnelUrl + "'></iframe>");
        $(document.body).after(tunnel);
        tunnel.hide();
    }
	var receiveArticles = function (pois) {
        if (pois.length === undefined) return;
        articles.clearLayers();
        for (var i = 0; i < pois.length; i++) {
            if (!pois[i].visible) continue;
            var marker = new L.CircleMarker(new L.LatLng(pois[i].lat, pois[i].lon), {
                color: "red",
                opacity: 0.4,
                fillOpacity: 0.1,
                radius: 5,
            });
            articles.addLayer(marker);
            var content = mw.html.element("a", {
                href: mw.util.getUrl(pois[i].name),
                title: pois[i].name,
            }, pois[i].name);
            marker.bindPopup(content);
        }
	};
	var articleTitle = mw.config.get("wgTitle");
	var receiveShapes = function (json) {
		shapes.clearLayers();
		
		if (json.coordinates) unprojectLayerPoints(json.coordinates);
		else if (json.geometries) {
			for (var i = 0; i < json.geometries.length; i++) {
				unprojectLayerPoints(json.geometries[i].coordinates);
			}
		}
		
		if (shapes.addData(json) && json.type !== "Point") {
			var zoom = map.getBoundsZoom(shapes.getBounds());
			if (zoom > 1) map.fitBounds(shapes.getBounds());
		}
	};
    addEventListener("message", function (evt) {
        if (evt.origin !== location.protocol + "//tilde.toolforge.org") return;
		
		var data = evt.data;
		if (data.length !== undefined) receiveArticles(data);	// poitunnel cũ
		else if (data.subject === "pois") receiveArticles(data.data);
		else if (data.subject === "shape") receiveShapes(data.data);
    }, false);
    var requestShapes = function () {
        tunnel[0].contentWindow.postMessage({
            subject: "shape",
			lang: wpMapConfig.language,
			article: articleTitle,
        }, wpMapConfig.tunnelUrl);
    };
    tunnel.load(requestShapes);
    var requestArticles = function () {
        tunnel[0].contentWindow.postMessage({
            subject: "pois",
			lang: wpMapConfig.language,
			bbox: map.getBounds().toBBoxString(),
        }, wpMapConfig.tunnelUrl);
    };
    tunnel.load(requestArticles);
    map.on("moveend", requestArticles);
    map.on("layeradd", function (evt) {
        if (evt.layer !== articles) return;
		requestArticles();
		map.on("moveend", requestArticles);
    });
    map.on("layerremove", function (evt) {
        if (evt.layer === articles) map.off("moveend", requestArticles);
    });
    
    // Đặt ghim vào vị trí của chủ đề bài.
    var marker = new L.Marker(params.center);
    map.addLayer(marker);
    var content = mw.html.element("strong", {}, articleTitle);
    marker.bindPopup(content);
    
    return map;
}

/**
 * Returns a copy of the given image URL with any embedded pixel width scaled by
 * the given factor.
 */
function scaledImageUrl(origUrl, scale) {
	return origUrl.replace(/\d+(?=px-)/, function (m) {
		return Math.ceil(parseInt(m) * scale);
	});
}

/**
 * Cài đặt khung bản đồ và hình tượng hiện/ẩn nó và gỡ bỏ WikiMiniAtlas.
 */
function installMap() {
    // Tìm tọa độ trong liên kết của GeoHack.
    var link = $("#coordinates a[href*='geohack']:not([href*='_globe:'])");
    var params = link && link.attr("href");
    params = params && params.match(/[?&]params=(.+?)(?:&|$)/);
    params = params && params[1].split("_");
    if (!params) return;
    
    // Tạo khung đựng bản đồ.
    $("#contentSub").append("<div id='openstreetmap-container' style='clear: both; display: none;'><div id='openstreetmap' style='height: " +
                            wpMapConfig.height + "px; width: 100%;'></div></div>");
    
    // Vô hiệu WikiMiniAtlas và xóa hình tượng của nó nếu bản đồ đã tải.
    $("#coordinates img").off("click").remove();
    wma_settings = {enabled: false};
    
    // Tạo hình tượng địa cầu.
    var iconSrcset = scaledImageUrl(wpMapConfig.iconUrl, 1.5) + " 1.5x, " +
    	scaledImageUrl(wpMapConfig.iconUrl, 2) + " 2x";
    var iconSize = wpMapConfig.iconUrl.match(/(\d+)px-/);
    var icon = $("<a id='openstreetmap-icon' href='#'><img src='" +
                wpMapConfig.iconUrl + "' srcset='" + iconSrcset + "' height='" +
                iconSize[0] + "' width='" + iconSize[0] + "' /></a>");
    
    // Đặt tooltip và phím tắt nếu có.
    var tooltip = wpMapConfig.iconTooltip;
    if (wpMapConfig.iconAccessKey) {
        tooltip += " [" + wpMapConfig.iconAccessKey + "]";
        icon.attr("accesskey", wpMapConfig.iconAccessKey);
    }
    icon.attr("title", tooltip);
    if (wpMapConfig.iconAccessKey) mw.util.updateTooltipAccessKeys(icon);
    
    // Khi nhấn chuột vào hình tượng, mở/đóng bản đồ.
    icon.click(function (evt) {
        // Nếu bản đồ đã được thiết lập, chỉ việc hiện/ẩn.
        var container = $("#openstreetmap-container");
        if (!container || container.hasClass("openstreetmap-loaded")) {
            container.slideToggle("slow", function () {
                this.map.setView(this.mapParams.center, this.mapParams.zoom);
            });
            return;
        }
        container.addClass("openstreetmap-loaded");
        
        loadLeaflet(function () {
            // Thiết lập và hiển thị bản đồ.
            container.slideDown("slow", function () {
                this.mapParams = parsedParameters(params, $(this).width());
//                console.log(this.mapParams);                                    // debug
                this.map = createMap("openstreetmap", this.mapParams);
            });
        });
    });
    
    // Chèn hình tượng đằng trước liên kết tọa độ.
    link.before(icon);
    link.before(" ");
    setTimeout(icon.hidpi, 0);
};

$(installMap);

});
Chúng tôi bán
Bài viết liên quan
Nhân vật Epsilon: the Precision - The Eminence In Shadow
Nhân vật Epsilon: the Precision - The Eminence In Shadow
Epsilon (イプシロン, Ipushiron?) (Έψιλον) là thành viên thứ năm của Shadow Garden, là một trong "Seven Shadows" ban đầu.
Review Ayato - Genshin Impact
Review Ayato - Genshin Impact
Về lối chơi, khả năng cấp thủy của Ayato theo mình đánh giá là khá yếu so với những nhân vật cấp thủy hiện tại về độ dày và liên tục của nguyên tố
Cốt lõi của
Cốt lõi của "kiệt sức vì công việc" nằm ở "mức độ hài lòng với bản thân"?
Nếu bạn cảm thấy suy kiệt, bắt đầu thấy ghét công việc và cho rằng năng lực chuyên môn của mình giảm sút, bạn đang có dấu hiệu kiệt sức vì công việc.
Vài trò của Hajime Kashimo sau Tử diệt hồi du
Vài trò của Hajime Kashimo sau Tử diệt hồi du
Hajime Kashimo là một chú thuật sư từ 400 năm trước, với sức mạnh phi thường của mình, ông cảm thấy nhàm chán