Page Menu
Home
WickedGov Phorge
Search
Configure Global Search
Log In
Files
F4123304
Title.js
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
28 KB
Referenced Files
None
Subscribers
None
Title.js
View Options
/*!
* @author Neil Kandalgaonkar, 2010
* @since 1.18
*/
/* Private members */
let
toUpperMap
;
const
mwString
=
require
(
'mediawiki.String'
),
namespaceIds
=
mw
.
config
.
get
(
'wgNamespaceIds'
),
/**
* @private
* @static
* @property {number} NS_MAIN
*/
NS_MAIN
=
namespaceIds
[
''
],
/**
* @private
* @static
* @property {number} NS_TALK
*/
NS_TALK
=
namespaceIds
.
talk
,
/**
* @private
* @static
* @property {number} NS_SPECIAL
*/
NS_SPECIAL
=
namespaceIds
.
special
,
/**
* @private
* @static
* @property {number} NS_MEDIA
*/
NS_MEDIA
=
namespaceIds
.
media
,
/**
* @private
* @static
* @property {number} NS_FILE
*/
NS_FILE
=
namespaceIds
.
file
,
/**
* @private
* @static
* @property {number} FILENAME_MAX_BYTES
*/
FILENAME_MAX_BYTES
=
240
,
/**
* @private
* @static
* @property {number} TITLE_MAX_BYTES
*/
TITLE_MAX_BYTES
=
255
,
/**
* Get the namespace id from a namespace name (either from the localized, canonical or alias
* name).
*
* Example: On a German wiki this would return 6 for any of 'File', 'Datei', 'Image' or
* even 'Bild'.
*
* @private
* @static
* @method getNsIdByName
* @param {string} ns Namespace name (case insensitive, leading/trailing space ignored)
* @return {number|boolean} Namespace id or boolean false
*/
getNsIdByName
=
function
(
ns
)
{
// Don't cast non-strings to strings, because null or undefined should not result in
// returning the id of a potential namespace called "Null:" (e.g. on null.example.org/wiki)
// Also, toLowerCase throws exception on null/undefined, because it is a String method.
if
(
typeof
ns
!==
'string'
)
{
return
false
;
}
// TODO: Should just use the local variable namespaceIds here, but it
// breaks test which modify the config
const
id
=
mw
.
config
.
get
(
'wgNamespaceIds'
)[
ns
.
toLowerCase
()
];
if
(
id
===
undefined
)
{
return
false
;
}
return
id
;
},
/**
* @private
* @method isKnownNamespace
* @param {number} namespace that may or may not exist
* @return {boolean}
*/
isKnownNamespace
=
function
(
namespace
)
{
return
namespace
===
NS_MAIN
||
mw
.
config
.
get
(
'wgFormattedNamespaces'
)[
namespace
]
!==
undefined
;
},
/**
* @private
* @method getNamespacePrefix
* @param {number} namespace that is valid and known. Callers should call
* `isKnownNamespace` before executing this method.
* @return {string}
*/
getNamespacePrefix
=
function
(
namespace
)
{
return
namespace
===
NS_MAIN
?
''
:
(
mw
.
config
.
get
(
'wgFormattedNamespaces'
)[
namespace
].
replace
(
/ /g
,
'_'
)
+
':'
);
},
rUnderscoreTrim
=
/^_+|_+$/g
,
rSplit
=
/^(.+?)_*:_*(.*)$/
,
// See MediaWikiTitleCodec.php#getTitleInvalidRegex
rInvalid
=
new
RegExp
(
'[^'
+
mw
.
config
.
get
(
'wgLegalTitleChars'
)
+
']'
+
// URL percent encoding sequences interfere with the ability
// to round-trip titles -- you can't link to them consistently.
'|%[\\dA-Fa-f]{2}'
+
// XML/HTML character references produce similar issues.
'|&[\\dA-Za-z\u0080-\uFFFF]+;'
),
// From MediaWikiTitleCodec::splitTitleString() in PHP
// Note that this is not equivalent to /\s/, e.g. underscore is included, tab is not included.
rWhitespace
=
/[ _\u00A0\u1680\u180E\u2000-\u200A\u2028\u2029\u202F\u205F\u3000]+/g
,
// From MediaWikiTitleCodec::splitTitleString() in PHP
rUnicodeBidi
=
/[\u200E\u200F\u202A-\u202E]+/g
,
/**
* Slightly modified from Flinfo. Credit goes to Lupo and Flominator.
*
* @private
* @static
* @property {Object[]} sanitationRules
*/
sanitationRules
=
[
// "signature"
{
pattern
:
/~{3}/g
,
replace
:
''
,
generalRule
:
true
},
// control characters
{
// eslint-disable-next-line no-control-regex
pattern
:
/[\x00-\x1f\x7f]/g
,
replace
:
''
,
generalRule
:
true
},
// URL encoding (possibly)
{
pattern
:
/%([\dA-Fa-f]{2})/g
,
replace
:
'% $1'
,
generalRule
:
true
},
// HTML-character-entities
{
pattern
:
/&(([\dA-Za-z\x80-\xff]+|#\d+|#x[\dA-Fa-f]+);)/g
,
replace
:
'& $1'
,
generalRule
:
true
},
// slash, colon (not supported by file systems like NTFS/Windows, Mac OS 9 [:], ext4 [/])
{
pattern
:
new
RegExp
(
'['
+
mw
.
config
.
get
(
'wgIllegalFileChars'
,
''
)
+
']'
,
'g'
),
replace
:
'-'
,
fileRule
:
true
},
// brackets, greater than
{
pattern
:
/[}\]>]/g
,
replace
:
')'
,
generalRule
:
true
},
// brackets, lower than
{
pattern
:
/[{[<]/g
,
replace
:
'('
,
generalRule
:
true
},
// everything that wasn't covered yet
{
pattern
:
new
RegExp
(
rInvalid
.
source
,
'g'
),
replace
:
'-'
,
generalRule
:
true
},
// directory structures
{
pattern
:
/^(\.|\.\.|\.\/.*|\.\.\/.*|.*\/\.\/.*|.*\/\.\.\/.*|.*\/\.|.*\/\.\.)$/g
,
replace
:
''
,
generalRule
:
true
}
],
/**
* Internal helper for #constructor and #newFromText.
*
* Based on Title.php#secureAndSplit
*
* @private
* @static
* @method parse
* @param {string} title
* @param {number} [defaultNamespace=NS_MAIN]
* @return {Object|boolean}
*/
parse
=
function
(
title
,
defaultNamespace
)
{
let
namespace
=
defaultNamespace
===
undefined
?
NS_MAIN
:
defaultNamespace
;
title
=
title
// Strip Unicode bidi override characters
.
replace
(
rUnicodeBidi
,
''
)
// Normalise whitespace to underscores and remove duplicates
.
replace
(
rWhitespace
,
'_'
)
// Trim underscores
.
replace
(
rUnderscoreTrim
,
''
);
if
(
title
.
indexOf
(
'\uFFFD'
)
!==
-
1
)
{
// Contained illegal UTF-8 sequences or forbidden Unicode chars.
// Commonly occurs when the text was obtained using the `URL` API, and the 'title' parameter
// was using a legacy 8-bit encoding, for example:
// new URL( 'https://en.wikipedia.org/w/index.php?title=Apollo%96Soyuz' ).searchParams.get( 'title' )
return
false
;
}
// Process initial colon
if
(
title
!==
''
&&
title
[
0
]
===
':'
)
{
// Initial colon means main namespace instead of specified default
namespace
=
NS_MAIN
;
title
=
title
// Strip colon
.
slice
(
1
)
// Trim underscores
.
replace
(
rUnderscoreTrim
,
''
);
}
if
(
title
===
''
)
{
return
false
;
}
// Process namespace prefix (if any)
let
m
=
title
.
match
(
rSplit
);
if
(
m
)
{
const
id
=
getNsIdByName
(
m
[
1
]
);
if
(
id
!==
false
)
{
// Ordinary namespace
namespace
=
id
;
title
=
m
[
2
];
// For Talk:X pages, make sure X has no "namespace" prefix
if
(
namespace
===
NS_TALK
&&
(
m
=
title
.
match
(
rSplit
)
)
)
{
// Disallow titles like Talk:File:x (subject should roundtrip: talk:file:x -> file:x -> file_talk:x)
if
(
getNsIdByName
(
m
[
1
]
)
!==
false
)
{
return
false
;
}
}
}
}
// Process fragment
const
i
=
title
.
indexOf
(
'#'
);
let
fragment
;
if
(
i
===
-
1
)
{
fragment
=
null
;
}
else
{
fragment
=
title
// Get segment starting after the hash
.
slice
(
i
+
1
)
// Convert to text
// NB: Must not be trimmed ("Example#_foo" is not the same as "Example#foo")
.
replace
(
/_/g
,
' '
);
title
=
title
// Strip hash
.
slice
(
0
,
i
)
// Trim underscores, again (strips "_" from "bar" in "Foo_bar_#quux")
.
replace
(
rUnderscoreTrim
,
''
);
}
// Reject illegal characters
if
(
rInvalid
.
test
(
title
)
)
{
return
false
;
}
// Disallow titles that browsers or servers might resolve as directory navigation
if
(
title
.
indexOf
(
'.'
)
!==
-
1
&&
(
title
===
'.'
||
title
===
'..'
||
title
.
indexOf
(
'./'
)
===
0
||
title
.
indexOf
(
'../'
)
===
0
||
title
.
indexOf
(
'/./'
)
!==
-
1
||
title
.
indexOf
(
'/../'
)
!==
-
1
||
title
.
slice
(
-
2
)
===
'/.'
||
title
.
slice
(
-
3
)
===
'/..'
)
)
{
return
false
;
}
// Disallow magic tilde sequence
if
(
title
.
indexOf
(
'~~~'
)
!==
-
1
)
{
return
false
;
}
// Disallow titles exceeding the TITLE_MAX_BYTES byte size limit (size of underlying database field)
// Except for special pages, e.g. [[Special:Block/Long name]]
// Note: The PHP implementation also asserts that even in NS_SPECIAL, the title should
// be less than 512 bytes.
if
(
namespace
!==
NS_SPECIAL
&&
mwString
.
byteLength
(
title
)
>
TITLE_MAX_BYTES
)
{
return
false
;
}
// Can't make a link to a namespace alone.
if
(
title
===
''
&&
namespace
!==
NS_MAIN
)
{
return
false
;
}
// Any remaining initial :s are illegal.
if
(
title
[
0
]
===
':'
)
{
return
false
;
}
return
{
namespace
:
namespace
,
title
:
title
,
fragment
:
fragment
};
},
/**
* Convert db-key to readable text.
*
* @private
* @static
* @method text
* @param {string} s
* @return {string}
*/
text
=
function
(
s
)
{
return
s
.
replace
(
/_/g
,
' '
);
},
/**
* Sanitizes a string based on a rule set and a filter
*
* @private
* @static
* @method sanitize
* @param {string} s
* @param {Array} filter
* @return {string}
*/
sanitize
=
function
(
s
,
filter
)
{
const
rules
=
sanitationRules
;
for
(
let
i
=
0
,
ruleLength
=
rules
.
length
;
i
<
ruleLength
;
++
i
)
{
const
rule
=
rules
[
i
];
for
(
let
m
=
0
,
filterLength
=
filter
.
length
;
m
<
filterLength
;
++
m
)
{
if
(
rule
[
filter
[
m
]
]
)
{
s
=
s
.
replace
(
rule
.
pattern
,
rule
.
replace
);
}
}
}
return
s
;
},
/**
* Cuts a string to a specific byte length, assuming UTF-8
* or less, if the last character is a multi-byte one
*
* @private
* @static
* @method trimToByteLength
* @param {string} s
* @param {number} length
* @return {string}
*/
trimToByteLength
=
function
(
s
,
length
)
{
return
mwString
.
trimByteLength
(
''
,
s
,
length
).
newVal
;
},
/**
* Cuts a file name to a specific byte length
*
* @private
* @static
* @method trimFileNameToByteLength
* @param {string} name without extension
* @param {string} extension file extension
* @return {string} The full name, including extension
*/
trimFileNameToByteLength
=
function
(
name
,
extension
)
{
// There is a special byte limit for file names and ... remember the dot
return
trimToByteLength
(
name
,
FILENAME_MAX_BYTES
-
extension
.
length
-
1
)
+
'.'
+
extension
;
};
/**
* @class mw.Title
* @classdesc Library for constructing MediaWiki titles.
*
* @example
* new mw.Title( 'Foo', NS_TEMPLATE ).getPrefixedText();
* // => 'Template:Foo'
* mw.Title.newFromText( 'Foo', NS_TEMPLATE ).getPrefixedText();
* // => 'Template:Foo'
* mw.Title.makeTitle( NS_TEMPLATE, 'Foo' ).getPrefixedText();
* // => 'Template:Foo'
*
* new mw.Title( 'Category:Foo', NS_TEMPLATE ).getPrefixedText();
* // => 'Category:Foo'
* mw.Title.newFromText( 'Category:Foo', NS_TEMPLATE ).getPrefixedText();
* // => 'Category:Foo'
* mw.Title.makeTitle( NS_TEMPLATE, 'Category:Foo' ).getPrefixedText();
* // => 'Template:Category:Foo'
*
* new mw.Title( 'Template:Foo', NS_TEMPLATE ).getPrefixedText();
* // => 'Template:Foo'
* mw.Title.newFromText( 'Template:Foo', NS_TEMPLATE ).getPrefixedText();
* // => 'Template:Foo'
* mw.Title.makeTitle( NS_TEMPLATE, 'Template:Foo' ).getPrefixedText();
* // => 'Template:Template:Foo'
*
* @constructor
* @description Parse titles into an object structure. Note that when using the constructor
* directly, passing invalid titles will result in an exception.
* Use [newFromText]{@link mw.Title.newFromText} to use the
* logic directly and get null for invalid titles which is easier to work with.
*
* Note that in the constructor and [newFromText]{@link mw.Title.newFromText} method,
* `namespace` is the **default** namespace only, and can be overridden by a namespace
* prefix in `title`. If you do not want this behavior,
* use [makeTitle]{@link mw.Title.makeTitle}.
*
* @param {string} title Title of the page. If no second argument given,
* this will be searched for a namespace
* @param {number} [namespace=NS_MAIN] If given, will used as default namespace for the given title
* @throws {Error} When the title is invalid
*/
function
Title
(
title
,
namespace
)
{
const
parsed
=
parse
(
title
,
namespace
);
if
(
!
parsed
)
{
throw
new
Error
(
'Unable to parse title'
);
}
this
.
namespace
=
parsed
.
namespace
;
this
.
title
=
parsed
.
title
;
this
.
fragment
=
parsed
.
fragment
;
}
/* Static members */
/**
* Constructor for Title objects with a null return instead of an exception for invalid titles.
*
* Note that `namespace` is the **default** namespace only, and can be overridden by a namespace
* prefix in `title`. If you do not want this behavior, use #makeTitle. See #constructor for
* details.
*
* @name mw.Title.newFromText
* @method
* @param {string} title
* @param {number} [namespace=NS_MAIN] Default namespace
* @return {mw.Title|null} A valid Title object or null if the title is invalid
*/
Title
.
newFromText
=
function
(
title
,
namespace
)
{
const
parsed
=
parse
(
title
,
namespace
);
if
(
!
parsed
)
{
return
null
;
}
const
t
=
Object
.
create
(
Title
.
prototype
);
t
.
namespace
=
parsed
.
namespace
;
t
.
title
=
parsed
.
title
;
t
.
fragment
=
parsed
.
fragment
;
return
t
;
};
/**
* Constructor for Title objects with predefined namespace.
*
* Unlike [newFromText]{@link mw.Title.newFromText} or the constructor, this function doesn't allow the given `namespace` to be
* overridden by a namespace prefix in `title`. See the constructor documentation for details about this behavior.
*
* The single exception to this is when `namespace` is 0, indicating the main namespace. The
* function behaves like [newFromText]{@link mw.Title.newFromText} in that case.
*
* @name mw.Title.makeTitle
* @method
* @param {number} namespace Namespace to use for the title
* @param {string} title
* @return {mw.Title|null} A valid Title object or null if the title is invalid
*/
Title
.
makeTitle
=
function
(
namespace
,
title
)
{
if
(
!
isKnownNamespace
(
namespace
)
)
{
return
null
;
}
else
{
return
mw
.
Title
.
newFromText
(
getNamespacePrefix
(
namespace
)
+
title
);
}
};
/**
* Constructor for Title objects from user input altering that input to
* produce a title that MediaWiki will accept as legal.
*
* @name mw.Title.newFromUserInput
* @method
* @param {string} title
* @param {number} [defaultNamespace=NS_MAIN]
* If given, will used as default namespace for the given title.
* @param {Object} [options] additional options
* @param {boolean} [options.forUploading=true]
* Makes sure that a file is uploadable under the title returned.
* There are pages in the file namespace under which file upload is impossible.
* Automatically assumed if the title is created in the Media namespace.
* @return {mw.Title|null} A valid Title object or null if the input cannot be turned into a valid title
*/
Title
.
newFromUserInput
=
function
(
title
,
defaultNamespace
,
options
)
{
let
namespace
=
parseInt
(
defaultNamespace
)
||
NS_MAIN
;
// merge options into defaults
options
=
Object
.
assign
(
{
forUploading
:
true
},
options
);
// Normalise additional whitespace
title
=
title
.
replace
(
/\s/g
,
' '
).
trim
();
// Process initial colon
if
(
title
!==
''
&&
title
[
0
]
===
':'
)
{
// Initial colon means main namespace instead of specified default
namespace
=
NS_MAIN
;
title
=
title
// Strip colon
.
slice
(
1
)
// Trim underscores
.
replace
(
rUnderscoreTrim
,
''
);
}
// Process namespace prefix (if any)
const
m
=
title
.
match
(
rSplit
);
if
(
m
)
{
const
id
=
getNsIdByName
(
m
[
1
]
);
if
(
id
!==
false
)
{
// Ordinary namespace
namespace
=
id
;
title
=
m
[
2
];
}
}
if
(
namespace
===
NS_MEDIA
||
(
options
.
forUploading
&&
(
namespace
===
NS_FILE
)
)
)
{
title
=
sanitize
(
title
,
[
'generalRule'
,
'fileRule'
]
);
// Operate on the file extension
// Although it is possible having spaces between the name and the ".ext" this isn't nice for
// operating systems hiding file extensions -> strip them later on
const
lastDot
=
title
.
lastIndexOf
(
'.'
);
// No or empty file extension
if
(
lastDot
===
-
1
||
lastDot
>=
title
.
length
-
1
)
{
return
null
;
}
// Get the last part, which is supposed to be the file extension
const
ext
=
title
.
slice
(
lastDot
+
1
);
// Remove whitespace of the name part (that without extension)
title
=
title
.
slice
(
0
,
lastDot
).
trim
();
// Cut, if too long and append file extension
title
=
trimFileNameToByteLength
(
title
,
ext
);
}
else
{
title
=
sanitize
(
title
,
[
'generalRule'
]
);
// Cut titles exceeding the TITLE_MAX_BYTES byte size limit
// (size of underlying database field)
if
(
namespace
!==
NS_SPECIAL
)
{
title
=
trimToByteLength
(
title
,
TITLE_MAX_BYTES
);
}
}
// Any remaining initial :s are illegal.
title
=
title
.
replace
(
/^:+/
,
''
);
return
Title
.
newFromText
(
title
,
namespace
);
};
/**
* Sanitizes a file name as supplied by the user, originating in the user's file system
* so it is most likely a valid MediaWiki title and file name after processing.
* Returns null on fatal errors.
*
* @name mw.Title.newFromFileName
* @method
* @param {string} uncleanName The unclean file name including file extension but
* without namespace
* @return {mw.Title|null} A valid Title object or null if the title is invalid
*/
Title
.
newFromFileName
=
function
(
uncleanName
)
{
return
Title
.
newFromUserInput
(
'File:'
+
uncleanName
);
};
/**
* Get the file title from an image element.
*
* @example
* const title = mw.Title.newFromImg( imageNode );
*
* @name mw.Title.newFromImg
* @method
* @param {HTMLElement|jQuery} img The image to use as a base
* @return {mw.Title|null} The file title or null if unsuccessful
*/
Title
.
newFromImg
=
function
(
img
)
{
const
src
=
img
.
jquery
?
img
[
0
].
src
:
img
.
src
,
data
=
mw
.
util
.
parseImageUrl
(
src
);
return
data
?
mw
.
Title
.
newFromText
(
'File:'
+
data
.
name
)
:
null
;
};
/**
* Check if a given namespace is a talk namespace.
*
* See NamespaceInfo::isTalk in PHP
*
* @name mw.Title.isTalkNamespace
* @method
* @param {number} namespaceId Namespace ID
* @return {boolean} Namespace is a talk namespace
*/
Title
.
isTalkNamespace
=
function
(
namespaceId
)
{
return
namespaceId
>
NS_MAIN
&&
namespaceId
%
2
===
1
;
};
/**
* Check if signature buttons should be shown in a given namespace.
*
* See NamespaceInfo::wantSignatures in PHP
*
* @name mw.Title.wantSignaturesNamespace
* @method
* @param {number} namespaceId Namespace ID
* @return {boolean} Namespace is a signature namespace
*/
Title
.
wantSignaturesNamespace
=
function
(
namespaceId
)
{
return
Title
.
isTalkNamespace
(
namespaceId
)
||
mw
.
config
.
get
(
'wgExtraSignatureNamespaces'
).
indexOf
(
namespaceId
)
!==
-
1
;
};
/**
* Whether this title exists on the wiki.
*
* @name mw.Title.exists
* @method
* @param {string|mw.Title} title prefixed db-key name (string) or instance of Title
* @return {boolean|null} Boolean if the information is available, otherwise null
* @throws {Error} If title is not a string or mw.Title
*/
Title
.
exists
=
function
(
title
)
{
const
obj
=
Title
.
exist
.
pages
;
let
match
;
if
(
typeof
title
===
'string'
)
{
match
=
obj
[
title
];
}
else
if
(
title
instanceof
Title
)
{
match
=
obj
[
title
.
toString
()
];
}
else
{
throw
new
Error
(
'mw.Title.exists: title must be a string or an instance of Title'
);
}
if
(
typeof
match
!==
'boolean'
)
{
return
null
;
}
return
match
;
};
/**
* @typedef {Object} mw.Title~TitleExistenceStore
* @property {Object} pages Keyed by title. Boolean true value indicates page does exist.
*
* @property {Function} set The setter function. Returns a boolean.
*
* Example to declare existing titles:
* ```
* Title.exist.set( ['User:John_Doe', ...] );
* ```
*
* Example to declare titles nonexistent:
* ```
* Title.exist.set( ['File:Foo_bar.jpg', ...], false );
* ```
*
* @property {string|string[]} set.titles Title(s) in strict prefixedDb title form
* @property {boolean} [set.state=true] State of the given titles
*/
/**
* @name mw.Title.exist
* @type {mw.Title~TitleExistenceStore}
*/
Title
.
exist
=
{
pages
:
{},
set
:
function
(
titles
,
state
)
{
const
pages
=
this
.
pages
;
titles
=
Array
.
isArray
(
titles
)
?
titles
:
[
titles
];
state
=
state
===
undefined
?
true
:
!!
state
;
for
(
let
i
=
0
,
len
=
titles
.
length
;
i
<
len
;
i
++
)
{
pages
[
titles
[
i
]
]
=
state
;
}
return
true
;
}
};
/**
* Normalize a file extension to the common form, making it lowercase and checking some synonyms,
* and ensure it's clean. Extensions with non-alphanumeric characters will be discarded.
* Keep in sync with File::normalizeExtension() in PHP.
*
* @name mw.Title.normalizeExtension
* @method
* @param {string} extension File extension (without the leading dot)
* @return {string} File extension in canonical form
*/
Title
.
normalizeExtension
=
function
(
extension
)
{
const
lower
=
extension
.
toLowerCase
(),
normalizations
=
{
htm
:
'html'
,
jpeg
:
'jpg'
,
mpeg
:
'mpg'
,
tiff
:
'tif'
,
ogv
:
'ogg'
};
if
(
Object
.
hasOwnProperty
.
call
(
normalizations
,
lower
)
)
{
return
normalizations
[
lower
];
}
else
if
(
/^[\da-z]+$/
.
test
(
lower
)
)
{
return
lower
;
}
else
{
return
''
;
}
};
/**
* PHP's strtoupper differs from String.toUpperCase in a number of cases (T147646).
*
* @name mw.Title.phpCharToUpper
* @method
* @param {string} chr Unicode character
* @return {string} Unicode character, in upper case, according to the same rules as in PHP
*/
Title
.
phpCharToUpper
=
function
(
chr
)
{
if
(
!
toUpperMap
)
{
toUpperMap
=
require
(
'./phpCharToUpper.json'
);
}
if
(
toUpperMap
[
chr
]
===
0
)
{
// Optimisation: When the override is to keep the character unchanged,
// we use 0 in JSON. This reduces the data by 50%.
return
chr
;
}
return
toUpperMap
[
chr
]
||
chr
.
toUpperCase
();
};
/* Public members */
Title
.
prototype
=
/** @lends mw.Title.prototype */
{
constructor
:
Title
,
/**
* Get the namespace number.
*
* Example: 6 for "File:Example_image.svg".
*
* @return {number}
*/
getNamespaceId
:
function
()
{
return
this
.
namespace
;
},
/**
* Get the namespace prefix (in the content language).
*
* Example: "File:" for "File:Example_image.svg".
* In `NS_MAIN` this is '', otherwise namespace name plus ':'
*
* @return {string}
*/
getNamespacePrefix
:
function
()
{
return
getNamespacePrefix
(
this
.
namespace
);
},
/**
* Get the page name as if it is a file name, without extension or namespace prefix,
* in the canonical form with underscores instead of spaces. For example, the title
* `File:Example_image.svg` will be returned as `Example_image`.
*
* Note that this method will work for non-file titles but probably give nonsensical results.
* A title like `User:Dr._J._Fail` will be returned as `Dr._J`! Use [getMain]{@link mw.Title#getMain} instead.
*
* @return {string}
*/
getFileNameWithoutExtension
:
function
()
{
const
ext
=
this
.
getExtension
();
if
(
ext
===
null
)
{
return
this
.
getMain
();
}
return
this
.
getMain
().
slice
(
0
,
-
ext
.
length
-
1
);
},
/**
* Get the page name as if it is a file name, without extension or namespace prefix,
* in the human-readable form with spaces instead of underscores. For example, the title
* `File:Example_image.svg` will be returned as "Example image".
*
* Note that this method will work for non-file titles but probably give nonsensical results.
* A title like `User:Dr._J._Fail` will be returned as `Dr. J`! Use [getMainText]{@link mw.Title#getMainText} instead.
*
* @return {string}
*/
getFileNameTextWithoutExtension
:
function
()
{
return
text
(
this
.
getFileNameWithoutExtension
()
);
},
/**
* Get the page name as if it is a file name, without extension or namespace prefix. Warning,
* this is usually not what you want! A title like `User:Dr._J._Fail` will be returned as
* `Dr. J`! Use [getMain]{@link mw.Title#getMain} or [getMainText]{@link mw.Title#getMainText} for the actual page name.
*
* @return {string} File name without file extension, in the canonical form with underscores
* instead of spaces. For example, the title `File:Example_image.svg` will be returned as
* `Example_image`.
* @deprecated since 1.40, use [getFileNameWithoutExtension]{@link mw.Title#getFileNameWithoutExtension} instead
*/
getName
:
function
()
{
return
this
.
getFileNameWithoutExtension
();
},
/**
* Get the page name as if it is a file name, without extension or namespace prefix. Warning,
* this is usually not what you want! A title like `User:Dr._J._Fail` will be returned as
* `Dr. J`! Use [getMainText]{@link mw.Title#getMainText} for the actual page name.
*
* @return {string} File name without file extension, formatted with spaces instead of
* underscores. For example, the title `File:Example_image.svg` will be returned as
* `Example image`.
* @deprecated since 1.40, use [getFileNameTextWithoutExtension]{@link mw.Title#getFileNameTextWithoutExtension} instead
*/
getNameText
:
function
()
{
return
text
(
this
.
getFileNameTextWithoutExtension
()
);
},
/**
* Get the extension of the page name (if any).
*
* @return {string|null} Name extension or null if there is none
*/
getExtension
:
function
()
{
const
lastDot
=
this
.
title
.
lastIndexOf
(
'.'
);
if
(
lastDot
===
-
1
)
{
return
null
;
}
return
this
.
title
.
slice
(
lastDot
+
1
)
||
null
;
},
/**
* Get the main page name.
*
* Example: `Example_image.svg` for `File:Example_image.svg`.
*
* @return {string}
*/
getMain
:
function
()
{
if
(
mw
.
config
.
get
(
'wgCaseSensitiveNamespaces'
).
indexOf
(
this
.
namespace
)
!==
-
1
||
!
this
.
title
.
length
)
{
return
this
.
title
;
}
const
firstChar
=
mwString
.
charAt
(
this
.
title
,
0
);
return
mw
.
Title
.
phpCharToUpper
(
firstChar
)
+
this
.
title
.
slice
(
firstChar
.
length
);
},
/**
* Get the main page name (transformed by text()).
*
* Example: `Example image.svg` for `File:Example_image.svg`.
*
* @return {string}
*/
getMainText
:
function
()
{
return
text
(
this
.
getMain
()
);
},
/**
* Get the full page name.
*
* Example: `File:Example_image.svg`.
* Most useful for API calls, anything that must identify the "title".
*
* @return {string}
*/
getPrefixedDb
:
function
()
{
return
this
.
getNamespacePrefix
()
+
this
.
getMain
();
},
/**
* Get the full page name (transformed by [text]{@link mw.Title#text}).
*
* Example: `File:Example image.svg` for `File:Example_image.svg`.
*
* @return {string}
*/
getPrefixedText
:
function
()
{
return
text
(
this
.
getPrefixedDb
()
);
},
/**
* Get the page name relative to a namespace.
*
* Example:
*
* - "Foo:Bar" relative to the Foo namespace becomes "Bar".
* - "Bar" relative to any non-main namespace becomes ":Bar".
* - "Foo:Bar" relative to any namespace other than Foo stays "Foo:Bar".
*
* @param {number} namespace The namespace to be relative to
* @return {string}
*/
getRelativeText
:
function
(
namespace
)
{
if
(
this
.
getNamespaceId
()
===
namespace
)
{
return
this
.
getMainText
();
}
else
if
(
this
.
getNamespaceId
()
===
NS_MAIN
)
{
return
':'
+
this
.
getPrefixedText
();
}
else
{
return
this
.
getPrefixedText
();
}
},
/**
* Get the fragment (if any).
*
* Note that this method (by design) does not include the hash character and
* the value is not url encoded.
*
* @return {string|null}
*/
getFragment
:
function
()
{
return
this
.
fragment
;
},
/**
* Get the URL to this title.
*
* @see [mw.util.getUrl]{@link module:mediawiki.util.getUrl}
* @param {Object} [params] A mapping of query parameter names to values,
* e.g. `{ action: 'edit' }`.
* @return {string}
*/
getUrl
:
function
(
params
)
{
const
fragment
=
this
.
getFragment
();
if
(
fragment
)
{
return
mw
.
util
.
getUrl
(
this
.
toString
()
+
'#'
+
fragment
,
params
);
}
else
{
return
mw
.
util
.
getUrl
(
this
.
toString
(),
params
);
}
},
/**
* Check if the title is in a talk namespace.
*
* @return {boolean} The title is in a talk namespace
*/
isTalkPage
:
function
()
{
return
Title
.
isTalkNamespace
(
this
.
getNamespaceId
()
);
},
/**
* Get the title for the associated talk page.
*
* @return {mw.Title|null} The title for the associated talk page, null if not available
*/
getTalkPage
:
function
()
{
if
(
!
this
.
canHaveTalkPage
()
)
{
return
null
;
}
return
this
.
isTalkPage
()
?
this
:
Title
.
makeTitle
(
this
.
getNamespaceId
()
+
1
,
this
.
getMainText
()
);
},
/**
* Get the title for the subject page of a talk page.
*
* @return {mw.Title|null} The title for the subject page of a talk page, null if not available
*/
getSubjectPage
:
function
()
{
return
this
.
isTalkPage
()
?
Title
.
makeTitle
(
this
.
getNamespaceId
()
-
1
,
this
.
getMainText
()
)
:
this
;
},
/**
* Check the title can have an associated talk page.
*
* @return {boolean} The title can have an associated talk page
*/
canHaveTalkPage
:
function
()
{
return
this
.
getNamespaceId
()
>=
NS_MAIN
;
},
/**
* Whether this title exists on the wiki.
*
* @see mw.Title.exists
* @return {boolean|null} Boolean if the information is available, otherwise null
*/
exists
:
function
()
{
return
Title
.
exists
(
this
);
}
};
/**
* Alias of [getPrefixedDb]{@link mw.Title#getPrefixedDb}.
*
* @name mw.Title.prototype.toString
* @method
*/
Title
.
prototype
.
toString
=
Title
.
prototype
.
getPrefixedDb
;
/**
* Alias of [getPrefixedText]{@link mw.Title#getPrefixedText}.
*
* @name mw.Title.prototype.toText
* @method
*/
Title
.
prototype
.
toText
=
Title
.
prototype
.
getPrefixedText
;
// Expose
mw
.
Title
=
Title
;
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Wed, Aug 19, 06:03 (13 h, 22 m ago)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
bb/4b/ef0983a89c7ff19533ad0e58efc4
Default Alt Text
Title.js (28 KB)
Attached To
Mode
rMWPROD MediaWiki Production
Attached
Detach File
Event Timeline
Log In to Comment