|
|
| Line 1: |
Line 1: |
| local p = {} | | local p = {} |
|
| |
| -- Change to "" upon deployment.
| |
| local moduleSuffix = ""
| |
|
| |
| local parserModuleName = "Module:Road data/parser" .. moduleSuffix
| |
| local statenameModuleName = "Module:Jct/statename" .. moduleSuffix -- TODO transition
| |
|
| |
|
| local concat = table.concat | | local concat = table.concat |
| Line 12: |
Line 6: |
| local trim = mw.text.trim | | local trim = mw.text.trim |
|
| |
|
| local parserModule = require(parserModuleName) | | local parserModule = require("Module:Road data/parser") |
| local parser = parserModule.parser | | local parser = parserModule.parser |
| local util = require("Module:Road data/util") | | local util = require("Module:Road data/util") |
| local sizeModule = require("Module:Road data/size").size({style = "jct"}) | | local sizeModuleName = 'Module:Road data/size/sandbox' -- REMOVE SANDBOX |
|
| |
|
| -- Shields | | -- Shields |
| local defaultShieldSize = 24 | | local function addContextBanner(route, name, suffix, bannerSpec, size) |
| | local bannerModule = 'Module:Road data/banners/' .. string.upper(route.country) |
| | local shieldField = name .. 'shield' |
| | local shield = parser(route, shieldField) |
|
| |
|
| local function addContextBanner(route, name, suffix, bannerSpec)
| |
| local bannerModule = 'Module:Road data/banners/' .. string.upper(route.country)
| |
| local shieldfield = name .. 'shield'
| |
| local shield = parser(route, shieldfield)
| |
| if shield == nil then | | if shield == nil then |
| -- This route type does not define shield. | | -- This route type does not define shield. |
| Line 32: |
Line 25: |
| suffix = parser(route, 'shield', 'suffix', bannerModule) | | suffix = parser(route, 'shield', 'suffix', bannerModule) |
| end | | end |
| | |
| if suffix and suffix ~= '' then | | if suffix and suffix ~= '' then |
| shield = shield .. " " .. suffix | | shield = shield .. " " .. suffix |
| end | | end |
| | |
| shield = shield .. ".svg" | | shield = shield .. ".svg" |
| end | | end |
| end | | end |
| | |
| if shield and shield ~= '' then | | if shield and shield ~= '' then |
| local shieldSize = sizeModule
| | -- Add banner plate |
| -- Add banner plate. | | insert(bannerSpec, {shield, size}) |
| insert(bannerSpec, {shield, shieldSize}) | |
| end | | end |
| end | | end |
|
| |
|
| local function bannerSpec(banner, bannerSize, bannerSuffix, route) | | local function bannerSpec(banner, bannerSize, bannerSuffix, route, size) |
| local banners = {} | | local banners = {} |
| | |
| if type(banner) == "table" then | | if type(banner) == "table" then |
| local bannerSizeIsNotTable = type(bannerSize) ~= "table" | | local bannerSizeIsNotTable = type(bannerSize) ~= "table" |
| for i,filename in ipairs(banner) do | | |
| local bannersize = bannerSizeIsNotTable and bannerSize or bannerSize[i] or defaultShieldSize | | for i, filename in ipairs(banner) do |
| insert(banners, {filename, bannersize}) | | local singleBannerSize = bannerSizeIsNotTable and bannerSize or bannerSize[i] |
| | insert(banners, {filename, singleBannerSize}) |
| end | | end |
| elseif banner ~= '' then | | elseif banner ~= '' then |
| Line 58: |
Line 55: |
|
| |
|
| if route.dir then | | if route.dir then |
| addContextBanner(route, 'dir', bannerSuffix, banners) | | addContextBanner(route, 'dir', bannerSuffix, banners, size) |
| end | | end |
| | |
| if route.to then | | if route.to then |
| addContextBanner(route, 'to', bannerSuffix, banners) | | addContextBanner(route, 'to', bannerSuffix, banners, size) |
| end | | end |
|
| |
|
| Line 67: |
Line 65: |
| end | | end |
|
| |
|
| local function shieldSpec(route, mainShield, shieldList) | | local function shieldSpec(route, shieldType, size, ignoreUpright) |
| local shieldSpec = {} | | local shieldSpec = {} |
|
| |
|
| local shield | | local shield |
| if mainShield then shield = parser(route, "shieldmain") end | | if route.to and shieldType == 'main' then shield = parser(route, 'shieldtomain') end |
| if shieldList then shield = parser(route, "shieldlist") end | | if not shield and route.to then shield = parser(route, 'shieldto') end |
| | if not shield and shieldType == 'main' then shield = parser(route, 'shieldmain') end |
| | if not shield and shieldType == 'list' then shield = parser(route, 'shieldlist') end |
| if not shield then shield = parser(route, 'shield') or '' end | | if not shield then shield = parser(route, 'shield') or '' end |
| if shield == '' then return shieldSpec end | | if shield == '' then return shieldSpec end |
| | |
| local orientation = parser(route, 'orientation') | | local orientation = parser(route, 'orientation') |
| | local shieldSize |
| | local shieldSizeIsNotTable |
| | |
| | if type(orientation) == "table" then |
| | shieldSize = {} |
| | shieldSizeIsNotTable = false |
|
| |
|
| local function size(route)
| | for i, shieldOrientation in ipairs(orientation) do |
| if orientation == "upright" then
| | insert(shieldSize, (shieldOrientation ~= 'upright' or ignoreUpright) and 'x' .. size or size) |
| return sizeModule
| |
| else return "x" .. sizeModule
| |
| end | | end |
| | else |
| | shieldSize = (orientation ~= 'upright' or ignoreUpright) and 'x' .. size or size |
| | shieldSizeIsNotTable = true |
| end | | end |
| | | |
| local shieldsize = sizeModule | | local banner = parser(route, 'banner') or {} |
| | local bannerSize = size |
| | local bannerSuffix = parser(route, 'bannersuffix') |
| | | |
| local banner = parser(route, 'banner') or {}
| |
| local bannersize = sizeModule
| |
| local bannersuffix = parser(route, 'bannersuffix')
| |
|
| |
| local bannerIsNotTable = type(banner) ~= "table" | | local bannerIsNotTable = type(banner) ~= "table" |
| local bannersizeIsNotTable = type(bannersize) ~= "table" | | local bannerSuffixIsNotTable = type(bannerSuffix) ~= "table" |
| local bannersuffixIsNotTable = type(bannersuffix) ~= "table"
| |
|
| |
|
| if type(shield) == "table" then | | if type(shield) == "table" then |
| for i,filename in ipairs(shield) do | | for i, filename in ipairs(shield) do |
| local size = shieldsize or shieldsize[i] | | -- Fallback to default size for the size style |
| if size == "" then size = nil end
| | local singleShieldSize = shieldSizeIsNotTable and shieldSize or shieldSize[i] or size |
| -- banner.all describes banners that apply to all multiple shields. | | |
| | -- banner.all describes banners that apply to all multiple shields |
| local shieldBanner = bannerIsNotTable and banner or (banner[i] or banner.all or {}) | | local shieldBanner = bannerIsNotTable and banner or (banner[i] or banner.all or {}) |
| -- Banner size is default if the corresponding entry
| | local shieldBannerSuffix = bannerSuffix and (bannerSuffixIsNotTable and bannerSuffix or bannerSuffix[i]) |
| -- in bannerSize table is not set.
| | |
| local shieldBannerSize =
| |
| bannersizeIsNotTable and bannersize
| |
| or (bannersize[i] or bannersize.all or defaultShieldSize)
| |
| local shieldBannerSuffix = bannersuffix and (bannersuffixIsNotTable and bannersuffix or bannersuffix[i]) | |
| insert(shieldSpec, { | | insert(shieldSpec, { |
| shield = {filename, size}, | | shield = {filename, singleShieldSize}, |
| banners = bannerSpec(shieldBanner, shieldBannerSize, shieldBannerSuffix, route) | | banners = bannerSpec(shieldBanner, bannerSize, shieldBannerSuffix, route, size), |
| | route = route |
| }) | | }) |
| end | | end |
| elseif shield ~= '' then | | elseif shield ~= '' then |
| if shieldsize == "" then shieldsize = nil end
| |
| insert(shieldSpec, { | | insert(shieldSpec, { |
| shield = {shield, shieldsize}, | | shield = {shield, shieldSize}, |
| banners = bannerSpec(banner, bannersize, bannersuffix, route) | | banners = bannerSpec(banner, bannerSize, bannerSuffix, route, size), |
| | route = route |
| }) | | }) |
| end | | end |
| Line 123: |
Line 126: |
|
| |
|
| local missingShields | | local missingShields |
|
| |
| local shieldExistsCache = {} | | local shieldExistsCache = {} |
|
| |
|
| -- Return up to two booleans.
| |
| -- The first boolean is false if `shield` does not exist, and true otherwise.
| |
| -- If the first boolean is true, the second boolean is true if the shield is
| |
| -- landscape (width >= height), and false otherwise.
| |
| local function shieldExists(shield) | | local function shieldExists(shield) |
| local result = shieldExistsCache[shield] | | local exists = shieldExistsCache[shield] |
| if result == nil then | | |
| | if exists == nil then |
| local file = mw.title.new(shield, 'Media').file | | local file = mw.title.new(shield, 'Media').file |
| -- Cache result. | | exists = file.exists |
| local exists = file.exists
| | -- Cache result |
| result = {exists} | | shieldExistsCache[shield] = exists |
| if exists then result[2] = file.width >= file.height end
| |
| shieldExistsCache[shield] = result | |
| end | | end |
| if result[1] then return true, result[2] end | | |
| | if exists then return true end |
| | |
| insert(missingShields, shield) | | insert(missingShields, shield) |
| return false | | return false |
| end | | end |
|
| |
|
| local function render(shieldEntry, scale, showLink) | | local function render(shieldEntry, nonDecorative) |
| local shield = shieldEntry.shield | | local shield = shieldEntry.shield |
| local banners = shieldEntry.banners | | local banners = shieldEntry.banners |
| local exists, landscape = shieldExists(shield[1]) | | local exists = shieldExists(shield[1]) |
| if not exists then return '' end | | if not exists then return '' end |
| | | |
| local size | | local alt = '' |
| if shield[2] then | | if nonDecorative then |
| local width, height = mw.ustring.match(shield[2], "(%d*)x?(%d*)") | | alt = (parser(shieldEntry.route, 'abbr') or '') .. ' marker' |
| width = tonumber(width)
| |
| height = tonumber(height)
| |
| local sizeparts = {}
| |
| if width then
| |
| insert(sizeparts, format("%d", width * scale))
| |
| end
| |
| if height then
| |
| insert(sizeparts, format("x%d", height * scale))
| |
| end
| |
| size = concat(sizeparts)
| |
| else
| |
| size = format("%s%d", landscape and "x" or "", sizeModule)
| |
| end | | end |
| local shieldCode = format("[[File:%s|%spx|link=|alt=]]", shield[1], size) | | |
| | local shieldCode = format("[[File:%s|%s|link=|alt=%s]]", shield[1], shield[2], alt) |
| if not banners[1] then return shieldCode end | | if not banners[1] then return shieldCode end |
|
| |
|
| for _,banner in ipairs(banners) do | | for _, banner in ipairs(banners) do |
| if shieldExists(banner[1]) then | | shieldCode = format( |
| shieldCode = format("[[File:%s|%s|link=|alt=]]<br>%s",
| | "[[File:%s|%s|link=|alt=%s]]<br>%s", |
| banner[1],
| | banner[1], |
| banner[2],
| | banner[2], |
| shieldCode)
| | alt, |
| end | | shieldCode |
| | ) |
| end | | end |
| return '<span style="display: inline-block; vertical-align: baseline; line-height: 0; text-align: center;">' .. shieldCode .. '</span>' | | |
| | return '<span style="display: inline-block; vertical-align: baseline; line-height: 0; text-align: center;">' |
| | .. shieldCode |
| | .. '</span>' |
| end | | end |
|
| |
|
| function p.shield(route, scale, showLink, mainShield, shieldList) | | function p.shield(route, shieldType, sizeOrStyle, nonDecorative) |
| missingShields = {} | | missingShields = {} |
| if route.rdt then | | |
| local shieldSize = mw.ustring.match(route.rdt, '^(%d+)$') or 17
| | local size |
| scale = shieldSize/defaultShieldSize | | local ignoreUpright |
| | |
| | if sizeOrStyle and sizeOrStyle:match('^%d+px$') then |
| | size = sizeOrStyle |
| | ignoreUpright = false |
| | else |
| | local sizeModule = require(sizeModuleName) -- REMOVE SANDBOX |
| | size = sizeModule._size({ style = sizeOrStyle }) |
| | ignoreUpright = sizeModule._ignoreUpright(sizeOrStyle) |
| end | | end |
| scale = scale or 1 | | |
| | |
| local rendered = {} | | local rendered = {} |
| for _,entry in ipairs(shieldSpec(route, mainShield, shieldList)) do | | for _, entry in ipairs(shieldSpec(route, shieldType, size, ignoreUpright)) do |
| insert(rendered, render(entry, scale, showLink)) | | insert(rendered, render(entry, nonDecorative)) |
| end | | end |
| return concat(rendered), missingShields | | |
| | return concat(rendered, ' '), missingShields |
| end | | end |
|
| |
|
| function p.link(route) | | -- Links |
| local abbr, errMsg = parser(route, 'abbr') | | function p.link(route, useName) |
| | local abbr, errMsg = parser(route, useName and 'name' or 'abbr') |
| | |
| if not abbr then | | if not abbr then |
| route.typeerror = true | | route.typeerror = true |
| return util.err(errMsg or format("Invalid type: %s", route.type or "(nil)")) | | return util.err(errMsg or format("Invalid type: %s", route.type or "(nil)")) |
| end | | end |
| | |
| if route.nolink then return abbr, abbr end | | if route.nolink then return abbr, abbr end |
|
| |
|
| Line 208: |
Line 211: |
|
| |
|
| return format("[[%s|%s]]", link, abbr), abbr | | return format("[[%s|%s]]", link, abbr), abbr |
| end
| |
|
| |
| local function stateName(args)
| |
| -- TODO transition
| |
| local data = mw.loadData(statenameModuleName)
| |
| local abbr = args.state or args.province
| |
| local countryData = data[args.country]
| |
| return countryData and countryData[abbr]
| |
| end
| |
|
| |
| function p.locations(args, module, group)
| |
| module = module or ""
| |
| local modulearticle = module .. "article"
| |
| local moduleprefix = module .. "prefix"
| |
| local modulenameprefix = module .. "nameprefix"
| |
| local modulenamesuffix = module .. "namesuffix"
| |
|
| |
| local warnings = {}
| |
|
| |
| -- Region, for disambiguation
| |
| local region = parserModule.parser(args, "region", " common ")
| |
| if not region then
| |
| -- TODO transition
| |
| if args.region then
| |
| warnings.region = "region parameter is deprecated"
| |
| region = args.region
| |
| elseif args.country and (args.state or args.province) then
| |
| warnings.region = "Inferring region from country and state/province"
| |
| region = stateName(args)
| |
| end
| |
| end
| |
| local regionName
| |
| local regionText
| |
| if type(region) == "table" then
| |
| regionName = region.name
| |
| regionText = format("[[%s|%s]]", region.link, regionName)
| |
| elseif region then
| |
| regionName = region
| |
| regionText = format("[[%s]]", regionName)
| |
| end
| |
| args.region = regionName
| |
|
| |
| local locations = parserModule.parser(args, "locations", " common ") or {}
| |
|
| |
| -- Primary topic requires no specialization to supplied locations.
| |
| local primaryTopic = not locations and module == "jctint" and args.primary_topic ~= 'no'
| |
| if args.primary_topic then
| |
| -- TODO transition
| |
| warnings.primary_topic = "primary_topic parameter is deprecated"
| |
| end
| |
|
| |
| -- Independent city
| |
| local indepCityText
| |
| if args.indep_city_special then
| |
| indepCityText = args.indep_city_special -- Overrides `indep_city` argument.
| |
| elseif args.indep_city then
| |
| local indepCity = args.indep_city
| |
| local spec = locations.indep_city
| |
| if spec then
| |
| local link = format("%s%s%s",
| |
| spec.linkprefix or "", indepCity, spec.linksuffix or "")
| |
| local name = format("%s%s%s",
| |
| spec[modulenameprefix] or spec.nameprefix or "",
| |
| indepCity,
| |
| spec[modulenamesuffix] or spec.namesuffix or "")
| |
| indepCityText = format("%s%s[[%s|%s]]",
| |
| spec[modulearticle] or spec.article or "",
| |
| spec[moduleprefix] or spec.prefix or "",
| |
| link, name)
| |
| else
| |
| -- TODO transition
| |
| warnings.indep_city = "Spec for indep_city parameter undefined in road data module"
| |
| local cityLink -- Wikilink for independent city
| |
| if primaryTopic then
| |
| cityLink = format('[[%s]]', indepCity)
| |
| else
| |
| -- Specialize independent city to the region.
| |
| cityLink = format('[[%s, %s|%s]]', indepCity, region, indepCity)
| |
| end
| |
| indepCityText = "[[Independent city|City]] of " .. cityLink
| |
| end
| |
| end
| |
| if indepCityText then
| |
| return {region = regionText, indep_city = indepCityText, warnings = warnings}
| |
| end
| |
|
| |
| -- First-level subdivision, e.g., county
| |
| -- Name of the type of subdivision, e.g., "County" and "Parish"
| |
| local sub1name = args.sub1name -- TODO transition
| |
| local sub1Text
| |
| if args.sub1_special then
| |
| sub1Text = args.sub1_special -- Overrides `sub1` argument.
| |
| elseif args.sub1 then
| |
| local sub1 = args.sub1
| |
| local article
| |
| local link = sub1
| |
| local name = sub1
| |
| -- Type of first-level subdivision area, as a form of disambiguation
| |
| local sub1area = args.sub1area
| |
| if sub1area then
| |
| local sub1areaSpec = locations.sub1areas and locations.sub1areas[sub1area]
| |
| if sub1areaSpec then
| |
| article = sub1areaSpec[modulearticle] or sub1areaSpec.article or ""
| |
| link = format("%s%s%s",
| |
| sub1areaSpec.linkprefix or "", link, sub1areaSpec.linksuffix or "")
| |
| name = format("%s%s%s",
| |
| group and "" or sub1areaSpec[modulenameprefix] or sub1areaSpec.nameprefix or "",
| |
| name,
| |
| group and "" or sub1areaSpec[modulenamesuffix] or sub1areaSpec.namesuffix or "")
| |
| else
| |
| -- TODO report error
| |
| local errMsg = util.err(format("Undefined sub1area: %s", sub1area))
| |
| name = format("%s%s", name, errMsg)
| |
| end
| |
| end
| |
| if locations.sub1 then
| |
| local spec = locations.sub1
| |
| -- Prepend and append text from spec.
| |
| link = format("%s%s%s",
| |
| spec.linkprefix or "", link, spec.linksuffix or "")
| |
| name = format("%s%s%s",
| |
| spec[modulenameprefix] or spec.nameprefix or "",
| |
| name,
| |
| spec[modulenamesuffix] or spec.namesuffix or "")
| |
| sub1Text = format("%s[[%s|%s]]", article or "", link, name)
| |
| else
| |
| -- TODO transition
| |
| warnings.sub1 = "Spec for sub1 parameter undefined in road data module"
| |
| -- Add type (if specified) to wikilink for first-level subdivision.
| |
| local sub1Link = sub1name and trim(format("%s %s", sub1, sub1name)) or sub1
| |
| local sub1Name = module == "jcttop" and sub1Link or sub1
| |
| if primaryTopic then
| |
| sub1Text = format('[[%s|%s]]', sub1Link, sub1Name)
| |
| else
| |
| -- Specialize first-level subdivision, with type added, to the region.
| |
| sub1Text = format('[[%s, %s|%s]]', sub1Link, region, sub1Name)
| |
| end
| |
| end
| |
| end
| |
|
| |
| -- Second-level subdivision, e.g., city and town
| |
| local sub2Text
| |
| if args.sub2_special then
| |
| sub2Text = args.sub2_special -- Overrides `sub2` argument.
| |
| elseif args.sub2 then
| |
| local sub2 = args.sub2
| |
| if sub2 == "none" then
| |
| sub2Text = "​" -- Zero-width space
| |
| elseif sub2 == " " then
| |
| -- TODO transition
| |
| warnings.sub2 = " argument for sub2 parameter is deprecated"
| |
| sub2Text = "​" -- Zero-width space
| |
| elseif primaryTopic then
| |
| -- TODO transition
| |
| sub2Text = format("[[%s]]", sub2)
| |
| else
| |
| local article
| |
| local link = sub2
| |
| local name = sub2
| |
| -- Type of area, e.g., city and village, as a form of disambiguation
| |
| local sub2area = args.sub2area --[[TODO transition]] or args.area
| |
| if sub2area then
| |
| local sub2areaSpec = locations.sub2areas and locations.sub2areas[sub2area]
| |
| if not sub2areaSpec then
| |
| -- TODO transition
| |
| warnings.sub2 =
| |
| format("Spec for area parameter '%s' undefined in road data module", sub2area)
| |
| local sub2areas = { -- table of different area types
| |
| city = {
| |
| linksuffix = " (city)",
| |
| jcttoparticle = "the ",
| |
| nameprefix = "City of "
| |
| },
| |
| town = {
| |
| linksuffix = " (town)",
| |
| jcttoparticle = "the ",
| |
| nameprefix = "Town of "
| |
| },
| |
| village = {
| |
| linksuffix = " (village)",
| |
| jcttoparticle = "the ",
| |
| nameprefix = "Village of "
| |
| },
| |
| community = {
| |
| linksuffix = " (community)",
| |
| jcttoparticle = "the ",
| |
| nameprefix = "Community of "
| |
| },
| |
| CDP = {
| |
| linksuffix = " (CDP)",
| |
| jcttoparticle = "the ",
| |
| nameprefix = "Community of "
| |
| },
| |
| hamlet = {
| |
| linksuffix = " (hamlet)",
| |
| jcttoparticle = "the ",
| |
| nameprefix = "Hamlet of "
| |
| },
| |
| ["unorganized territory"] = {
| |
| linksuffix = " (unorganized territory)",
| |
| jcttoparticle = "the ",
| |
| nameprefix = "Unorganized Territory of "
| |
| },
| |
| township = {
| |
| linksuffix = " Township",
| |
| namesuffix = " Township",
| |
| }
| |
| }
| |
| sub2areaSpec = sub2areas[sub2area]
| |
| end
| |
| if sub2areaSpec then
| |
| article = sub2areaSpec[modulearticle] or sub2areaSpec.article or ""
| |
| link = format("%s%s%s",
| |
| sub2areaSpec.linkprefix or "", link, sub2areaSpec.linksuffix or "")
| |
| name = format("%s%s%s",
| |
| group and "" or sub2areaSpec[modulenameprefix] or sub2areaSpec.nameprefix or "",
| |
| name,
| |
| group and "" or sub2areaSpec[modulenamesuffix] or sub2areaSpec.namesuffix or "")
| |
| else
| |
| -- TODO report error
| |
| local errMsg = util.err(format("Undefined sub2area: %s", sub2area))
| |
| name = format("%s%s", name, errMsg)
| |
| end
| |
| end
| |
| if locations.sub2 then
| |
| local spec = locations.sub2
| |
| -- Prepend and append text from spec.
| |
| link = format("%s%s%s",
| |
| spec.linkprefix or "", link, spec.linksuffix or "")
| |
| name = format("%s%s%s",
| |
| spec[modulenameprefix] or spec.nameprefix or "",
| |
| name,
| |
| spec[modulenamesuffix] or spec.namesuffix or "")
| |
| else
| |
| -- TODO transition
| |
| warnings.sub2 = "Spec for sub2 parameter undefined in road data module"
| |
| -- Some second-level subdivisions are not unique in a given region.
| |
| -- `sub1dab` is the first-level subdivision to be used for disambiguation.
| |
| local sub1dab = args.sub1dab
| |
| if sub1dab then
| |
| sub1dab = sub1name and trim(format("%s %s", sub1dab, sub1name)) or sub1dab
| |
| link = format("%s, %s", link, sub1dab)
| |
| end
| |
| link = format("%s, %s", link, region) -- Add region to wikilink
| |
| end
| |
| sub2Text = format("%s[[%s|%s]]", article or "", link, name)
| |
| end
| |
| end
| |
| return {region = regionText, sub1 = sub1Text, sub2 = sub2Text, warnings = warnings}
| |
| end | | end |
|
| |
|
| return p | | return p |