Jump to content
Toggle menu
  • 8 articles
  • 75 files
  • 7 users
  • 37.8K edits
Neodyland Wiki
Toggle preferences menu
Toggle personal menu
Not logged in
Your IP address will be publicly visible if you make any edits.

Module:Clickable button: Difference between revisions

From Neodyland Wiki
No edit summary
import from module:clickable button/sandbox that'll likely be merged into module:clickable button
Line 1: Line 1:
-- This module implements {{blue button}}.
-- [[w:en:Module:Clickable button]]
 
-- Builds Codex's button component. See [[wmdoc:codex/latest]].
local yesno = require('Module:Yesno')
-- Implements [[Template:Clickable button]] and others.
local delink = require('Module:Delink')._delink
-- Wiki's [[Special:Version]] must have @wikimedia/codex and its dependences.
-- To add icons: [[Template:Clickable button/styles.css]].


local p = {}
local p = {}


function p.main(frame)
function p.main(frame)
local getArgs = require('Module:Arguments').getArgs
local args = require('Module:Arguments').getArgs(frame, {
local args = getArgs(frame)
wrappers = 'Template:Clickable button', 'Template:Clickable button/sandbox',
return p._main(args)
'Template:Cdx-button'})
 
-- Return empty string if no arguments were supplied
local hasInput = false
for k, v in pairs(args) do
if v and v ~= "" then
hasInput = true
break
end
end
if not hasInput then
return ''
end
 
return frame:extensionTag(
'templatestyles', '', { src = 'Template:Clickable button/styles.css' }
) .. p.luaMain(args)
end
end


function p._main(args)
function p.luaMain(args)
-- If first arg or a url is not provided,
 
-- but we have a second arg, make a button.
-- Backward compatibility:
-- Otherwise, return nothing.
 
args.originalInput = args[1]
-- @TODO: In future, can format any wikilinks removing underscores,
args[1] = delink({args[1]})
--       URL-coding using [[Module:Format link]] and [[Module:Urldecode]]
if args[1] == "" then
--        like in [[Template:Format linkr]]
args[1] = nil
-- It's weird that we may make a link a label above, but if we truly only got 1=,
-- then that would mean it's intentional to make both the link and label the same.
-- Ensure label is set from priority: label= > 2= > 1=
args.label = args.label or args[2] or args[1]
 
--Disable if link=no or disabled=1
args.disabled = (args.link == "no") or (args.disabled == "1")
 
args.link = args.link or args[1]
 
-- Remove positional args after assigning
args[1] = nil
args[2] = nil
 
--[[ Remove links if disabled
if args.disabled then
args.link = nil
args.url = nil
end ]]
 
-- Make aria-disabled=true later on if no link
if args.link and args.link ~= "no" then
args.ariaDisabled = false
else
-- If no link whatsoever, make dummy button.
-- But for accessibility, ARIA must know it won't do anything.
args.ariaDisabled = true
-- Option to forcefully disable dummy buttons.
-- args.disabled = true
end
end
-- Normalize ARIA label keys
    args.aria_label = args.aria_label or args['aria-label'] or args.arialabel


if not args[1] and not args.url then
-- Determine action from old parameters color/class
if args[2] then
local color = type(args.color) == 'string' and args.color:lower()
p.nolink = true
local class = type(args.class) == 'string' and args.class:lower()
else
if (color == "blue"
return ''
or color == "green"
end
or class == 'ui-button-green'
or class == 'ui-button-blue'
or class == 'mw-ui-progressive'
or class == 'progressive') then
args.action = "progressive"
elseif (color == "red"
    or class == 'ui-button-red'
or class == 'mw-ui-destructive'
or class == 'destructive') then
args.action = "destructive"
end
end


local data = p.makeLinkData(args)
local data = p.makeLinkData(args)
local link = p.renderLink(args.originalInput, data)
 
