<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://www.cultopedia.org/index.php?action=history&amp;feed=atom&amp;title=Module%3ACheck_height</id>
	<title>Module:Check height - Revision history</title>
	<link rel="self" type="application/atom+xml" href="https://www.cultopedia.org/index.php?action=history&amp;feed=atom&amp;title=Module%3ACheck_height"/>
	<link rel="alternate" type="text/html" href="https://www.cultopedia.org/index.php?title=Module:Check_height&amp;action=history"/>
	<updated>2026-04-06T01:52:46Z</updated>
	<subtitle>Revision history for this page on the wiki</subtitle>
	<generator>MediaWiki 1.44.5</generator>
	<entry>
		<id>https://www.cultopedia.org/index.php?title=Module:Check_height&amp;diff=2261&amp;oldid=prev</id>
		<title>imported&gt;S.A. Julio: remove refs</title>
		<link rel="alternate" type="text/html" href="https://www.cultopedia.org/index.php?title=Module:Check_height&amp;diff=2261&amp;oldid=prev"/>
		<updated>2025-12-24T17:01:09Z</updated>

		<summary type="html">&lt;p&gt;remove refs&lt;/p&gt;
&lt;p&gt;&lt;b&gt;New page&lt;/b&gt;&lt;/p&gt;&lt;div&gt;-- Validates height formatting in infoboxes and adds error tracking categories when issues are found&lt;br /&gt;
&lt;br /&gt;
local p = {}&lt;br /&gt;
&lt;br /&gt;
-- Default ranges&lt;br /&gt;
local DEFAULT_FT_MIN = 4.10&lt;br /&gt;
local DEFAULT_FT_MAX = 6.11&lt;br /&gt;
local DEFAULT_CM_MIN = 147&lt;br /&gt;
local DEFAULT_CM_MAX = 212&lt;br /&gt;
&lt;br /&gt;
-- Helper function to parse and validate ftin_range parameter&lt;br /&gt;
local function parseFtInRange(rangeStr)&lt;br /&gt;
    if not rangeStr or rangeStr == &amp;#039;&amp;#039; then&lt;br /&gt;
        return DEFAULT_FT_MIN, DEFAULT_FT_MAX&lt;br /&gt;
    end&lt;br /&gt;
    &lt;br /&gt;
    -- Pattern: number (with optional decimal) - number (with optional decimal)&lt;br /&gt;
    local minStr, maxStr = rangeStr:match(&amp;#039;^(%d+%.%d+)%-(%d+%.%d+)$&amp;#039;)&lt;br /&gt;
    &lt;br /&gt;
    if not minStr or not maxStr then&lt;br /&gt;
        return DEFAULT_FT_MIN, DEFAULT_FT_MAX&lt;br /&gt;
    end&lt;br /&gt;
    &lt;br /&gt;
    local minVal = tonumber(minStr)&lt;br /&gt;
    local maxVal = tonumber(maxStr)&lt;br /&gt;
    &lt;br /&gt;
    if not minVal or not maxVal then&lt;br /&gt;
        return DEFAULT_FT_MIN, DEFAULT_FT_MAX&lt;br /&gt;
    end&lt;br /&gt;
    &lt;br /&gt;
    -- Validate that decimal part is exactly 2 digits and represents inches (00-11)&lt;br /&gt;
    local minInches = minStr:match(&amp;#039;%.(%d+)$&amp;#039;)&lt;br /&gt;
    local maxInches = maxStr:match(&amp;#039;%.(%d+)$&amp;#039;)&lt;br /&gt;
    &lt;br /&gt;
    if not minInches or not maxInches then&lt;br /&gt;
        return DEFAULT_FT_MIN, DEFAULT_FT_MAX&lt;br /&gt;
    end&lt;br /&gt;
    &lt;br /&gt;
    -- Check exactly 2 digits&lt;br /&gt;
    if #minInches ~= 2 or #maxInches ~= 2 then&lt;br /&gt;
        return DEFAULT_FT_MIN, DEFAULT_FT_MAX&lt;br /&gt;
    end&lt;br /&gt;
    &lt;br /&gt;
    -- Check range 00-11&lt;br /&gt;
    local minInchesNum = tonumber(minInches)&lt;br /&gt;
    local maxInchesNum = tonumber(maxInches)&lt;br /&gt;
    &lt;br /&gt;
    if not minInchesNum or not maxInchesNum or &lt;br /&gt;
       minInchesNum &amp;lt; 0 or minInchesNum &amp;gt; 11 or&lt;br /&gt;
       maxInchesNum &amp;lt; 0 or maxInchesNum &amp;gt; 11 then&lt;br /&gt;
        return DEFAULT_FT_MIN, DEFAULT_FT_MAX&lt;br /&gt;
    end&lt;br /&gt;
    &lt;br /&gt;
    return minVal, maxVal&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Helper function to parse and validate cm_range parameter&lt;br /&gt;
local function parseCmRange(rangeStr)&lt;br /&gt;
    if not rangeStr or rangeStr == &amp;#039;&amp;#039; then&lt;br /&gt;
        return DEFAULT_CM_MIN, DEFAULT_CM_MAX&lt;br /&gt;
    end&lt;br /&gt;
    &lt;br /&gt;
    -- Pattern: number - number (no decimals allowed)&lt;br /&gt;
    local minStr, maxStr = rangeStr:match(&amp;#039;^(%d+)%-(%d+)$&amp;#039;)&lt;br /&gt;
    &lt;br /&gt;
    if not minStr or not maxStr then&lt;br /&gt;
        return DEFAULT_CM_MIN, DEFAULT_CM_MAX&lt;br /&gt;
    end&lt;br /&gt;
    &lt;br /&gt;
    local minVal = tonumber(minStr)&lt;br /&gt;
    local maxVal = tonumber(maxStr)&lt;br /&gt;
    &lt;br /&gt;
    if not minVal or not maxVal then&lt;br /&gt;
        return DEFAULT_CM_MIN, DEFAULT_CM_MAX&lt;br /&gt;
    end&lt;br /&gt;
    &lt;br /&gt;
    return minVal, maxVal&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Helper function to remove fraction HTML markup&lt;br /&gt;
local function removeFractionMarkup(str)&lt;br /&gt;
    if not str then return str end&lt;br /&gt;
    &lt;br /&gt;
    -- Remove templatestyles strip marker&lt;br /&gt;
    str = str:gsub(&amp;quot;\127&amp;#039;\&amp;quot;`UNIQ%-%-templatestyles.-QINU`\&amp;quot;&amp;#039;\127&amp;quot;, &amp;#039;&amp;#039;)&lt;br /&gt;
    &lt;br /&gt;
    -- Remove span tags with specific classes while preserving inner content appropriately&lt;br /&gt;
    -- For sr-only spans, we want to convert the + to actual +&lt;br /&gt;
    str = str:gsub(&amp;#039;&amp;lt;span class=&amp;quot;sr%-only&amp;quot;&amp;gt;%+&amp;lt;/span&amp;gt;&amp;#039;, &amp;#039;+&amp;#039;)&lt;br /&gt;
    &lt;br /&gt;
    -- Remove the frac wrapper span (keep contents)&lt;br /&gt;
    str = str:gsub(&amp;#039;&amp;lt;span class=&amp;quot;frac&amp;quot;&amp;gt;(.-)&amp;lt;/span&amp;gt;&amp;#039;, &amp;#039;%1&amp;#039;)&lt;br /&gt;
    &lt;br /&gt;
    -- Remove num spans (keep contents)&lt;br /&gt;
    str = str:gsub(&amp;#039;&amp;lt;span class=&amp;quot;num&amp;quot;&amp;gt;(.-)&amp;lt;/span&amp;gt;&amp;#039;, &amp;#039;%1&amp;#039;)&lt;br /&gt;
    &lt;br /&gt;
    -- Remove den spans (keep contents)&lt;br /&gt;
    str = str:gsub(&amp;#039;&amp;lt;span class=&amp;quot;den&amp;quot;&amp;gt;(.-)&amp;lt;/span&amp;gt;&amp;#039;, &amp;#039;%1&amp;#039;)&lt;br /&gt;
    &lt;br /&gt;
    return str&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Helper function to remove references/inline tags&lt;br /&gt;
local function removeReferences(str)&lt;br /&gt;
    if not str then return str end&lt;br /&gt;
    &lt;br /&gt;
    -- Pattern for MediaWiki strip markers (references)&lt;br /&gt;
    str = str:gsub(&amp;quot;\127&amp;#039;\&amp;quot;`UNIQ%-%-ref.-QINU`\&amp;quot;&amp;#039;\127&amp;quot;, &amp;#039;&amp;#039;)&lt;br /&gt;
    &lt;br /&gt;
    -- Pattern for inline tags (e.g. citation needed)&lt;br /&gt;
    str = str:gsub(&amp;#039;%[%[Category:.-%&amp;lt;/sup&amp;gt;&amp;#039;, &amp;#039;&amp;#039;)&lt;br /&gt;
    &lt;br /&gt;
    return str&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Helper function to extract regular and converted heights&lt;br /&gt;
local function extractHeights(str)&lt;br /&gt;
    if not str then return nil, nil, false end&lt;br /&gt;
&lt;br /&gt;
    local regular, converted = str:match(&amp;#039;^(.-)%s%((.-)%)$&amp;#039;)&lt;br /&gt;
    &lt;br /&gt;
    if not regular or not converted then&lt;br /&gt;
        return nil, nil, false&lt;br /&gt;
    end&lt;br /&gt;
    &lt;br /&gt;
    local reconstructed = str:gsub(&amp;#039;^&amp;#039; .. regular:gsub(&amp;#039;([%(%)%.%%%+%-%*%?%[%]%^%$])&amp;#039;, &amp;#039;%%%1&amp;#039;), &amp;#039;&amp;#039;)&lt;br /&gt;
&lt;br /&gt;
    local expectedRemainder = &amp;#039; (&amp;#039; .. converted .. &amp;#039;)&amp;#039;&lt;br /&gt;
    local actualRemainder = reconstructed&lt;br /&gt;
&lt;br /&gt;
    local remainderCheck = actualRemainder:gsub(converted:gsub(&amp;#039;([%(%)%.%%%+%-%*%?%[%]%^%$])&amp;#039;, &amp;#039;%%%1&amp;#039;), &amp;#039;&amp;#039;)&lt;br /&gt;
    &lt;br /&gt;
    if remainderCheck ~= &amp;#039; ()&amp;#039; then&lt;br /&gt;
        return nil, nil, false&lt;br /&gt;
    end&lt;br /&gt;
    &lt;br /&gt;
    return regular, converted, true&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Helper function to delink wikilinks&lt;br /&gt;
local function delink(str)&lt;br /&gt;
    if not str then return str end&lt;br /&gt;
    return str:gsub(&amp;quot;%[%[(.-)%]%]&amp;quot;, function(match)&lt;br /&gt;
        local pipePos = match:find(&amp;quot;|&amp;quot;)&lt;br /&gt;
        if pipePos then&lt;br /&gt;
            return match:sub(pipePos + 1)&lt;br /&gt;
        else&lt;br /&gt;
            return match&lt;br /&gt;
        end&lt;br /&gt;
    end)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Helper function to check for invalid decimal feet/inches format (e.g., &amp;quot;5.5 ft&amp;quot; instead of &amp;quot;5 ft 6 in&amp;quot;)&lt;br /&gt;
local function hasDecimalImperial(str)&lt;br /&gt;
    if not str then return false end&lt;br /&gt;
    &lt;br /&gt;
    -- Delink the string and convert to lowercase&lt;br /&gt;
    local testStr = delink(str)&lt;br /&gt;
    local lowerStr = testStr:lower()&lt;br /&gt;
    &lt;br /&gt;
    -- Replace &amp;amp;nbsp; with regular space&lt;br /&gt;
    lowerStr = lowerStr:gsub(&amp;#039;&amp;amp;nbsp;&amp;#039;, &amp;#039; &amp;#039;)&lt;br /&gt;
    &lt;br /&gt;
    -- Regex pattern&lt;br /&gt;
    if lowerStr:match(&amp;#039;%d[,.]%d%s*foot&amp;#039;) or &lt;br /&gt;
       lowerStr:match(&amp;#039;%d[,.]%d%s*feet&amp;#039;) or &lt;br /&gt;
       lowerStr:match(&amp;#039;%d[,.]%d%s*ft&amp;#039;) or &lt;br /&gt;
       lowerStr:match(&amp;quot;%d%.%d+%s*[′&amp;#039;]&amp;quot;) or &lt;br /&gt;
       lowerStr:match(&amp;#039;%d[,.]%d%s*in&amp;#039;) then&lt;br /&gt;
        return true&lt;br /&gt;
    end&lt;br /&gt;
    &lt;br /&gt;
    return false&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Helper function to check for parentheses without &amp;amp;nbsp; inside them&lt;br /&gt;
local function hasParenWithoutNbsp(str)&lt;br /&gt;
    if not str then return false end&lt;br /&gt;
    &lt;br /&gt;
    -- Remove references and inline tags&lt;br /&gt;
    str = removeReferences(str)&lt;br /&gt;
    &lt;br /&gt;
    -- Find all parenthetical expressions and check if any lack &amp;amp;nbsp;&lt;br /&gt;
    for parenContent in str:gmatch(&amp;#039;%((.-)%)&amp;#039;) do&lt;br /&gt;
        if not parenContent:find(&amp;#039;&amp;amp;nbsp;&amp;#039;) then&lt;br /&gt;
            return true&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
    &lt;br /&gt;
    return false&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Validate imperial height format&lt;br /&gt;
local function validateImperial(height, ftMin, ftMax)&lt;br /&gt;
    if not height then return false end&lt;br /&gt;
    &lt;br /&gt;
    -- Extract feet and inches parts from the min/max values&lt;br /&gt;
    local minFeet = math.floor(ftMin)&lt;br /&gt;
    local minInches = math.floor((ftMin - minFeet) * 100 + 0.5)&lt;br /&gt;
    local maxFeet = math.floor(ftMax)&lt;br /&gt;
    local maxInches = math.floor((ftMax - maxFeet) * 100 + 0.5)&lt;br /&gt;
    &lt;br /&gt;
    -- Pattern 1: X&amp;amp;nbsp;ft Y&amp;amp;nbsp;in (simple)&lt;br /&gt;
    local feet, inches = height:match(&amp;#039;^(%d+)&amp;amp;nbsp;ft (%d+)&amp;amp;nbsp;in$&amp;#039;)&lt;br /&gt;
    if feet and inches then&lt;br /&gt;
        feet = tonumber(feet)&lt;br /&gt;
        inches = tonumber(inches)&lt;br /&gt;
        &lt;br /&gt;
        -- Check if within range&lt;br /&gt;
        if feet &amp;lt; minFeet or feet &amp;gt; maxFeet then&lt;br /&gt;
            return false&lt;br /&gt;
        end&lt;br /&gt;
        if feet == minFeet and inches &amp;lt; minInches then&lt;br /&gt;
            return false&lt;br /&gt;
        end&lt;br /&gt;
        if feet == maxFeet and inches &amp;gt; maxInches then&lt;br /&gt;
            return false&lt;br /&gt;
        end&lt;br /&gt;
        if inches &amp;lt; 0 or inches &amp;gt; 11 then&lt;br /&gt;
            return false&lt;br /&gt;
        end&lt;br /&gt;
        &lt;br /&gt;
        return true&lt;br /&gt;
    end&lt;br /&gt;
    &lt;br /&gt;
    -- Pattern 2a: X&amp;amp;nbsp;ft Y+N&amp;amp;frasl;D&amp;amp;nbsp;in (whole inches plus fraction, e.g., &amp;quot;6 ft 2+1/2 in&amp;quot;)&lt;br /&gt;
    local feet2, inches2, num2, den2 = height:match(&amp;#039;^(%d+)&amp;amp;nbsp;ft (%d+)%+(%d+)&amp;amp;frasl;(%d+)&amp;amp;nbsp;in$&amp;#039;)&lt;br /&gt;
    if feet2 and inches2 and num2 and den2 then&lt;br /&gt;
        feet2 = tonumber(feet2)&lt;br /&gt;
        inches2 = tonumber(inches2)&lt;br /&gt;
        num2 = tonumber(num2)&lt;br /&gt;
        den2 = tonumber(den2)&lt;br /&gt;
        &lt;br /&gt;
        -- Reject &amp;quot;0+&amp;quot; before fraction (should use pattern 2b instead)&lt;br /&gt;
        if inches2 == 0 then&lt;br /&gt;
            return false&lt;br /&gt;
        end&lt;br /&gt;
        &lt;br /&gt;
        -- Check if within range&lt;br /&gt;
        if feet2 &amp;lt; minFeet or feet2 &amp;gt; maxFeet then&lt;br /&gt;
            return false&lt;br /&gt;
        end&lt;br /&gt;
        if feet2 == minFeet and inches2 &amp;lt; minInches then&lt;br /&gt;
            return false&lt;br /&gt;
        end&lt;br /&gt;
        if feet2 == maxFeet and inches2 &amp;gt; maxInches then&lt;br /&gt;
            return false&lt;br /&gt;
        end&lt;br /&gt;
        if inches2 &amp;lt; 0 or inches2 &amp;gt; 11 then&lt;br /&gt;
            return false&lt;br /&gt;
        end&lt;br /&gt;
        -- Numerator must be at least 1, denominator must be &amp;gt; 1, and num &amp;lt; den&lt;br /&gt;
        if num2 &amp;lt; 1 or den2 &amp;lt;= 1 or num2 &amp;gt;= den2 then&lt;br /&gt;
            return false&lt;br /&gt;
        end&lt;br /&gt;
        &lt;br /&gt;
        return true&lt;br /&gt;
    end&lt;br /&gt;
    &lt;br /&gt;
    -- Pattern 2b: X&amp;amp;nbsp;ft N&amp;amp;frasl;D&amp;amp;nbsp;in (fraction only, e.g., &amp;quot;6 ft 1/2 in&amp;quot;)&lt;br /&gt;
    local feet3, num3, den3 = height:match(&amp;#039;^(%d+)&amp;amp;nbsp;ft (%d+)&amp;amp;frasl;(%d+)&amp;amp;nbsp;in$&amp;#039;)&lt;br /&gt;
    if feet3 and num3 and den3 then&lt;br /&gt;
        feet3 = tonumber(feet3)&lt;br /&gt;
        num3 = tonumber(num3)&lt;br /&gt;
        den3 = tonumber(den3)&lt;br /&gt;
        &lt;br /&gt;
        -- Check if within range (inches = 0 for range checking purposes)&lt;br /&gt;
        if feet3 &amp;lt; minFeet or feet3 &amp;gt; maxFeet then&lt;br /&gt;
            return false&lt;br /&gt;
        end&lt;br /&gt;
        if feet3 == minFeet and 0 &amp;lt; minInches then&lt;br /&gt;
            return false&lt;br /&gt;
        end&lt;br /&gt;
        if feet3 == maxFeet and 0 &amp;gt; maxInches then&lt;br /&gt;
            return false&lt;br /&gt;
        end&lt;br /&gt;
        -- Numerator must be at least 1, denominator must be &amp;gt; 1, and num &amp;lt; den&lt;br /&gt;
        if num3 &amp;lt; 1 or den3 &amp;lt;= 1 or num3 &amp;gt;= den3 then&lt;br /&gt;
            return false&lt;br /&gt;
        end&lt;br /&gt;
        &lt;br /&gt;
        return true&lt;br /&gt;
    end&lt;br /&gt;
    &lt;br /&gt;
    return false&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Validate metric height format (meters)&lt;br /&gt;
local function validateMeters(height, cmMin, cmMax)&lt;br /&gt;
    if not height then return false end&lt;br /&gt;
    &lt;br /&gt;
    -- Pattern: X.XX&amp;amp;nbsp;m&lt;br /&gt;
    local meters = height:match(&amp;#039;^(%d+%.%d+)&amp;amp;nbsp;m$&amp;#039;)&lt;br /&gt;
    if meters then&lt;br /&gt;
        -- Remove decimal point and check value&lt;br /&gt;
        local metersNoDot = meters:gsub(&amp;#039;%.&amp;#039;, &amp;#039;&amp;#039;)&lt;br /&gt;
        local numericValue = tonumber(metersNoDot)&lt;br /&gt;
        if numericValue and numericValue &amp;gt;= cmMin and numericValue &amp;lt;= cmMax then&lt;br /&gt;
            return true&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
    &lt;br /&gt;
    return false&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Validate metric height format (centimeters)&lt;br /&gt;
local function validateCentimeters(height, cmMin, cmMax)&lt;br /&gt;
    if not height then return false end&lt;br /&gt;
    &lt;br /&gt;
    -- Pattern: XXX&amp;amp;nbsp;cm&lt;br /&gt;
    local cm = height:match(&amp;#039;^(%d+)&amp;amp;nbsp;cm$&amp;#039;)&lt;br /&gt;
    if cm then&lt;br /&gt;
        local numericValue = tonumber(cm)&lt;br /&gt;
        if numericValue and numericValue &amp;gt;= cmMin and numericValue &amp;lt;= cmMax then&lt;br /&gt;
            return true&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
    &lt;br /&gt;
    return false&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Validate metric height based on metric parameter&lt;br /&gt;
local function validateMetric(height, metricParam, cmMin, cmMax)&lt;br /&gt;
    if not height then return false end&lt;br /&gt;
    &lt;br /&gt;
    metricParam = metricParam or &amp;#039;both&amp;#039;&lt;br /&gt;
    &lt;br /&gt;
    if metricParam == &amp;#039;m&amp;#039; then&lt;br /&gt;
        return validateMeters(height, cmMin, cmMax)&lt;br /&gt;
    elseif metricParam == &amp;#039;cm&amp;#039; then&lt;br /&gt;
        return validateCentimeters(height, cmMin, cmMax)&lt;br /&gt;
    else -- &amp;#039;both&amp;#039; or default&lt;br /&gt;
        return validateMeters(height, cmMin, cmMax) or validateCentimeters(height, cmMin, cmMax)&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Main function&lt;br /&gt;
function p.main(frame)&lt;br /&gt;
	local arguments = require(&amp;#039;Module:Arguments&amp;#039;)&lt;br /&gt;
    local personHeight = require(&amp;#039;Module:Person height&amp;#039;)&lt;br /&gt;
    local args = arguments.getArgs(frame, {trim = true})&lt;br /&gt;
    local parentArgs = frame:getParent().args&lt;br /&gt;
    &lt;br /&gt;
    -- Get parameters&lt;br /&gt;
    local height = args[1] or parentArgs[1] or &amp;#039;&amp;#039;&lt;br /&gt;
    local metricParam = args[&amp;#039;metric&amp;#039;] or parentArgs[&amp;#039;metric&amp;#039;] or &amp;#039;both&amp;#039;&lt;br /&gt;
    local catParam = args[&amp;#039;cat&amp;#039;] or parentArgs[&amp;#039;cat&amp;#039;] or &amp;#039;&amp;#039;&lt;br /&gt;
    local ftinRangeParam = args[&amp;#039;ftin_range&amp;#039;] or parentArgs[&amp;#039;ftin_range&amp;#039;] or &amp;#039;&amp;#039;&lt;br /&gt;
    local cmRangeParam = args[&amp;#039;cm_range&amp;#039;] or parentArgs[&amp;#039;cm_range&amp;#039;] or &amp;#039;&amp;#039;&lt;br /&gt;
    &lt;br /&gt;
    -- Parse range parameters&lt;br /&gt;
    local ftMin, ftMax = parseFtInRange(ftinRangeParam)&lt;br /&gt;
    local cmMin, cmMax = parseCmRange(cmRangeParam)&lt;br /&gt;
    &lt;br /&gt;
    -- If no height provided, return empty&lt;br /&gt;
    if height == &amp;#039;&amp;#039; then&lt;br /&gt;
        return &amp;#039;&amp;#039;&lt;br /&gt;
    end&lt;br /&gt;
    &lt;br /&gt;
    -- Check raw input for invalid decimal feet format (e.g., &amp;quot;5.5 ft&amp;quot; instead of &amp;quot;5 ft 6 in&amp;quot;)&lt;br /&gt;
    if hasDecimalImperial(height) then&lt;br /&gt;
        mw.addWarning(&amp;#039;&amp;lt;span style=&amp;quot;color:#d33&amp;quot;&amp;gt;Height format error: The raw height input does not match the expected pattern (feet or inches value uses decimal).&amp;lt;/span&amp;gt;&amp;#039;)&lt;br /&gt;
        if catParam ~= &amp;#039;&amp;#039; then&lt;br /&gt;
            return &amp;#039;[[Category:&amp;#039; .. catParam .. &amp;#039;]]&amp;#039;&lt;br /&gt;
        end&lt;br /&gt;
        return &amp;#039;&amp;#039;&lt;br /&gt;
    end&lt;br /&gt;
    &lt;br /&gt;
    -- Check raw input for parentheses without &amp;amp;nbsp; inside&lt;br /&gt;
    if hasParenWithoutNbsp(height) then&lt;br /&gt;
        mw.addWarning(&amp;#039;&amp;lt;span style=&amp;quot;color:#d33&amp;quot;&amp;gt;Height format error: The raw height input should not include the converted height in parentheses (this should be calculated automatically or by a template).&amp;lt;/span&amp;gt;&amp;#039;)&lt;br /&gt;
        if catParam ~= &amp;#039;&amp;#039; then&lt;br /&gt;
            return &amp;#039;[[Category:&amp;#039; .. catParam .. &amp;#039;]]&amp;#039;&lt;br /&gt;
        end&lt;br /&gt;
        return &amp;#039;&amp;#039;&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    -- Set enforce based on metric parameter (nil if &amp;#039;both&amp;#039;)&lt;br /&gt;
    local enforceValue = nil&lt;br /&gt;
    if metricParam == &amp;#039;m&amp;#039; then&lt;br /&gt;
        enforceValue = &amp;#039;m&amp;#039;&lt;br /&gt;
    elseif metricParam == &amp;#039;cm&amp;#039; then&lt;br /&gt;
        enforceValue = &amp;#039;cm&amp;#039;&lt;br /&gt;
    end&lt;br /&gt;
    &lt;br /&gt;
    local heightArgs = {&lt;br /&gt;
        [1] = height,&lt;br /&gt;
        [&amp;#039;enforce&amp;#039;] = enforceValue,&lt;br /&gt;
        [&amp;#039;ri&amp;#039;] = &amp;#039;cmin&amp;#039;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    -- Use pcall to catch any errors from Module:Person height&lt;br /&gt;
    local success, formattedHeight = pcall(function()&lt;br /&gt;
        return personHeight.main(frame:newChild{ args = heightArgs })&lt;br /&gt;
    end)&lt;br /&gt;
    &lt;br /&gt;
    -- If Module:Person height produced an error, treat as invalid structure&lt;br /&gt;
    if not success then&lt;br /&gt;
        if catParam ~= &amp;#039;&amp;#039; then&lt;br /&gt;
            return &amp;#039;[[Category:&amp;#039; .. catParam .. &amp;#039;]]&amp;#039;&lt;br /&gt;
        end&lt;br /&gt;
        return &amp;#039;&amp;#039;&lt;br /&gt;
    end&lt;br /&gt;
    &lt;br /&gt;
    -- Remove fraction markup&lt;br /&gt;
    local cleanedHeight = removeFractionMarkup(formattedHeight)&lt;br /&gt;
    &lt;br /&gt;
    -- Trim any trailing whitespace&lt;br /&gt;
    cleanedHeight = cleanedHeight:gsub(&amp;#039;%s+$&amp;#039;, &amp;#039;&amp;#039;)&lt;br /&gt;
    &lt;br /&gt;
    -- Remove references and inline tags&lt;br /&gt;
    cleanedHeight = removeReferences(cleanedHeight)&lt;br /&gt;
    &lt;br /&gt;
    -- Extract regular and converted heights&lt;br /&gt;
    local regularHeight, convertedHeight, structureValid = extractHeights(cleanedHeight)&lt;br /&gt;
    &lt;br /&gt;
    -- Check if structure is valid&lt;br /&gt;
    if not structureValid then&lt;br /&gt;
        -- Add error category&lt;br /&gt;
        mw.addWarning(&amp;#039;&amp;lt;span style=&amp;quot;color:#d33&amp;quot;&amp;gt;Height format error: The height format does not match the expected pattern.&amp;lt;/span&amp;gt;&amp;#039;)&lt;br /&gt;
        if catParam ~= &amp;#039;&amp;#039; then&lt;br /&gt;
            return &amp;#039;[[Category:&amp;#039; .. catParam .. &amp;#039;]]&amp;#039;&lt;br /&gt;
        end&lt;br /&gt;
        return &amp;#039;&amp;#039;&lt;br /&gt;
    end&lt;br /&gt;
    &lt;br /&gt;
    -- Validate the heights&lt;br /&gt;
    local regularValid = false&lt;br /&gt;
    local convertedValid = false&lt;br /&gt;
    &lt;br /&gt;
    -- Check if regular height is imperial or metric&lt;br /&gt;
    if validateImperial(regularHeight, ftMin, ftMax) then&lt;br /&gt;
        regularValid = true&lt;br /&gt;
        -- If regular is imperial, converted should be metric&lt;br /&gt;
        convertedValid = validateMetric(convertedHeight, metricParam, cmMin, cmMax)&lt;br /&gt;
    elseif validateMetric(regularHeight, metricParam, cmMin, cmMax) then&lt;br /&gt;
        regularValid = true&lt;br /&gt;
        -- If regular is metric, converted should be imperial&lt;br /&gt;
        convertedValid = validateImperial(convertedHeight, ftMin, ftMax)&lt;br /&gt;
    end&lt;br /&gt;
    &lt;br /&gt;
    -- If validation failed, add error category&lt;br /&gt;
    if not regularValid or not convertedValid then&lt;br /&gt;
        if catParam ~= &amp;#039;&amp;#039; then&lt;br /&gt;
            -- Create dynamic error message based on current ranges&lt;br /&gt;
            local ftMinFeet = math.floor(ftMin)&lt;br /&gt;
            local ftMinInches = math.floor((ftMin - ftMinFeet) * 100 + 0.5)&lt;br /&gt;
            local ftMaxFeet = math.floor(ftMax)&lt;br /&gt;
            local ftMaxInches = math.floor((ftMax - ftMaxFeet) * 100 + 0.5)&lt;br /&gt;
            &lt;br /&gt;
            local errorMsg = string.format(&lt;br /&gt;
                &amp;#039;&amp;lt;span style=&amp;quot;color:#d33&amp;quot;&amp;gt;Height format error: The height values or format are not within acceptable ranges. Height must be between %d\&amp;#039;%d&amp;quot; and %d\&amp;#039;%d&amp;quot; (%d–%d cm).&amp;lt;/span&amp;gt;&amp;#039;,&lt;br /&gt;
                ftMinFeet, ftMinInches, ftMaxFeet, ftMaxInches, cmMin, cmMax&lt;br /&gt;
            )&lt;br /&gt;
            &lt;br /&gt;
            mw.addWarning(errorMsg)&lt;br /&gt;
            return &amp;#039;[[Category:&amp;#039; .. catParam .. &amp;#039;]]&amp;#039;&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
    &lt;br /&gt;
    -- All checks passed, return empty&lt;br /&gt;
    return &amp;#039;&amp;#039;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return p&lt;/div&gt;</summary>
		<author><name>imported&gt;S.A. Julio</name></author>
	</entry>
</feed>