Module:title/newTitle
Jump to navigation
Jump to search
Documentation for this module may be created at Module:title/newTitle/doc
local load_module = "Module:load"
local scribunto_module = "Module:Scribunto"
local title_get_current_namespace_module = "Module:title/getCurrentNamespace"
local title_get_current_title_module = "Module:title/getCurrentTitle"
local title_get_main_page_title_module = "Module:title/getMainPageTitle"
local byte = string.byte
local find = string.find
local match = string.match
local new_title = mw.title.new
local reverse = string.reverse
local sub = string.sub
local type = type
local function get_current_title(...)
get_current_title = require(title_get_current_title_module)
return get_current_title(...)
end
local function get_main_page_title(...)
get_main_page_title = require(title_get_main_page_title_module)
return get_main_page_title(...)
end
local function php_ltrim(...)
php_ltrim = require(scribunto_module).php_ltrim
return php_ltrim(...)
end
local function php_rtrim(...)
php_rtrim = require(scribunto_module).php_rtrim
return php_rtrim(...)
end
local current_pagename
local function get_current_pagename()
-- Call mw.title.getCurrentTitle() directly rather than the modified version
-- at [[Module:title/getCurrentTitle]], as it doesn't do anything extra when
-- no fragment argument is given, so there's no point in loading it.
current_pagename, get_current_pagename = mw.title.getCurrentTitle().prefixedText, nil
return current_pagename
end
local namespace_has_subpages
local function get_namespace_has_subpages()
namespace_has_subpages, get_namespace_has_subpages = require(load_module).load_data(title_get_current_namespace_module).hasSubpages, nil
return namespace_has_subpages
end
local function split_text(text)
-- Split off the fragment.
local hash, target, fragment = find(text, "#", nil, true)
if hash then
target, fragment = sub(text, 1, hash - 1), sub(text, hash)
else
target, fragment = text, ""
end
return php_rtrim(target), fragment
end
--[==[
A modified version of {mw.title.new}:
* It is no longer possible to generate title objects for the empty string by inputting a title starting with {"#"}. Such empty string titles do not represent a valid page, and are broken in various ways (e.g. attempting to access certain keys results in an error); see [[phab:T240678]].
* There are two additional boolean flags:
** If {allowOnlyFragment} is set, string inputs starting with {#} are handled analogously to links (e.g. the input {"#foo"} returns a title object for the current page with the addition of the fragment {"foo"}, analogous to the link [[#foo]]). As a special case, the input {"#"} returns the title for the main page (see TitleValue.php).
** If {allowRelative} is set, inputs representing relative titles will work (e.g. {"/foo"} and {"../"}, analogous to the relative links [[/foo]] and [[../]]).]==]
return function(text_or_id, defaultNamespace, allowOnlyFragment, allowRelative)
-- Process relative titles in the same way as normalizeSubpageLink() in
-- Linker.php.
if (
allowRelative and
type(text_or_id) == "string" and
-- Distinguish nil and false for the purposes of calling the getter.
(namespace_has_subpages == nil and get_namespace_has_subpages() or namespace_has_subpages)
) then
-- For both kinds of relative title, `text_or_id` has to be split into
-- `target` and `fragment` (if any). `target` has to be trimmed, but
-- as an optimisation, only do a left-trim on `text_or_id`, which is
-- sufficient for the initial checks for potential relative titles, as
-- this avoids unnecessary splitting/trimming in the vast majority of
-- cases where the title won't be relative. In the cases where it is,
-- `target` will then be right-trimmed once it has been split out of
-- `text_or_id`.
text_or_id = php_ltrim(text_or_id)
local init = byte(text_or_id)
-- If the target starts with "/", it's treated as a subpage of the
-- current page. Final slashes are trimmed, but this can't affect the
-- intervening slash (e.g. "[[///]]" refers to "[[{{PAGENAME}}/]]").
if init == 0x2F then -- /
local target, fragment = split_text(text_or_id)
text_or_id, defaultNamespace = (current_pagename or get_current_pagename()) .. (match(target, "^/.*[^/]") or "/") .. fragment, nil
-- If the title starts with "../", trim it and any further "../" that
-- follow, and go up that many subpage levels. Then, treat any
-- additional text as a subpage of that page. Final slashes are trimmed.
elseif init == 0x2E and sub(text_or_id, 2, 3) == "./" then -- ../
local n, target, fragment = 4, split_text(text_or_id)
while sub(target, n, n + 2) == "../" do
n = n + 3
end
-- Retain an initial "/".
target = sub(target, n - 1)
-- Trim the relevant number of subpages from the pagename.
local pagename, i = reverse(current_pagename or get_current_pagename()), 0
for _ = 1, (n - 1) / 3 do
i = find(pagename, "/", i + 1, true)
-- Fail if there aren't enough slashes.
if not i then
return nil
end
end
-- Add the subpage text; since the intervening "/" is retained in
-- `target`, it can be trimmed along with any other final slashes
-- (e.g. [[..///]] refers to "{{BASEPAGENAME}}".)
text_or_id, defaultNamespace = reverse(sub(pagename, i + 1)) .. (match(target, "^.*[^/]") or "") .. fragment, nil
end
end
local title = new_title(text_or_id, defaultNamespace)
if not title then
return nil
elseif title.prefixedText ~= "" then
return title
-- If `prefixedText` is the empty string, the input only has a fragment.
elseif not allowOnlyFragment then
return nil
end
-- Retrieve the fragment: if it's the empty string, return a title object
-- for the main page (which is where the link [[#]] resolves); otherwise,
-- return a current title object using [[Module:title/getCurrentTitle]],
-- which sets the fragment on the returned title to the string passed in.
-- (Note that inputs like "##foo" aren't interpreted as the main page with
-- the fragment "foo"; instead, it's a fragment for the section "#foo" on
-- the current page, since "#" is itself a valid fragment character.)
local fragment = title.fragment
return fragment == "" and get_main_page_title() or get_current_title(fragment)
end