local trackingCategories = p.renderTrackingCategories(args)
-- Error if ARIA label required and not provided
return link .. trackingCategories
if data == 'error-no-aria' then
return '<strong class="error">Clickable button error: A button without' ..
' a visible label needs an [[WAI-ARIA|ARIA]] label, please define it using "' ..
'aria-label".</strong>'
end
 
return p.renderLink(data)
end
end


-- Constructs attributes for the HTML elements.
function p.makeLinkData(args)
function p.makeLinkData(args)
local data = {}
local data = {}


-- Get the link and display values,
-- Decide link vs. url vs. none
-- and find whether we are outputting
-- URL has priority over link if both provided. Also, clean URL
-- a wikilink or a URL.
if args.url then
if args.url then
data.isUrl = true
data.isUrl = true
data.link = args.url
-- Clean up URL
if args[1] then
local label
data.display = args[1]
data.url, label = p.url(args.url, args.label)
elseif args[2] then
-- Make pretty link label using the URL if no label
data.display = args[2]
data.label = args.label or label
else
elseif args.link then
data.display = args.url
p.urlisdisplay = true
end
else
data.isUrl = false
data.isUrl = false
p.urlisdisplay = false
data.link = args.link
data.link = args[1]
data.label = args.label
if args[2] then
elseif not args.url and not args.link then
data.display = args[2]
-- Dummy button, no link at all
else
data.label = args.label
data.display = args[1]
end
if args[1] and args[1]:find('http') then
data.isUrl = true
end
end
end
-- Make sure if icon-only button or no label at all button (i.e. dummy button) it has aria-label else error.
if yesno(args.link) == false then
if (not data.label
p.nolink = true
and not args.aria_label
and not args.disabled
and not args.ariaDisabled) then
return 'error-no-aria'
end
end


-- Colours
-- Classes
-- For the merge with {{clickable button}}
local class = type(args.class) == 'string' and args.class:lower()
local colour = args.color and args.color:lower()
data.classes = {'cdx-button', 'cdx-button--fake-button'}
local action = type(args.action) == 'string' and args.action:lower() or 'default'
local weight = type(args.weight) == 'string' and args.weight:lower() or 'normal'
local size  = type(args.size) == 'string' and args.size:lower() or 'medium'
 
table.insert(data.classes, 'cdx-button--action-' .. action)
table.insert(data.classes, 'cdx-button--weight-' .. weight)
table.insert(data.classes, 'cdx-button--size-' .. size)


-- Classes
-- Disabled state
local class = args.class and args.class:lower()
data.disabled = args.disabled
data.classes = {}
if data.disabled then
if class == 'ui-button-green'
table.insert(data.classes, 'cdx-button--fake-button--disabled')
or class == 'ui-button-blue'
or class == 'ui-button-red'
then
table.insert(
data.classes,
'submit ui-button ui-widget ui-state-default ui-corner-all'
.. ' ui-button-text-only ui-button-text'
)
else
else
table.insert(data.classes, 'mw-ui-button')
table.insert(data.classes, 'cdx-button--fake-button--enabled')
end
end
 
--If class is unset,
-- Icon
--then let color determine class
local icon = type(args.icon) == 'string' and args.icon:lower()
if not class then
if icon then
if colour == 'blue' then
data.iconSpan = mw.html.create('span')
class = 'mw-ui-progressive'
:addClass('cdx-button__icon cdx-demo-css-icon--' .. icon)
elseif colour == 'red' then
:attr('aria-hidden', 'true')
class = 'mw-ui-destructive'
end
elseif colour == 'green' then
 
class = 'mw-ui-constructive'
data.aria_label = args.aria_label
end
data.ariaDisabled = args.ariaDisabled
 
return data
end
 
function p.renderLink(data)
-- Build span tag
local span = mw.html.create('span')
for _, class in ipairs(data.classes or {}) do
span:addClass(class)
end
span:attr('role', 'button')
if data.aria_label then
span:attr('aria-label', data.aria_label)
end
-- ARIA disabled attribute for disabled/no-link/dummy buttons
if data.disabled or data.ariaDisabled then
span:attr('aria-disabled', 'true')
elseif data.disabled == false then
span:attr('aria-disabled', 'false')
end
if data.iconSpan then
span:node(data.iconSpan)
end
if data.label then
span:wikitext(data.label)
end
 
