Module:List and Module:Delink: Difference between pages

From Frontierpedia, the Microsoft Agent encyclopedia
(Difference between pages)
en>Mr. Stradivarius
(add support for horizontal numbered lists)
 
en>Mr. Stradivarius
(html comments are probably mostly removed by the parser anyway - testing)
 
Line 1: Line 1:
-- This module outputs different kinds of lists. At the moment, bulleted, unbulleted, horizontal, and numbered lists are supported.
-- This module de-links most wikitext.


local p = {}
p = {}


local function getListItem(data, style, itemStyle)
local function delinkReversePipeTrick(s)
if not data then
    if mw.ustring.match(s, "^%[%[|.*|") then -- Check for multiple pipes.
return nil
        return s
end
    else
if style or itemStyle then
        return mw.ustring.match(s, "%[%[|(.*)%]%]")
style = style or ''
    end
itemStyle = itemStyle or ''
return mw.ustring.format(
'<li style="%s%s">%s</li>',
style, itemStyle, data
)
else
return mw.ustring.format(
'<li>%s</li>',
data
)
end
end
end


local function getArgNums(args)
local function delinkPipeTrick(s)
-- Returns an array containing the keys of all positional arguments
    local linkarea, display = "", ""
-- that contain data (i.e. non-whitespace values).
    -- We need to deal with colons, brackets, and commas, per [[Help:Pipe trick]].
local nums = {}
   
for k, v in pairs(args) do
    -- First, remove the text before the first colon, if any.
if type(k) == 'number' and
    if mw.ustring.match(s, ":") then
k >= 1 and
        s = mw.ustring.match(s, "%[%[.-:(.*)|%]%]")
math.floor(k) == k and
    -- If there are no colons, grab all of the text apart from the square brackets and the pipe.
mw.ustring.match(v, '%S') then
    else
table.insert(nums, k)
        s = mw.ustring.match(s, "%[%[(.*)|%]%]")
end
    end
end
   
table.sort(nums)
    -- Next up, brackets and commas.
return nums
    if mw.ustring.match(s, "%(.-%)$") then -- Brackets trump commas.
        s = mw.ustring.match(s, "(.-) ?%(.-%)$")
    elseif mw.ustring.match(s, ",") then -- If there are no brackets, display only the text before the first comma.
        s = mw.ustring.match(s, "(.-),.*$")
    end
    return s
end
end


local function getClass(listType, class)
local function delinkWikilink(s)
local classes = {}
    -- Deal with the reverse pipe trick.
if listType == 'horizontal' or listType == 'horizontal_numbered' then
    if mw.ustring.match(s, "%[%[|") then
table.insert(classes, 'hlist')
        return delinkReversePipeTrick(s)
elseif listType == 'unbulleted' then
    end
table.insert(classes, 'plainlist')
   
end
    -- Check for bad titles. To do this we need to find the
table.insert(classes, class)
    -- title area of the link, i.e. the part before any pipes.
local ret
    local titlearea
if #classes == 0 then
    if mw.ustring.match(s, "|") then -- Find if we're dealing with a piped link.
return nil
        titlearea = mw.ustring.match(s, "^%[%[(.-)|.*%]%]")
end
    else
return mw.ustring.format(' class="%s"', table.concat(classes, ' '))
        titlearea = mw.ustring.match(s, "^%[%[(.-)%]%]")
    end
    -- If the title area is not a valid title, return the whole string.
    -- Use pcall in case we're over the expensive functions limit.
    local goodcall, title = pcall(mw.title.new, titlearea, "")
    if not (goodcall and title) then
        return s
    end
    -- Check for characters that are allowed in titles but not in wikilinks.
    local other_invalid_link_strings = { '&#x0000;' }
    for i,v in ipairs(other_invalid_link_strings) do
        if mw.ustring.match(titlearea, v) then
            return s
        end
    end
   
    -- Check for categories, interwikis, and files.
    local colonprefix = mw.ustring.match(s, "%[%[(.-):.*%]%]") or "" -- Get the text before the first colon.
    if mw.language.isKnownLanguageTag(colonprefix)
    or mw.ustring.match(colonprefix, "^[Cc]ategory$")
    or mw.ustring.match(colonprefix, "^[Ff]ile$")
    or mw.ustring.match(colonprefix, "^[Ii]mage$") then
        return ""
    end
   
    -- Remove the colon if the link is using the [[Help:Colon trick]].
    if mw.ustring.match(s, "%[%[:") then
        s = "[[" .. mw.ustring.match(s, "%[%[:(.*%]%])")
    end
   
    -- Deal with links using the [[Help:Pipe trick]].
    if mw.ustring.match(s, "^%[%[[^|]*|%]%]") then
        return delinkPipeTrick(s)
    end
   
    -- Find the display area of the wikilink
    local display
    if mw.ustring.match(s, "|") then -- Find if we're dealing with a piped link.
        display = mw.ustring.match(s, "^%[%[.-|(.+)%]%]")
    else
        display = mw.ustring.match(s, "^%[%[(.-)%]%]")
    end
 
    return display
