Модуль:Core

    Матеріал з Релігія в огні

    Документацію для цього модуля можна створити у Модуль:Core/документація

    --[[  
      __  __           _       _         ____               
     |  \/  | ___   __| |_   _| | ___ _ / ___|___  _ __ ___ 
     | |\/| |/ _ \ / _` | | | | |/ _ (_) |   / _ \| '__/ _ \
     | |  | | (_) | (_| | |_| | |  __/_| |__| (_) | | |  __/
     |_|  |_|\___/ \__,_|\__,_|_|\___(_)\____\___/|_|  \___|
                                                            
    Core is a meta-module that consists of common and useful Lua functions that can 
    be used in many Lua scripts. It was writen as a core of several Lua 
    modules for creating file infobox templates on Commons. Many of the functions
    are bare-bones versions of full functions found in other modules.
    
    Authors and maintainers:
    * User:Jarekt  
    ]]
    
    local core = {}
    
    ------------------------------------------------------------------------------
    --[[
    Based on frame structure create "args" table with all the input parameters:
     * table keys - equivalent to template parameters are converted to lower case 
                    so they will not be case-sensitive.
                    Also underscored are treated the same way as speces. 
     * table values - input values are trimmed (whitespaces removed from the beggining 
                    and the end) and empty string are converted to nils. 
    If "lang" is not provided than we substitute user's prefered language.
    
    This function collects inputs from both frame and frame's parent. See 
    https://www.mediawiki.org/wiki/Extension:Scribunto/Lua_reference_manual#frame:getParent .
    If both structures have the same field than value from "frame" takes priority.
    
    Inputs:
    	1) frame - frame objects see below for details
    	https://www.mediawiki.org/wiki/Extension:Scribunto/Lua_reference_manual#frame-object
    
    See also: Module:Arguments on enWiki - which is much larger single purpose module
    ]]
    function core.getArgs(frame)
    	local function normalize_input_args(input_args, output_args)
    		for name, value in pairs( input_args ) do 
    			value = mw.text.trim(value) -- trim whitespaces from the beggining and the end of the string
    			if value ~= '' then -- nuke empty strings
    				if type(name)=='string' then 
    					name = string.gsub( string.lower(name), ' ', '_')
    				end
    				output_args[name] = value
    			end
    		end
    		return output_args
    	end
    	local args = {}
    	args = normalize_input_args(frame:getParent().args, args)
    	args = normalize_input_args(frame.args, args)
    	if (args.lang and mw.language.isSupportedLanguage(args.lang)) then 
    		args.lang = string.lower(args.lang)
    	else
    		args.lang = frame:callParserFunction("int","lang")  -- get user's chosen language
    	end
    	return args
    end
    
    ------------------------------------------------------------------------------
    --[[
    Simplified code equivalent to https://commons.wikimedia.org/wiki/Template:LangSwitch
    
    Example usage:
      text = langSwitch({en='text in english', pl='tekst po polsku'}, lang)
    
    Inputs:
      1: args - table with translations by language
      2: lang - desired language (often user's native language)
      
    Outputs:
      1: label - returned label
      2: lang  - language of the label (langSwitchWithLang only)
    ]]
    function core.langSwitchWithLang(args, lang)
    	local langList = mw.language.getFallbacksFor(lang)
    	table.insert(langList,1,lang)
    	for i,language in ipairs(langList) do
    		if args[language] then
    			return args[language], language
    		end
    	end
    end
    
    function core.langSwitch(args, lang)
    	local label, lang = core.langSwitchWithLang(args, lang)
    	return label
    end
    
    ------------------------------------------------------------------------------
    --[[
    display a language followed by a message. Like "English: Hello" with extensive HTML marking
    Code equivalent to https://commons.wikimedia.org/wiki/Template:Description 
    Inputs:
      1) text_lang - language code for the above text, also used as a name of the CSS formating class
      2) text - description text to display
      3) args -  additional optional arguments. Numbers in perenthesis are parameter numbers in the original template.
    	   * hover    - (3) hover aka mouseover aka tooltip text
    	   * lang_tag - (4) standard code for lang tag in HTML (optional, default is same as text_lang)
    	   * ext      - (5) extension text shown after the language name before colon (optional, default is empty)
           * inline - Optional, default is false. When set to true, forces the template to be displayed 
                inline, so that it does not break the current paragraph (that makes possible to put several 
    	        descriptions side by side on a single line)	   
    ]]
    function core.langWrapper(text_lang, text, args) 
    	local dir, space, colon, lang_name, hover
    	local inline    = core.yesno(args.inline, false) and 'inline' or nil 
    	if 	mw.language.isKnownLanguageTag(text_lang) then -- supported language
    		local langObj = mw.language.new( text_lang )
    		dir       = langObj:getDir()          -- text direction
    		space     = mw.message.new( "Word-separator" ):inLanguage(text_lang):plain() 
    		colon     = mw.message.new( "Colon" ):inLanguage(text_lang):plain() 
    		hover     = mw.language.fetchLanguageName( text_lang, args.user_lang or 'en')
    		lang_name = mw.language.fetchLanguageName( text_lang, text_lang)
    		lang_name = langObj:ucfirst(lang_name)
    	else -- unsuported language
    		local RTL_LUT = {['fa-af']=1, prd=1, ydd=1}
    		dir   = (RTL_LUT[text_lang]==1 or text_lang:gsub('-arab', '')~=text_lang) and 'rtl' or 'ltr'
    		space = ' '
    		colon = ':'
    		hover = args.hover
    		lang_name = text_lang or 'Unknown'
    	end
    	lang_name = args.lang_name or lang_name-- user provided args.lang_name takes presedent
    	lang_name = '<b>' .. lang_name .. (args.ext or '') .. colon .. '</b>'
    	
    	-- create HTML
    	local ltag = mw.html.create('span')  -- bold language name string
    		:addClass('language')  -- class: "language" 
    		:addClass(text_lang)   -- class:  "en", "de" etc.	
    		:attr('title', hover)  -- add hover aka mouseover aka tooltip text
    		:wikitext(lang_name)
    	local dtag = mw.html.create('div')
    		:addClass('description')      -- div.description is tracked by mw:Extension:CommonsMetadata
    		:addClass('mw-content-'..dir) -- mw-content-rtl and mw-content-ltr are defined in mediawiki-code (https://gerrit.wikimedia.org/r/c/mediawiki/core/+/208332)
    		:addClass(text_lang)          -- not sure where "en", "de" etc. are defined
    		:attr('dir', dir)
    		:attr('lang', text_lang)
    		:css('display', inline) 
    		:wikitext(tostring(ltag) .. space .. text)
    	return tostring(dtag)
    end
    
    ------------------------------------------------------------------------------
    --[[
    Function allowing for consistent treatment of boolean-like wikitext input.
    Inputs:
      1) val - value to be evaluated, outputs as a function of values:
    		true  : true  (boolean), 1 (number), or strings: "yes", "y", "true", "1"
    		false : false (boolean), 0 (number), or strings: "no", "n", "false", "0"
      2) default - value to return otherwise
    See Also: It works similarly to Module:Yesno
    ]]
    function core.yesno(val, default)
    	if type(val) == 'boolean' then
    		return val
    	elseif type(val) == 'number' then
    		val = tostring(val)
    	end
    	if type(val) == 'string' then
    		local LUT = {
    			yes=true , y=true , ['true'] =true , t=true , ['1']=true , on =true,
    			no =false, n=false, ['false']=false, f=false, ['0']=false, off=false }
    	    val = LUT[mw.ustring.lower(val)]  -- put in lower case
    	    if (val~=nil) then
    			return val
    		end
        end
        return default
    end
    
    ------------------------------------------------------------------------------
    --[[
    Read Commons Data:SOMENAME.tab dataset and look for message identified by a 
    "key" in a language "lang". See editAtWikidata for an example. It uses
    https://www.mediawiki.org/wiki/Extension:Scribunto/Lua_reference_manual#mw.ext.data
    
    Inputs:
     1) dataset - name of commons page in "data" namespace. for example "I18n/EditAt.tab" 
    	for c:Data:I18n/EditAt.tab
     2) key - which message to pull
     3) lang - desired language of the message
    Output: message as a string
    ]]
    function core.formatMessage(dataset, key, lang)
    	for _, row in pairs(mw.ext.data.get(dataset, lang).data) do
    		local id, msg = unpack(row)
    		if id == key then
    			return mw.message.newRawMessage(msg):plain()
    		end
    	end
    	error('Invalid message key "' .. key .. '"')
    end
    
    ------------------------------------------------------------------------------
    --[[
    Assembles the "Edit at Wikidata" pen icon with a link to Wikidata page or specific 
    property and returns it as wikitext string.
    Inputs:
      1) entityID - wikidata entity object for a given page (output of wikibase.getEntity( id ))
      2) propertyID - string like 'P31' so the link will point to that specific property. Use "" 
         to link to the whole page. 
      3) lang - language of the "Edit at Wikidata"  message
    Dependencies: Data:I18n/EditAt.tab
    See Also: en:Module:EditAtWikidata
    ]]
    -------------------------------------------------------------------------------
    function core.editAtWikidata(entityID, propertyID, lang)
    	local msg  = core.formatMessage('I18n/EditAt.tab', 'EditAtWikidata', lang)
    	local link = 'https://www.wikidata.org/wiki/' .. entityID .. (propertyID == "" and "" or ("#" .. propertyID))
    	return "&nbsp;[[File:OOjs UI icon edit-ltr-progressive.svg |frameless |text-top |10px |alt="..msg.."|link="..link.."|"..msg.."]]"
    end
    
    ------------------------------------------------------------------------------
    --[[
    Assembles the "Edit at SDC" pen icon with a link to a property in SDC part of the current file 
    page, and returns it as wikitext string.
    Inputs:
      2) propertyID - string like 'P31' so the link will point to that specific property. Use 'ooui-php-4' 
         to link to the label section. 
      3) lang - language of the "Edit at Wikidata"  message
    Dependencies: Data:I18n/EditAt.tab
    See Also: en:Module:EditAtWikidata
    ]]
    function core.editAtSDC(propertyID, lang)
    	local msg  = core.formatMessage('I18n/EditAt.tab', 'EditAtSDC', lang)
    	local link =  mw.title.getCurrentTitle():fullUrl() .. (propertyID == "" and "" or ("#" .. propertyID))
    	return "&nbsp;[[File:OOjs UI icon edit-ltr-progressive.svg |frameless |text-top |10px |alt="..msg.."|link="..link.."|"..msg.."]]"
    end
    
    -------------------------------------------------------------------------------
    --[[
    This function returns a label translated to desired language, created based on wikidata
    Code equivalent to require("Module:Wikidata label")._getLabel
    
    Inputs:
    	1: item - wikidata's item's q-id or entity class
    	2: userLang - desired language of the label
    ]]
    function core.getLabel(item, userLang) 
    	local label, link
    	-- build language fallback list
    	local langList = mw.language.getFallbacksFor(userLang)
    	table.insert(langList, 1, userLang) 
    	-- get label
    	for _, lang in ipairs(langList) do  -- loop over language fallback list looking for label in the specific language
    		label = mw.wikibase.getLabelByLang(item, lang)
    		if label then break end                    -- label found and we are done
    	end	
    	label = label or item -- fallback value
    	-- get link
    	for _, lang in ipairs(langList) do  -- loop over language fallback list looking for label in the specific language
    		link =  mw.wikibase.getSitelink(item, lang .. 'wiki')
    		if link then
    			link = mw.ustring.format('w:%s:%s', lang, link)
    			break
    		end
    	end
    	link = link or 'd:'..item -- fallback value
    	-- look for description
    	local desc = mw.wikibase.getDescription(item)
    	if desc then  -- add description if we have one
    		desc  = mw.text.nowiki(desc) -- add description as hover text
    		label = '<span title="' .. desc .. '">' .. label .. '</span>'
    	end
       return '[['..link..'|'..label..']]'
    end
    
    -------------------------------------------------------------------------------
    --[[
    Core component of many "get property value" functions
    Example: (core.parse_statements(entity:getBestStatements( prop ), nil) or {nil})[1] would 
    return the first best statement
    Inputs:
     1: statements - can be provided by:
       * entity:getBestStatements( prop )
       * entity:getAllStatements( prop )
       * mw.wikibase.getBestStatements( item, prop )
       * mw.wikibase.getAllStatements( item, prop )
     2: lang - language code (like "en"), if provided than item IDs will be 
         changed to a label
    Output:
     * table of strings or nil
    ]]
    function core.parseStatements(statements, lang)
    	local output = {}
    	for _, statement in ipairs(statements) do
    		if (statement.mainsnak.snaktype == "value") and (statement.rank ~= 'deprecated')  then
    			table.insert(output, core.parseSnak(statement.mainsnak, lang))
    		end
    	end
    	if #output==0 then return nil end
    	return output
    end
    
    -------------------------------------------------------------------------------
    --[[
    parse part of the statement called "snak"
    
    Inputs:
     1: snak - 
     2: lang - language code (like "en"), if provided than item IDs will be 
         changed to a label
    Output:
     * string
    ]]
    function core.parseSnak(snak, lang)
    	local val = snak.datavalue.value
    	if val.id then 
    		val = val.id
    		if lang ~= nil then
    			val = core.getLabel(val, lang)
    		end
    	elseif val.text then
    		val = val.text
    	elseif val.amount then
    		val = tonumber(val.amount)
    	elseif val.time then -- handle most dates (use [[Module:Wikidata date]]	for the rest
    		local gregorian = 'http://www.wikidata.org/entity/Q1985727'
    		if (val.calendarmodel==gregorian) and (mw.ustring.sub(val.time,1,1)=='+') and val.precision >= 9 then 
    			local trim = 3*math.min(val.precision,11) - 22 -- day (11)->11, month (10)->8, year (9)->5
    			val = mw.ustring.sub(val.time, 2, trim)    -- return YYYY-MM-DD, YYYY-MM or YYYY depending on precission
    		end
    	end
    	return val
    end
    
    return core