local display = tostring(span)
 
if data.disabled then
return display
end
end
if data.isUrl then
if class then
-- mw.logObject(data)
table.insert(data.classes, class)
return string.format('<span class="plainlinks">[%s %s]</span>', data.url, display)
elseif data.isUrl == false then
return string.format('[[%s|%s]]', data.link, display)
else
-- No url/link provided
return display
end
end
end


-- Styles
-- Cleans up and validates a URL.
do
-- Copied from [[en:Module:URL]] with minor modifications.
--[[
 
-- Check whether we are on the same page as we have specified in
local function safeUri(s)
-- args[1], but not if we are using a URL link, as then args[1] is only
local success, uri = pcall(function()
-- a display value. If we are currently on the page specified in
return mw.uri.new(s)
-- args[1] make the button colour darker so that it stands out from
end)
-- other buttons on the page.
if success then
--]]
return uri
local success, linkTitle, currentTitle
if not data.isUrl then
currentTitle = mw.title.getCurrentTitle()
success, linkTitle = pcall(mw.title.new, args[1])
elseif p.urlisdisplay then
currentTitle = mw.title.getCurrentTitle()
end
if success
and linkTitle
and mw.title.equals(currentTitle, linkTitle)
and not p.urlisdisplay
then
if class == 'ui-button-blue'
or class == 'mw-ui-progressive'
or class == 'mw-ui-constructive'
then
data.backgroundColor = '#2962CB'
data.color = '#fff'
elseif class == 'ui-button-green' then
data.backgroundColor = '#008B6D'
elseif class == 'ui-button-red' or class == 'mw-ui-destructive' then
data.backgroundColor = '#A6170F'
else
data.backgroundColor = '#CCC'
data.color = '#666'
end
elseif p.urlisdisplay then
data.dummyLink = tostring(currentTitle)
end
-- Add user-specified styles.
data.style = args.style
end
end
return data
end
end


function p.renderLink(originalInput, data)
local function extractUrl(extract)
-- Render the display span tag.
local url = extract
local display
url = mw.ustring.gsub(url, '^[Hh][Tt][Tt][Pp]([Ss]?):(/?)([^/])',
do
  'http%1://%3')
local displaySpan = mw.html.create('span')
local uri = safeUri(url);
for i, class in ipairs(data.classes or {}) do
if uri and uri.host then
displaySpan:addClass(class)
return url
end
end
end
function p._url(url, text)
url = mw.text.trim(url or '')
text = mw.text.trim(text or '')
if url == '' then
return text
end


displaySpan
-- If the URL contains any unencoded spaces, encode them, because MediaWiki will otherwise interpret a space as the end of the URL.
:css{
url = mw.ustring.gsub(url, '%s', function(s) return mw.uri.encode(s,
['background-color'] = data.backgroundColor,
  'PATH')
color = data.color
  end)
}
 
if data.style then
-- If there is an empty query string or fragment id, remove it as it will cause mw.uri.new to throw an error
displaySpan:cssText(data.style)
url = mw.ustring.gsub(url, '#$', '')
end
url = mw.ustring.gsub(url, '%?$', '')
displaySpan:wikitext(data.display)
 
display = tostring(displaySpan)
-- If it's an HTTP[S] URL without the double slash, fix it.
url = mw.ustring.gsub(url, '^[Hh][Tt][Tt][Pp]([Ss]?):(/?)([^/])', 'http%1://%3')
 
local uri = safeUri(url)
 
-- Handle URL's without a protocol and URL's that are protocol-relative,
-- e.g. www.example.com/foo or www.example.com:8080/foo, and //www.example.com/foo
if uri and (not uri.protocol or (uri.protocol and not uri.host)) and url:sub(1, 2) ~= '//' then
url = 'http://' .. url
uri = safeUri(url)
end
end