end
end


local function getStyle(listType, indent, style)
local function delinkURL(s)
local styles = {}
    -- Assume we have already delinked internal wikilinks, and that
if listType == 'horizontal' then
    -- we have been passed some text between two square brackets [foo].
indent = indent and tonumber(indent)
   
indent = tostring((indent and indent * 1.6) or 0)
    -- Check if the text has a valid URL prefix and at least one valid URL character.
table.insert(styles, 'margin-left: ' .. indent .. 'em;')
    local valid_url_prefixes = {"//", "http://", "https://", "ftp://", "gopher://", "mailto:", "news:", "irc://"}  
end
    local url_prefix
table.insert(styles, style)
    for i,v in ipairs(valid_url_prefixes) do
if #styles == 0 then
        if mw.ustring.match(s, '^%[' .. v ..'[^"%s].*%]' ) then
return nil
            url_prefix = v
end
            break
return mw.ustring.format(' style="%s"', table.concat(styles, ' '))
        end
    end
   
    -- Get display text
    if not url_prefix then
        return s
    else
        s = mw.ustring.match(s, "^%[" .. url_prefix .. "(.*)%]") -- Grab all of the text after the URL prefix and before the final square bracket.
        s = mw.ustring.match(s, '^.-(["<> ].*)') or "" -- Grab all of the text after the first URL separator character ("<> ).
        s = mw.ustring.match(s, "^%s*(%S.*)$") or "" -- If the separating character was a space, trim it off.
        return s
    end
end
end


function p.makeList(listType, args)
local function delinkLinkClass(s, pattern, delinkFunction)
-- This is the main function to be called from other Lua modules.
    if not type(s) == "string" then
-- First, get the list items.
        error("Attempt to de-link non-string input.", 2)
local listItems = {}
    end
local argNums = getArgNums(args)
    if not ( type(pattern) == "string" and mw.ustring.sub(pattern, 1, 1) == "^" ) then
for i, num in ipairs(argNums) do
        error('Invalid pattern detected. Patterns must begin with "^".', 2)
local item = getListItem(
    end
args[num],
    -- Iterate over the text string, and replace any matched text. using the  
args.item_style or args.li_style, -- li_style is included for backwards compatibility. item_style was included to be easier to understand for non-coders.
    -- delink function. We need to iterate character by character rather
args['item_style' .. tostring(num)] or args['li_style' .. tostring(num)]
    -- than just use gsub, otherwise nested links aren't detected properly.
)
    local result = ""
table.insert(listItems, item)
    while mw.ustring.len(s) > 0 do
end
        -- Replace text using one iteration of gsub.
if #listItems == 0 then
        s = mw.ustring.gsub(s, pattern, delinkFunction, 1)
return ''
        -- Append the left-most character to the result string.
end
        result = result .. mw.ustring.sub(s, 1, 1)
-- Check if we need a ul tag or an ol tag, and get the start and type attributes for ordered lists.
        s = mw.ustring.sub(s, 2, -1)
local listTag = 'ul'
    end
local startAttr, typeAttr
    return result
if listType == 'numbered' or listType == 'horizontal_numbered' then
listTag = 'ol'
startAttr = args.start
if startAttr then
startAttr = ' start="' .. startAttr .. '"'
end
typeAttr = args.type
if typeAttr then
typeAttr = ' type="' .. typeAttr .. '"'
end
end
startAttr = startAttr or ''
typeAttr = typeAttr or ''
-- Get the classes and styles and output the list.
local class = getClass(listType, args.class) or ''
local style = getStyle(listType, args.indent, args.style) or ''
local list_style = args.list_style or args.ul_style -- ul_style is included for backwards compatibility, and will work even with ordered lists.
list_style = list_style and (' style="' .. list_style .. '"') or ''
return mw.ustring.format(
'<div%s%s><%s%s%s%s>%s</%s></div>',
class, style, listTag, startAttr, typeAttr, list_style, table.concat(listItems), listTag
)
end
end


local function makeWrapper(listType)
local function _delink(args)
return function(frame)
    local text = args[1] or ""
local origArgs
--    text = mw.ustring.gsub(text, "<!%-%-.-%-%->", "") -- Remove html comments.
if frame == mw.getCurrentFrame() then
    text = delinkLinkClass(text, "^%[%[.-%]%]", delinkWikilink) -- De-link wikilinks.
origArgs = frame:getParent().args
    text = delinkLinkClass(text, "^%[.-%]", delinkURL) -- De-link URLs.
for k, v in pairs(frame.args) do
    text = mw.ustring.gsub(text, "%s+", " ") -- Remove extra whitespace.
origArgs = frame.args
    return text
break
end
else
origArgs = frame
end
local args = {}
for k, v in pairs(origArgs) do
if type(k) == 'number' or v ~= '' then
args[k] = v
end
end
return p.makeList(listType, args)
end
end
end


local funcNames = {'bulleted', 'unbulleted', 'horizontal', 'numbered', 'horizontal_numbered'}
function p.delink(frame)
 
    local args
for _, funcName in ipairs(funcNames) do
    if frame == mw.getCurrentFrame() then
p[funcName] = makeWrapper(funcName)
        -- We're being called via #invoke. If the invoking template passed any args, use
        -- them. Otherwise, use the args that were passed into the template.
        args = frame:getParent().args
        for k, v in pairs(frame.args) do
            args = frame.args
            break
        end
    else
        -- We're being called from another module or from the debug console, so assume
        -- the args are passed in directly.
        args = frame
    end
    return _delink(args)
end
end


return p
return p

Revision as of 07:50, 5 April 2013

Documentation for this module may be created at Module:Delink/doc

-- This module de-links most wikitext.

p = {}

local function delinkReversePipeTrick(s)
    if mw.ustring.match(s, "^%[%[|.*|") then -- Check for multiple pipes.
        return s
    else
        return mw.ustring.match(s, "%[%[|(.*)%]%]")
    end
end

local function delinkPipeTrick(s)
    local linkarea, display = "", ""
    -- We need to deal with colons, brackets, and commas, per [[Help:Pipe trick]].
    
    -- First, remove the text before the first colon, if any.
    if mw.ustring.match(s, ":") then
        s = mw.ustring.match(s, "%[%[.-:(.*)|%]%]")
    -- If there are no colons, grab all of the text apart from the square brackets and the pipe.
    else
        s = mw.ustring.match(s, "%[%[(.*)|%]%]")
    end
    
    -- Next up, brackets and commas.
    if mw.ustring.match(s, "%(.-%)$") then -- Brackets trump commas.
        s = mw.ustring.match(s, "(.-) ?%(.-%)$")
    elseif mw.ustring.match(s, ",") then -- If there are no brackets, display only the text before the first comma.
        s = mw.ustring.match(s, "(.-),.*$")
    end
    return s
end

local function delinkWikilink(s)
    -- Deal with the reverse pipe trick.
    if mw.ustring.match(s, "%[%[|") then
        return delinkReversePipeTrick(s)
    end
    
    -- Check for bad titles. To do this we need to find the
    -- title area of the link, i.e. the part before any pipes.
    local titlearea
    if mw.ustring.match(s, "|") then -- Find if we're dealing with a piped link.
        titlearea = mw.ustring.match(s, "^%[%[(.-)|.*%]%]")
    else
        titlearea = mw.ustring.match(s, "^%[%[(.-)%]%]")
    end
    -- If the title area is not a valid title, return the whole string.
    -- Use pcall in case we're over the expensive functions limit.
    local goodcall, title = pcall(mw.title.new, titlearea, "")
    if not (goodcall and title) then
        return s
    end
    -- Check for characters that are allowed in titles but not in wikilinks.
    local other_invalid_link_strings = { '&#x0000;' }
    for i,v in ipairs(other_invalid_link_strings) do
        if mw.ustring.match(titlearea, v) then
            return s
        end
    end
    
    -- Check for categories, interwikis, and files.
    local colonprefix = mw.ustring.match(s, "%[%[(.-):.*%]%]") or "" -- Get the text before the first colon.
    if mw.language.isKnownLanguageTag(colonprefix)
    or mw.ustring.match(colonprefix, "^[Cc]ategory$")
    or mw.ustring.match(colonprefix, "^[Ff]ile$")
    or mw.ustring.match(colonprefix, "^[Ii]mage$") then
        return ""
    end
    
    -- Remove the colon if the link is using the [[Help:Colon trick]].
    if mw.ustring.match(s, "%[%[:") then
        s = "[[" .. mw.ustring.match(s, "%[%[:(.*%]%])")
    end
    
    -- Deal with links using the [[Help:Pipe trick]].
    if mw.ustring.match(s, "^%[%[[^|]*|%]%]") then
        return delinkPipeTrick(s)
    end
    
    -- Find the display area of the wikilink
    local display
    if mw.ustring.match(s, "|") then -- Find if we're dealing with a piped link.
        display = mw.ustring.match(s, "^%[%[.-|(.+)%]%]")
    else
        display = mw.ustring.match(s, "^%[%[(.-)%]%]")
    end

    return display
end

local function delinkURL(s)
    -- Assume we have already delinked internal wikilinks, and that
    -- we have been passed some text between two square brackets [foo].
    
    -- Check if the text has a valid URL prefix and at least one valid URL character.
    local valid_url_prefixes = {"//", "http://", "https://", "ftp://", "gopher://", "mailto:", "news:", "irc://"} 
    local url_prefix
    for i,v in ipairs(valid_url_prefixes) do
        if mw.ustring.match(s, '^%[' .. v ..'[^"%s].*%]' ) then
            url_prefix = v
            break
        end
    end
    
    -- Get display text
    if not url_prefix then
        return s
    else
        s = mw.ustring.match(s, "^%[" .. url_prefix .. "(.*)%]") -- Grab all of the text after the URL prefix and before the final square bracket.
        s = mw.ustring.match(s, '^.-(["<> ].*)') or "" -- Grab all of the text after the first URL separator character ("<> ).
        s = mw.ustring.match(s, "^%s*(%S.*)$") or "" -- If the separating character was a space, trim it off.
        return s
    end
end

local function delinkLinkClass(s, pattern, delinkFunction)
    if not type(s) == "string" then
        error("Attempt to de-link non-string input.", 2)
    end
    if not ( type(pattern) == "string" and mw.ustring.sub(pattern, 1, 1) == "^" ) then
        error('Invalid pattern detected. Patterns must begin with "^".', 2)
    end
    -- Iterate over the text string, and replace any matched text. using the 
    -- delink function. We need to iterate character by character rather 
    -- than just use gsub, otherwise nested links aren't detected properly.
    local result = ""
    while mw.ustring.len(s) > 0 do
        -- Replace text using one iteration of gsub.
        s = mw.ustring.gsub(s, pattern, delinkFunction, 1)
        -- Append the left-most character to the result string.
        result = result .. mw.ustring.sub(s, 1, 1)
        s = mw.ustring.sub(s, 2, -1)
    end
    return result
end

local function _delink(args)
    local text = args[1] or ""
--    text = mw.ustring.gsub(text, "<!%-%-.-%-%->", "") -- Remove html comments.
    text = delinkLinkClass(text, "^%[%[.-%]%]", delinkWikilink) -- De-link wikilinks.
    text = delinkLinkClass(text, "^%[.-%]", delinkURL) -- De-link URLs.
    text = mw.ustring.gsub(text, "%s+", " ") -- Remove extra whitespace.
    return text
end

function p.delink(frame)
    local args
    if frame == mw.getCurrentFrame() then
        -- We're being called via #invoke. If the invoking template passed any args, use
        -- them. Otherwise, use the args that were passed into the template.
        args = frame:getParent().args
        for k, v in pairs(frame.args) do
            args = frame.args
            break
        end
    else
        -- We're being called from another module or from the debug console, so assume
        -- the args are passed in directly.
        args = frame
    end
    return _delink(args)
end

return p