Module:CanadaByProvinceCatNav

--[[v01.18: fix handling of definite article (e.g. for 'the Northwest Territories')2.0 Resolve category redirects each title consists of 3 parts    * prefix    * province name    * suffix e.g. "Foo in Quebec"    * prefix = "Foo in "    * province name = "Quebec"    * suffix = "" e.g. "Nunavut-related lists"    * prefix = ""    * province name = "Nunavut"    * suffix = "-related lists"]]-- configlocal textSize = '90%'local tableClass="toc"local evenRowStyle = "vertical-align:top; background-color:#f3f3f3;"local oddRowStyle = "vertical-align:top;"local labelStyle = "text-align:right; font-weight: bold; padding: 0.25em 0.5em 0.25em 0.5em;"local listStyle = "text-align:left; font-weight: normal; padding: 0.25em 0.5em 0.25em 0.5em;"local greyLinkColor = "#888"--[[ Note that the table styles are designed to ensure that the navbox is as wide as possible, while still leaving     enough enough space on the right for portal boxes, commons links, and GeoGroup templates.     A lot of fiddling was needed to make it work, so please test any chnages very carfully in the sandbox.]]local tableFallbackMaxWidth="auto"local tableMaxWidth="calc(100% - 25em)" -- Template:GeoGroup has width: 23em<-- local tableStyle="margin-left:0; margin-right:auto; clear:left !important; margin-top:0 !important; float:left; width:auto;"local tableStyle="margin-left:0; margin-right:auto; clear:left !important; margin-top:0 !important; width:auto;"-- Templates which are allowed to call this modulelocal callingTemplates = {'Template:CanadaByProvinceCatNav',}-- globals for this modulelocal debugging = falselocal debugmsg = ""local tableRowNum = 0local title_prefix = ""local title_suffix = ""local title_prefixlocal title_suffixlocal thisPageProvincelocal greyLinkCount = 0local blueLinkCount = 0local parentname = ""local templateNamelocal horizontal = require('Module:List').horizontallocal getArgs = require('Module:Arguments').getArgslocal yesno = require('Module:Yesno')local ResolveCategoryRedirect = require('Module:Resolve category redirect').rtargetlocal p = {}--[[Plain text list of provinces and territories* Each entry exactly as it appears in running text in categoiry titles, with any prefix (e.g. "the")* Be sure to avoid hidden characters and duplicate spaces.  They break the pattern-matching on which this module relies]]local CanadaProvinces = {'Alberta','British Columbia','Manitoba','New Brunswick','Newfoundland and Labrador','the Northwest Territories','Nova Scotia','Nunavut','Ontario','Prince Edward Island','Quebec','Saskatchewan','Yukon'}-- If the page title matches any of these Lua patterns, treat it as a false positive local falsePositiveChecks = {"Northern Alberta","Southern Alberta","Quebec City","Central Ontario","Eastern Ontario","Northern Ontario","Southwestern Ontario"}local function publishDebugLog()if not debugging thenreturn ""endreturn "==Debugging ==\n\n" .. debugmsg .. "\n== Output ==\n"end-- debugLog builds a log which can be output if debuging is enabled-- each log entry is given a level, so that the output is not simply a flat list-- a debug msg may be appended to the previous msg by setting the level to nillocal function debugLog(level, msg)if (debugmsg == nil) thendebugmsg = ""endif (level ~= nil) then-- not appending, so make a new linedebugmsg = debugmsg .. "\n"-- then add the levellocal ifor i = 1, level doif (i % 2) == 1 thendebugmsg = debugmsg .. "#"elsedebugmsg = debugmsg .. "*"endend enddebugmsg = debugmsg .. " " .. msgreturn trueendlocal function makeTrackingCategory()-- discount the current page, which will always be coded as a blue link, but rendered as bold un-navigableblueLinkCount = blueLinkCount - 1if greyLinkCount == 0 thenreturn "[[Category:" .. templateName .. " with no grey links]]"endif blueLinkCount == 0 thenreturn "[[Category:" .. templateName .. " with all grey links]]"endif  greyLinkCount <= 5 thenreturn "[[Category:" .. templateName .. " with fewer than 5 grey links]]"endif  greyLinkCount >= 10 thenreturn "[[Category:" .. templateName .. " with 10 or more grey links]]"endif  greyLinkCount > 5 thenreturn "[[Category:" .. templateName .. " with over 5 grey links]]"endreturn ""end-- Make a piped link to a category, if it exists-- If it doesn't exist, just display the greyed the link title without linkinglocal function makeCatLink(catname, disp)local displaytextif (disp ~= "") and (disp ~= nil) then-- use 'disp' parameter, but strip any leading word "the"displaytext = mw.ustring.gsub(disp, "^[tT]he%s+", "");elsedisplaytext = catnameendlocal fmtlinklocal catPage = mw.title.new( catname, "Category" )if (catPage.exists) thenfmtlink = "[[:Category:" .. catname .. "|" .. displaytext .. "]]"blueLinkCount = blueLinkCount + 1elsefmtlink = '<span style="color:' .. greyLinkColor .. '">' .. displaytext .. "</span>"greyLinkCount = greyLinkCount + 1endreturn fmtlinkendlocal function makeCatName(provinceName, prefix, suffix)local thisCatName = prefix .. provinceName .. suffixdebugLog(5, "thisCatName = [" .. thisCatName .. "]")--[[Now check whether the all following conditions are true1/ the provinceName begins with "the"2/ the category does NOT exist if we use "the "3/ the category does exist if we strip "the " If those conditions are all true, them strip "the" ]] if (mw.ustring.match(provinceName, "^[tT]he ") ~= nil) thendebugLog(6, "ProvinceName begins with 'the'")local provinceNameStripped = mw.ustring.gsub(provinceName, "^[tT]he ", "", 1)local thisCatNameStripped = prefix .. provinceNameStripped .. suffixdebugLog(6, "thisCatNameStripped = [" .. thisCatNameStripped .. "]")local testCatPage = mw.title.new(thisCatName, "Category" )local testCatPageStripped = mw.title.new(thisCatNameStripped, "Category" )if not testCatPage.exists thendebugLog(7, "[" .. testCatPage.fullText .. "] .. does not exist") if (testCatPageStripped.exists) thendebugLog(7, "[" .. testCatPageStripped.fullText .. "] .. DOES exist, so use that") return thisCatNameStrippedendendendreturn thisCatNameendlocal function makeTableRow(rowLabel, provinceList)debugLog(2, "makeTableRow, label: ")if (rowLabel == nil) thenrowLabel = "By&nbsp;province<br />or territory"debugLog(nil, rowLabel)elserowLabel = mw.text.trim(rowLabel)debugLog(nil, " [" .. rowLabel .. "]")endtableRowNum = tableRowNum + 1local thisRowif (tableRowNum % 2) == 0 thendebugLog(3, "Even-numbered")thisRow = '<tr style="' .. evenRowStyle .. '">\n'elsedebugLog(3, "Odd-numbered")thisRow = '<tr style="' .. oddRowStyle .. '">\n'endif not ((rowLabel == nil) or (rowLabel =="")) thenthisRow = thisRow .. '<td style="' .. labelStyle .. '">' .. rowLabel .. '</td>\n'end-- now begin making the row contentsdebugLog(3, "Process provinceList")local list_args = {}for i, aProvince in ipairs(provinceList) dodebugLog(4, "No. " .. tostring(i) .. ": [" .. aProvince .. "]")myCatName = ResolveCategoryRedirect(makeCatName(aProvince, title_prefix, title_suffix))table.insert(list_args, makeCatLink(myCatName, aProvince))endreturn thisRow .. '<td style="' .. listStyle .. ';">' .. horizontal(list_args) .. '</td>\n</tr>'endlocal function makeTable()debugLog(1, "makeTable")tableRowNum = 0local myTable = '<table class="' .. tableClass .. '"'myTable = myTable .. ' style="' .. tableStyle .. '; font-size:' .. textSize .. '; max-width:' .. tableFallbackMaxWidth .. '; max-width:' .. tableMaxWidth ..'">\n'myTable = myTable .. makeTableRow(nil, CanadaProvinces)myTable = myTable .. "</table>\n"return myTableendlocal function patternSearchEncode(s)return mw.ustring.gsub(s, "([%W])", "%%%1")end-- Does the pagename include a province name?local function findprovinceNameInPagename(pn, provinceList, description)local i, aProvince, testProvincedebugLog(2, "checking [" .. pn .."] for a province name in province set: " .. description)for i, aProvince in ipairs(provinceList) dotestProvince = aProvincedebugLog(3, "testing: ["  .. testProvince .. "]")local testProvinceEncoded = patternSearchEncode(testProvince)-- For efficiency, the first test is a simple match as a a screening test-- If the bare county name is nowhere in the pagename, then no need for-- more precise checks-- This check would be one line in regex, but Lua pattern matching is cruder,--so we need several passes to ensure that any match is of a complete worddebugLog(4, "simple match? ")if (not mw.ustring.match(pn, testProvinceEncoded)) thendebugLog(nil, "Fail")elsedebugLog(nil, "Success")-- test for false positiveslocal j, aFalsePositiveTestfor j, aFalsePositiveTest in ipairs(falsePositiveChecks) dodebugLog(5, "false positive test pattern '" .. aFalsePositiveTest .. "' ? ")if (mw.ustring.match(pn, aFalsePositiveTest)) thendebugLog(nil, "Match, so fail")return nilenddebugLog(nil, "No match, so OK")enddebugLog(4, "match whole name? ")if (pn == testProvince) thendebugLog(nil, "Yes")return testProvinceenddebugLog(nil, "No")debugLog(4, "match at start, followed by separator? ")if mw.ustring.match(pn, "^" .. testProvinceEncoded .. "[^%w]") thendebugLog(nil, "Yes")return testProvinceenddebugLog(nil, "No")debugLog(4, "match at end, preceded by separator? ")if mw.ustring.match(pn, "[^%w]" .. testProvinceEncoded .. "$") thendebugLog(nil, "Yes")return testProvinceenddebugLog(nil, "No")debugLog(4, "match anywhere, preceded and followed by separator? ")if mw.ustring.match(pn, "[^%w]" .. testProvinceEncoded .. "[^%w]") thendebugLog(nil, "Yes")return testProvinceenddebugLog(nil, "No")end-- Special case: if the province name we are testing begins with a prefixed "the"debugLog(4, "does testProvince begin with 'the' ? ")if (mw.ustring.match(testProvince, "^[tT]he ") == nil) thendebugLog(nil, "No")elsedebugLog(nil, "Yes")endif (mw.ustring.match(testProvince, "^[tT]he ") ~= nil) thenlocal testProvinceStripped = mw.ustring.gsub(testProvince, "^[tT]he ", "", 1)local testProvinceStrippedEncoded = patternSearchEncode(testProvinceStripped)debugLog(4, "test pattern without leading definite article, i.e. '" .. testProvinceStrippedEncoded .. "' ? ")if (mw.ustring.match(pn, "[^%w]" .. testProvinceStrippedEncoded .. "[^%w]")  ~= nil)or (mw.ustring.match(pn, "^" .. testProvinceStrippedEncoded .. "[^%w]")  ~= nil)or (mw.ustring.match(pn, "[^%w]" .. testProvinceStrippedEncoded .. "$")  ~= nil)or (mw.ustring.match(pn, "^" .. testProvinceStrippedEncoded .. "$")  ~= nil) thendebugLog(nil, "Yes")return testProvinceStrippedenddebugLog(nil, "No")endendreturn nilend-- parse the pagename to find 3 parts: prefix, province name, suffixlocal function parsePagename(pn)debugLog(1, "parsePagename: [" .. pn .. "]")local validprovinceNamevalidprovinceName = findprovinceNameInPagename(pn, CanadaProvinces, "provincelst")if validprovinceName == nil thenreturn falseend-- if we get here, the page name "pn" includes a validprovinceName-- so now we need to split the stringdebugLog(2, "split pagename around [" .. validprovinceName .. "]")local validProvinceEncoded = mw.ustring.gsub(validprovinceName, "([%W])", "%%%1")match_prefix, match_province, match_suffix = mw.ustring.match(pn, "^(.*)(" .. validProvinceEncoded .. ")(.*)$")title_prefix = match_prefixtitle_suffix = match_suffixdebugLog(2, "parse successful")debugLog(3, "title_prefix = [" .. title_prefix .. "]")debugLog(3, "thisPageProvince = [" .. match_province .. "]")debugLog(3, "title_suffix = [" .. title_suffix .. "]")return trueendlocal function getYesNoParam(args, thisParamName, defaultVal)local paramVal = args[thisParamName]if paramVal == nil thenparamVal = ""enddebugLog(2, "Evaluate yes/no parameter: [" .. thisParamName .. "] = [" .. paramVal .. "]")debugLog(3, "default = " .. ((defaultVal and "Yes") or "No"))debugLog(3, "Evaluate as: ")local returnValueif paramVal == "" thenreturnValue = defaultValelsereturnValue = yesno(args[thisParamName], defaultVal)endif (returnValue) thendebugLog(nil, "Yes")elsedebugLog(nil, "No")endreturn returnValueendlocal function makeErrorMsg(s)return '<p class="error">[[' .. parentname .. ']] Error: ' .. s .. '</p>\n'endlocal function isValidParent(p)for i, aParent in ipairs(callingTemplates) doif p == aParent thenreturn trueendendreturn falseendfunction p.main(frame)local parent = frame:getParent()if parent then parentname = parent:getTitle():gsub('/sandbox$', '')endif (parentname == nil) or not isValidParent(parentname) thenlocal errormsg = '<p class="error"> Error: ' .. parentname .. ' is not a valid wrapper for [[' .. frame:getTitle() .. ']]\n'errormsg = errormsg .. '<br><br>Valid wrappers: 'local i, aParentfor i, aParent in ipairs(callingTemplates) doerrormsg = errormsg .. '[[' .. aParent  .. ']]'enderrormsg = errormsg .. '</p>'return errormsgendtemplateName = mw.ustring.gsub(parentname, "^Template:", "")debugLog(1, "Check parameters")debugging = getYesNoParam(frame.args, "debug", false)-- get the page titlethispage = mw.title.getCurrentTitle()thispagename = thispage.text;debugLog(1, "mw.title.getCurrentTitle()")debugLog(2, "thispage.text = [" .. thispage.text .."]")debugLog(2, "thispage.namespace = [" .. thispage.namespace .."]")debugLog(2, "thispage.nsText = [" .. thispage.nsText .."]")debugLog(2, "is it a cat? using (thispage:inNamespace(14)): ")if not (thispage:inNamespace(14)) thendebugLog(nil, "No, this is not a category")debugLog(1, "Not a category, so no output")return makeErrorMsg("only for use on a category page") .. publishDebugLog()enddebugLog(nil, "Yes, this is a category")if not parsePagename(thispagename) then-- some error parsing the title, so don't proceed to outputlocal trackingCatInvalid = "[[Category:" .. templateName .. " on invalid category]]"return makeErrorMsg('the name of this category does not include a valid Candian province or territory') .. publishDebugLog() .. trackingCatInvalidenddebugLog(1, "all parse done")debugLog(2, "title_prefix = [" .. title_prefix .. "]")debugLog(2, "title_suffix = [" .. title_suffix .. "]")local myNavTable = makeTable()debugLog(2, "blueLinkCount = [" .. blueLinkCount .. "]. &nbsp; (NB The current page is always counted as a bluelink, but will not be navigable)")debugLog(2, "greyLinkCount = [" .. greyLinkCount .. "]")if (blueLinkCount <= 1) then-- This is a navbar to nowhere, so suppress displaymyNavTable = ""debugLog(1, "Zero bluelinks (other than the current page) makes this a navbox to nowhere, so do not display the navbox")endlocal myTrackingCat = makeTrackingCategory()return publishDebugLog() .. myNavTable .. myTrackingCatendreturn p