-- Render the link
if text == '' then
local link
if uri then
if originalInput and originalInput:find('|') then
if uri.path == '/' then uri.path = '' end
link = string.format('[[%s|%s]]', delink({originalInput, wikilinks = 'target'}), display)
 
elseif p.nolink then
local port = ''
if p.urlisdisplay then
if uri.port then port = ':' .. uri.port end
link = string.format('[[%s|%s]]', data.dummyLink, display)
 
else
text = mw.ustring.lower(uri.host or '') .. port .. (uri.relativePath or '')
link = string.format('%s', display)
 
end
-- Add <wbr> before _/.-# sequences
else
text = mw.ustring.gsub(text, "(/+)", "<wbr/>%1") -- This entry MUST be the first. "<wbr/>" has a "/" in it, you know.
if data.isUrl then
text = mw.ustring.gsub(text, "(%.+)", "<wbr/>%1")
link = string.format('[%s %s]', data.link, display)
-- text = mw.ustring.gsub(text,"(%-+)","<wbr/>%1") -- DISABLED for now
else
text = mw.ustring.gsub(text, "(%#+)", "<wbr/>%1")
link = string.format('[[%s |%s]]', data.link, display)
text = mw.ustring.gsub(text, "(_+)", "<wbr/>%1")
else -- URL is badly-formed, so just display whatever was passed in
text = url
end
end
end
end


return string.format('<span class="plainlinks clickbutton">%s</span>', link)
return url, text
end
end


function p.renderTrackingCategories(args)
function p.url(URL, TEXT)
if yesno(args.category) == false then
local url = URL or ''
return ''
local text = TEXT or ''
end
url = url or extractUrl(url) or extractUrl(text) or ''
local class = args.class and args.class:lower()
-- Strip out HTML tags and [ ] from URL
if class == 'ui-button-green'
url = (url or ''):gsub("<[^>]*>", ""):gsub("[%[%]]", "")
or class == 'ui-button-blue'
-- Truncate anything after a space
or class == 'ui-button-red'
url = url:gsub("%%20", " "):gsub(" .*", "")
then
return p._url(url, text)
return '[[Category:Pages using old style ui-button-color]]'
else
return ''
end
end
end


return p
return p

Revision as of 19:40, 19 September 2025

Module documentation[ view · edit · history · purge ]
This documentation is transcluded from Module:Clickable button/doc.

Template:Template rating

Template:Uses templatestyles

Generates wikitext for clickable Codex button. Renders the button component from the Codex design system for Wikimedia. Includes helper functions for URL parsing and cleaning, adding tracking categories. Intended for use in templates and other modules. Implements Template:Clickable button and others. Supports legacy parameters.

  • Options to include an icon or create an icon-only button.
  • Target a URL or a wikilink
  • Set the weight, size and state of the button (enabled or disabled).
  • Dummy button creation can be disabled.

For more information on appropriate usage of UI buttons, see the Codex documentation.

  • Inserts two CSS files. Module:Clickable button/styles.css is required and makes minor tweaks for word-wrapping if the visible label is too long, centering or aligning button left or right, and minimum widths as is needed for icon-only buttons and labels containing two characters or less.
  • The second CSS file, Module:Clickable button/icons.css, is prepended to the button's HTML only if an icon is used.
  • Supports legacy parameters from previous templates.

Usage in wikitext

Some arguments are case-insensitive.

