local title = {} local php local NS_MEDIA = -2 local util = require 'libraryUtil' local checkType = util.checkType local checkTypeForIndex = util.checkTypeForIndex local function checkNamespace( name, argIdx, arg ) if type( arg ) == 'string' and tostring( tonumber( arg ) ) == arg then arg = tonumber( arg ) end if type( arg ) == 'number' then arg = math.floor( arg + 0.5 ) if not mw.site.namespaces[arg] then local msg = string.format( "bad argument #%d to '%s' (unrecognized namespace number '%s')", argIdx, name, arg ) error( msg, 3 ) end elseif type( arg ) == 'string' then local ns = mw.site.namespaces[arg] if not ns then local msg = string.format( "bad argument #%d to '%s' (unrecognized namespace name '%s')", argIdx, name, arg ) error( msg, 3 ) end arg = ns.id else local msg = string.format( "bad argument #%d to '%s' (string or number expected, got %s)", argIdx, name, type( arg ) ) error( msg, 3 ) end return arg end local function lt( a, b ) if a.interwiki ~= b.interwiki then return a.interwiki < b.interwiki end if a.namespace ~= b.namespace then return a.namespace < b.namespace end return a.text < b.text end local function makeTitleObject( data ) if not data then return nil end local obj = {} local checkSelf = util.makeCheckSelfFunction( 'mw.title', 'title', obj, 'title object' ); -- For external (interwiki) links, data.namespace is unknown and we'll -- end up just using the properties for the default namespace here. -- We'll fix it up below to avoid misleading the caller. local ns = mw.site.namespaces[data.namespace] local isCurrentTitle = data.isCurrentTitle data.isCurrentTitle = nil data.isContentPage = ns.isContent data.isExternal = data.interwiki ~= '' data.isSpecialPage = data.namespace == mw.site.namespaces.Special.id data.isTalkPage = ns.isTalk data.subjectNsText = ns.subject.name if ns.talk ~= nil then data.canTalk = true data.talkNsText = ns.talk.name else data.canTalk = false end if data.isExternal then -- For interwiki links we don't know the true value of any of these -- properties, so nil them out. data.isContentPage = nil data.isSpecialPage = nil data.isTalkPage = nil data.canTalk = nil data.subjectNsText = nil data.talkNsText = nil end data.prefixedText = data.text if data.nsText ~= '' then data.prefixedText = string.gsub( data.nsText .. ':' .. data.prefixedText, '_', ' ' ) end if data.interwiki ~= '' then data.prefixedText = data.interwiki .. ':' .. data.prefixedText end local firstSlash, lastSlash if ns.hasSubpages then firstSlash, lastSlash = string.match( data.text, '^[^/]*().*()/[^/]*$' ) end if firstSlash then data.isSubpage = true data.rootText = string.sub( data.text, 1, firstSlash - 1 ) data.baseText = string.sub( data.text, 1, lastSlash - 1 ) data.subpageText = string.sub( data.text, lastSlash + 1 ) else data.isSubpage = false data.rootText = data.text data.baseText = data.text data.subpageText = data.text end function data:inNamespace( ns ) checkSelf( self, 'inNamespace' ) ns = checkNamespace( 'inNamespace', 1, ns ) return ns == self.namespace end function data:inNamespaces( ... ) checkSelf( self, 'inNamespaces' ) for i = 1, select( '#', ... ) do local ns = checkNamespace( 'inNamespaces', i, select( i, ... ) ) if ns == self.namespace then return true end end return false end function data:hasSubjectNamespace( ns ) checkSelf( self, 'hasSubjectNamespace' ) ns = checkNamespace( 'hasSubjectNamespace', 1, ns ) return ns == mw.site.namespaces[self.namespace].subject.id end function data:isSubpageOf( title ) checkSelf( self, 'isSubpageOf' ) checkType( 'isSubpageOf', 1, title, 'table' ) return self.interwiki == title.interwiki and self.namespace == title.namespace and title.text .. '/' == string.sub( self.text, 1, #title.text + 1 ) end function data:subPageTitle( text ) checkSelf( self, 'subpageTitle' ) checkType( 'subpageTitle', 1, text, 'string' ) return title.makeTitle( data.namespace, data.text .. '/' .. text ) end function data:partialUrl() checkSelf( self, 'partialUrl' ) return data.thePartialUrl end function data:fullUrl( query, proto ) checkSelf( self, 'fullUrl' ) return php.getUrl( self.fullText, 'fullUrl', query, proto ) end function data:localUrl( query ) checkSelf( self, 'localUrl' ) return php.getUrl( self.fullText, 'localUrl', query ) end function data:canonicalUrl( query ) checkSelf( self, 'canonicalUrl' ) return php.getUrl( self.fullText, 'canonicalUrl', query ) end function data:getContent() -- deprecated: should use `title.content` instead checkSelf( self, 'getContent' ) local content = php.getContent( self.fullText ) data.getContent = function ( self ) checkSelf( self, 'getContent' ) return content end return content end -- Known fields, both those defined above and any dynamically handled in -- __index. Truthy values represent read-only, and falsey values represent -- read-write. If the value is the string 'e', expensive data will be loaded -- if the field is read. local readOnlyFields = { fragment = false, fullText = true, rootPageTitle = true, basePageTitle = true, talkPageTitle = true, subjectPageTitle = true, fileExists = true, file = true, protectionLevels = true, cascadingProtection = true, exists = 'e', isRedirect = 'e', contentModel = 'e', id = 'e', redirectTarget = true, } for k in pairs( data ) do readOnlyFields[k] = true end local function pairsfunc( t, k ) local v repeat k = next( readOnlyFields, k ) if k == nil then return nil end v = t[k] until v ~= nil return k, v end return setmetatable( obj, { __eq = title.equals, __lt = lt, __pairs = function ( t ) return pairsfunc, t, nil end, __index = function ( t, k ) if k == 'exists' and data.namespace == NS_MEDIA then k = 'fileExists' end if readOnlyFields[k] == 'e' and data[k] == nil then for k,v in pairs( php.getExpensiveData( t.fullText ) ) do data[k] = v end end if k == 'id' and isCurrentTitle then php.recordVaryFlag( t.fullText, 'vary-page-id' ) end if k == 'fullText' then if data.fragment ~= '' then return data.prefixedText .. '#' .. data.fragment else return data.prefixedText end end if k == 'rootPageTitle' then return title.makeTitle( data.namespace, data.rootText ) end if k == 'basePageTitle' then return title.makeTitle( data.namespace, data.baseText ) end if k == 'talkPageTitle' then local ns = mw.site.namespaces[data.namespace].talk if not ns then return nil end if ns.id == data.namespace then return obj end return title.makeTitle( ns.id, data.text ) end if k == 'subjectPageTitle' then local ns = mw.site.namespaces[data.namespace].subject if ns.id == data.namespace then return obj end return title.makeTitle( ns.id, data.text ) end if k == 'pageLang' then if data.pageLang == nil then data.pageLang = mw.language.new( php.getPageLangCode( data.prefixedText ) ); end return data.pageLang end if k == 'file' then if data.file == nil then data.file = php.getFileInfo( data.prefixedText ) end return data.file or nil end if k == 'fileExists' then -- Kept for backward compatibility. Since 1.25, file.exists is preferred over this return t.file and t.file.exists end if k == 'protectionLevels' then if data.protectionLevels == nil then data.protectionLevels = php.protectionLevels( data.prefixedText ) end return data.protectionLevels end if k == 'cascadingProtection' then if data.cascadingProtection == nil then data.cascadingProtection = php.cascadingProtection( data.prefixedText ) end return data.cascadingProtection end if k == 'redirectTarget' then if data.redirectTarget == nil then data.redirectTarget = makeTitleObject( php.redirectTarget( data.prefixedText ) ) or false end return data.redirectTarget end if k == 'content' then if data.content == nil then data.content = php.getContent( data.prefixedText ) or false end return data.content end if k == 'categories' then if data.categories == nil then data.categories = php.getCategories( data.prefixedText ) end return data.categories end return data[k] end, __newindex = function ( t, k, v ) if k == 'fragment' then checkTypeForIndex( k, v, 'string' ) v = string.gsub( v, '[%s_]+', ' ' ) v = string.gsub( v, '^(.-) ?$', '%1' ) data[k] = v elseif readOnlyFields[k] then error( "index '" .. k .. "' is read only", 2 ) else readOnlyFields[k] = v and false -- assigns nil if v == nil, false otherwise rawset( t, k, v ) end end, __tostring = function ( t ) return t.prefixedText end } ) end function title.setupInterface( options ) -- Boilerplate title.setupInterface = nil php = mw_interface mw_interface = nil NS_MEDIA = options.NS_MEDIA -- Set current title title.getCurrentTitle = function () return makeTitleObject( mw.clone( options.thisTitle ) ) end -- Register this library in the "mw" global mw = mw or {} mw.title = title package.loaded['mw.title'] = title end function title.new( text_or_id, defaultNamespace ) return makeTitleObject( php.newTitle( text_or_id, defaultNamespace ) ) end function title.makeTitle( ns, title, fragment, interwiki ) return makeTitleObject( php.makeTitle( ns, title, fragment, interwiki ) ) end function title.equals( a, b ) return a.interwiki == b.interwiki and a.namespace == b.namespace and a.text == b.text end function title.compare( a, b ) if a.interwiki ~= b.interwiki then return a.interwiki < b.interwiki and -1 or 1 end if a.namespace ~= b.namespace then return a.namespace < b.namespace and -1 or 1 end if a.text ~= b.text then return a.text < b.text and -1 or 1 end return 0 end return title