{{#invoke:Clickable button|main
| 1        = <!-- Alias for wikilink -->
| 2        = <!-- Alias for label -->
| label    = <!-- Button visible text label -->
| link     = <!-- Target wikilink -->
| url      = <!-- Target external URL -->
<!-- Inputs action, weight, size, and icon are case-insensitive -->
| action   = <!-- progressive | destructive | default: default. -->
| weight   = <!-- primary | quiet | default: normal. -->
| size     = <!-- small | large | default: medium. Automatically chooses size based on line-height and device. -->
| icon     = <!-- Name of icon, stored in [[Module:Clickable button/icons.css]] e.g., search  -->
| disabled = <!-- `true` or any other true value like `1` or `yes`. -->
| aria-label = <!-- [[w:ARIA]] label for accessibility DOM tree. -->
| nocat      = <!-- `true` to not auto-categorize. -->
<!-- Others -->
| category = <!-- Category name e.g., Category:Name or Name or [[Category:Name]] -->
| class    = <!-- Custom CSS class without quotation marks -->
| style    = <!-- Custom CSS styling without quotation marks -->
<!-- Legacy arguments -->
| color    = <!-- blue | red --> 
}}

Usage in other modules

Ensure you know what to expect from the function you call from another module.

  • function p.main(frame) emits TemplateStyles for the CSS files with the wikitext, and pre-processes the arguments in a frame using Module:Arguments, e.g. ignore blank values'', and trim trailing whitespace.
  • function p._main(arguments) Parses the arguments such as lowercase appropriate arguments, account for use of legacy parameters and decides whether aria-disabled should be true.
  • function p.url(url, [label]) is available, not for button creation, but as an adaption of Module:URL to clean and normalise a URL string and optionally generate a label.
  • The module's other functions, such as makeLinkData() and renderLink(), are localised/local to the module and would need to be made global first to be accessible to other modules.

To call p.main() for example, use:

local createButton = require( 'Module:Clickable button/sandbox' )
buttonWikitext = createButton.main( {
    link = 'South Africa',
    label = 'Go to South Africa',
    action = 'progressive'
    weight = 'default',
    size = 'medium',
    icon = 'link-external',
} ) 
return buttonWikitext

and the value of buttonWikitext would be:

<<templatestyles src="Module:Cdx-button/styles.css" /><templatestyles src="Module:Cdx-button/icons.css" /><span class="cdx-button cdx-button--fake-button cdx-button--action-progressive cdx-button--weight-quiet cdx-button--size-medium" role="button" aria-disabled="false"><span class="cdx-button__icon cdx-demo-css-icon--link-external" aria-hidden="true"></span>Go to South Africa</span>

Function _main would output:

<span class="cdx-button cdx-button--fake-button cdx-button--action-progressive cdx-button--weight-quiet cdx-button--size-medium" role="button" aria-disabled="false"><span class="cdx-button__icon cdx-demo-css-icon--link-external" aria-hidden="true"></span>Go to South Africa</span>

As a result, unless a CSS file is added to give the appropriate class an icon, the icon will not render.

Implementation

Length of visible label

See the Codex button component documentation.


-- [[w:en:Module:Clickable button]]
-- Builds Codex's button component. See [[wmdoc:codex/latest]].
-- Implements [[Template:Clickable button]] and others.
-- Wiki's [[Special:Version]] must have @wikimedia/codex and its dependences.
-- To add icons: [[Template:Clickable button/styles.css]].

local p = {}

function p.main(frame)
	local args = require('Module:Arguments').getArgs(frame, {
		wrappers = 'Template:Clickable button', 'Template:Clickable button/sandbox',
		'Template:Cdx-button'})

	-- Return empty string if no arguments were supplied
	local hasInput = false
	for k, v in pairs(args) do
		if v and v ~= "" then
			hasInput = true
			break
		end
	end
	if not hasInput then
		return ''
	end

	return frame:extensionTag(
		'templatestyles', '', { src = 'Template:Clickable button/styles.css' }
		) .. p.luaMain(args)
end

function p.luaMain(args)

	-- Backward compatibility:

	-- @TODO: In future, can format any wikilinks removing underscores,
	--        URL-coding using [[Module:Format link]] and [[Module:Urldecode]]
	--        like in [[Template:Format linkr]]
	-- It's weird that we may make a link a label above, but if we truly only got 1=,
	-- then that would mean it's intentional to make both the link and label the same.
	-- Ensure label is set from priority: label= > 2= > 1=
	args.label = args.label or args[2] or args[1]

	--Disable if link=no or disabled=1
	args.disabled = (args.link == "no") or (args.disabled == "1")

	args.link = args.link or args[1]

	-- Remove positional args after assigning
	args[1] = nil
	args[2] = nil

	--[[ Remove links if disabled
	if args.disabled then
		args.link = nil
		args.url = nil 
	end ]]

	-- Make aria-disabled=true later on if no link
	if args.link and args.link ~= "no" then
		args.ariaDisabled = false
	else
		-- If no link whatsoever, make dummy button.
		-- But for accessibility, ARIA must know it won't do anything.
		args.ariaDisabled = true
		-- Option to forcefully disable dummy buttons.
		-- args.disabled = true
	end
	
	-- Normalize ARIA label keys
    args.aria_label = args.aria_label or args['aria-label'] or args.arialabel

	-- Determine action from old parameters color/class
	local color = type(args.color) == 'string' and args.color:lower()
	local class = type(args.class) == 'string' and args.class:lower()
	if (color == "blue"
		or color == "green"
		or class == 'ui-button-green'
		or class == 'ui-button-blue'
		or class == 'mw-ui-progressive'
		or class == 'progressive') then
		args.action = "progressive"
	elseif (color == "red"
		    or class == 'ui-button-red'
			or class == 'mw-ui-destructive'
			or class == 'destructive') then
		args.action = "destructive"
	end

	local data = p.makeLinkData(args)

	-- Error if ARIA label required and not provided
	if data == 'error-no-aria' then
		return '<strong class="error">Clickable button error: A button without' ..
			' a visible label needs an [[WAI-ARIA|ARIA]] label, please define it using "' ..
			'aria-label".</strong>'
	end

	return p.renderLink(data)
end

-- Constructs attributes for the HTML elements.
function p.makeLinkData(args)
	local data = {}

	-- Decide link vs. url vs. none
	-- URL has priority over link if both provided. Also, clean URL
	if args.url then
		data.isUrl = true
		-- Clean up URL
		local label
		data.url, label = p.url(args.url, args.label)
		-- Make pretty link label using the URL if no label
		data.label = args.label or label
	elseif args.link then
		data.isUrl = false
		data.link = args.link
		data.label = args.label
	elseif not args.url and not args.link then
		-- Dummy button, no link at all
		data.label = args.label
	end
	-- Make sure if icon-only button or no label at all button (i.e. dummy button) it has aria-label else error.
	if (not data.label
		and not args.aria_label
		and not args.disabled
		and not args.ariaDisabled) then
		return 'error-no-aria'
	end

	-- Classes
	local class = type(args.class) == 'string' and args.class:lower()
	data.classes = {'cdx-button', 'cdx-button--fake-button'}
	local action = type(args.action) == 'string' and args.action:lower() or 'default'
	local weight = type(args.weight) == 'string' and args.weight:lower() or 'normal'
	local size   = type(args.size) == 'string' and args.size:lower() or 'medium'

	table.insert(data.classes, 'cdx-button--action-' .. action)
	table.insert(data.classes, 'cdx-button--weight-' .. weight)
	table.insert(data.classes, 'cdx-button--size-' .. size)

	-- Disabled state
	data.disabled = args.disabled
	if data.disabled then
		table.insert(data.classes, 'cdx-button--fake-button--disabled')
	else
		table.insert(data.classes, 'cdx-button--fake-button--enabled')
	end

	-- Icon
	local icon = type(args.icon) == 'string' and args.icon:lower()
	if icon then
		data.iconSpan = mw.html.create('span')
			:addClass('cdx-button__icon cdx-demo-css-icon--' .. icon)
			:attr('aria-hidden', 'true')
	end

	data.aria_label = args.aria_label
	data.ariaDisabled = args.ariaDisabled

	return data
end

function p.renderLink(data)
	-- Build span tag
	local span = mw.html.create('span')
	for _, class in ipairs(data.classes or {}) do
		span:addClass(class)
	end
	span:attr('role', 'button')
	if data.aria_label then
		span:attr('aria-label', data.aria_label)
	end
	-- ARIA disabled attribute for disabled/no-link/dummy buttons
	if data.disabled or data.ariaDisabled then
		span:attr('aria-disabled', 'true')
	elseif data.disabled == false then
		span:attr('aria-disabled', 'false')
	end
	if data.iconSpan then
		span:node(data.iconSpan)
	end
	if data.label then
		span:wikitext(data.label)
	end

	local display = tostring(span)

	if data.disabled then
		return display
	end
	if data.isUrl then
		-- mw.logObject(data)
		return string.format('<span class="plainlinks">[%s %s]</span>', data.url, display)
	elseif data.isUrl == false then
		return string.format('[[%s|%s]]', data.link, display)
	else
		-- No url/link provided
		return display
	end
end

-- Cleans up and validates a URL.
-- Copied from [[en:Module:URL]] with minor modifications.

local function safeUri(s)
	local success, uri = pcall(function()
		return mw.uri.new(s)
	end)
	if success then
		return uri
	end
end

local function extractUrl(extract)
	local url = extract
		url = mw.ustring.gsub(url, '^[Hh][Tt][Tt][Pp]([Ss]?):(/?)([^/])',
			  'http%1://%3')
		local uri = safeUri(url);
		if uri and uri.host then
			return url
		end
end

function p._url(url, text)
	url = mw.text.trim(url or '')
	text = mw.text.trim(text or '')

	if url == '' then
		return text
	end

	-- If the URL contains any unencoded spaces, encode them, because MediaWiki will otherwise interpret a space as the end of the URL.
	url = mw.ustring.gsub(url, '%s', function(s) return mw.uri.encode(s,
		  	'PATH')
		  end)

	-- If there is an empty query string or fragment id, remove it as it will cause mw.uri.new to throw an error
	url = mw.ustring.gsub(url, '#$', '')
	url = mw.ustring.gsub(url, '%?$', '')

	-- If it's an HTTP[S] URL without the double slash, fix it.
	url = mw.ustring.gsub(url, '^[Hh][Tt][Tt][Pp]([Ss]?):(/?)([^/])', 'http%1://%3')

	local uri = safeUri(url)

	-- Handle URL's without a protocol and URL's that are protocol-relative,
	-- e.g. www.example.com/foo or www.example.com:8080/foo, and //www.example.com/foo
	if uri and (not uri.protocol or (uri.protocol and not uri.host)) and url:sub(1, 2) ~= '//' then
		url = 'http://' .. url
		uri = safeUri(url)
	end

	if text == '' then
		if uri then
			if uri.path == '/' then uri.path = '' end

			local port = ''
			if uri.port then port = ':' .. uri.port end

			text = mw.ustring.lower(uri.host or '') .. port .. (uri.relativePath or '')

			-- Add <wbr> before _/.-# sequences
			text = mw.ustring.gsub(text, "(/+)", "<wbr/>%1") -- This entry MUST be the first. "<wbr/>" has a "/" in it, you know.
			text = mw.ustring.gsub(text, "(%.+)", "<wbr/>%1")
			-- text = mw.ustring.gsub(text,"(%-+)","<wbr/>%1") 	-- DISABLED for now
			text = mw.ustring.gsub(text, "(%#+)", "<wbr/>%1")
			text = mw.ustring.gsub(text, "(_+)", "<wbr/>%1")
		else -- URL is badly-formed, so just display whatever was passed in
			text = url
		end
	end

	return url, text
end

function p.url(URL, TEXT)
	local url = URL or ''
	local text = TEXT or ''
	url = url or extractUrl(url) or extractUrl(text) or ''
	-- Strip out HTML tags and [ ] from URL
	url = (url or ''):gsub("<[^>]*>", ""):gsub("[%[%]]", "")
	-- Truncate anything after a space
	url = url:gsub("%%20", " "):gsub(" .*", "")
	return p._url(url, text)
end

return p
Cookies help us deliver our services. By using our services, you agree to our use of cookies.