Open main menu
Home
Random
Recent changes
Special pages
Community portal
Settings
About Porn Base Central
Disclaimers
Porn Base Central
Search
User menu
Talk
Contributions
Create account
Log in
Editing
Module:Pornbasedata
Warning:
You are not logged in. Your IP address will be publicly visible if you make any edits. If you
log in
or
create an account
, your edits will be attributed to your username, along with other benefits.
Anti-spam check. Do
not
fill this in!
-- settings, may differ from project to project local fileDefaultSize = '267x400px'; local outputReferences = true; -- sources that shall be omitted if any preffered sources exists local deprecatedSources = { Q255 = true, -- Gemeinsame Normdatei Q256 = true, -- Find a Grave Q257 = true, -- BNF }; local preferredSources = { Q258 = true, -- Encyclopædia Britannica Online Q259 = true, -- Great Soviet Encyclopedia (1969—1978) }; -- Ссылки на используемые модули, которые потребуются в 99% случаев загрузки страниц (чтобы иметь на виду при переименовании) local moduleSources = require( 'Module:Sources' ) local WDS = require( 'Module:PornBaseDataSelectors' ); -- Константы local contentLanguageCode = mw.getContentLanguage():getCode(); local p = {}; local config = nil; local formatDatavalue, formatEntityId, formatRefs, formatSnak, formatStatement, formatStatementDefault, formatProperty, getSourcingCircumstances, getPropertyDatatype, getPropertyParams, throwError, toBoolean; local function copyTo( obj, target, skipEmpty ) for k, v in pairs( obj ) do if skipEmpty ~= true or ( v ~= nil and v ~= '' ) then target[k] = v; end end return target; end local function min( prev, next ) if ( prev == nil ) then return next; elseif ( prev > next ) then return next; else return prev; end end local function max( prev, next ) if ( prev == nil ) then return next; elseif ( prev < next ) then return next; else return prev; end end local function getConfig( section, code ) if config == nil then config = require( 'Module:Pornbasedata/config' ); end; if not config then config = {}; end if not section then return config; end if not code then return config[ section ] or {}; end if not config[ section ] then return nil; end return config[ section ][ code ]; end local function getCategoryByCode( code ) local value = getConfig( 'categories', code ); if not value or value == '' then return ''; end return '[[Category:' .. value .. ']]'; end local function splitISO8601(str) if 'table' == type(str) then if str.args and str.args[1] then str = '' .. str.args[1] else return 'unknown argument type: ' .. type( str ) .. ': ' .. table.tostring( str ) end end local Y, M, D = (function(str) local pattern = "(%-?%d+)%-(%d+)%-(%d+)T" local Y, M, D = mw.ustring.match( str, pattern ) return tonumber(Y), tonumber(M), tonumber(D) end) (str); local h, m, s = (function(str) local pattern = "T(%d+):(%d+):(%d+)%Z"; local H, M, S = mw.ustring.match( str, pattern); return tonumber(H), tonumber(M), tonumber(S); end) (str); local oh,om = ( function(str) if str:sub(-1)=="Z" then return 0,0 end; -- ends with Z, Zulu time -- matches ±hh:mm, ±hhmm or ±hh; else returns nils local pattern = "([-+])(%d%d):?(%d?%d?)$"; local sign, oh, om = mw.ustring.match( str, pattern); sign, oh, om = sign or "+", oh or "00", om or "00"; return tonumber(sign .. oh), tonumber(sign .. om); end )(str) return {year=Y, month=M, day=D, hour=(h+oh), min=(m+om), sec=s}; end local function parseTimeBoundaries( time, precision ) local s = splitISO8601( time ); if (not s) then return nil; end if ( precision >= 0 and precision <= 8 ) then local powers = { 1000000000 , 100000000, 10000000, 1000000, 100000, 10000, 1000, 100, 10 } local power = powers[ precision + 1 ]; local left = s.year - ( s.year % power ); return { tonumber(os.time( {year=left, month=1, day=1, hour=0, min=0, sec=0} )) * 1000, tonumber(os.time( {year=left + power - 1, month=12, day=31, hour=29, min=59, sec=58} )) * 1000 + 1999 }; end if ( precision == 9 ) then return { tonumber(os.time( {year=s.year, month=1, day=1, hour=0, min=0, sec=0} )) * 1000, tonumber(os.time( {year=s.year, month=12, day=31, hour=23, min=59, sec=58} )) * 1000 + 1999 }; end if ( precision == 10 ) then local lastDays = {31, 28.25, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; local lastDay = lastDays[s.month]; return { tonumber(os.time( {year=s.year, month=s.month, day=1, hour=0, min=0, sec=0} )) * 1000, tonumber(os.time( {year=s.year, month=s.month, day=lastDay, hour=23, min=59, sec=58} )) * 1000 + 1999 }; end if ( precision == 11 ) then return { tonumber(os.time( {year=s.year, month=s.month, day=s.day, hour=0, min=0, sec=0} )) * 1000, tonumber(os.time( {year=s.year, month=s.month, day=s.day, hour=23, min=59, sec=58} )) * 1000 + 1999 }; end if ( precision == 12 ) then return { tonumber(os.time( {year=s.year, month=s.month, day=s.day, hour=s.hour, min=0, sec=0} )) * 1000, tonumber(os.time( {year=s.year, month=s.month, day=s.day, hour=s.hour, min=59, sec=58} )) * 1000 + 19991999 }; end if ( precision == 13 ) then return { tonumber(os.time( {year=s.year, month=s.month, day=s.day, hour=s.hour, min=s.min, sec=0} )) * 1000, tonumber(os.time( {year=s.year, month=s.month, day=s.day, hour=s.hour, min=s.min, sec=58} )) * 1000 + 1999 }; end if ( precision == 14 ) then local t = tonumber(os.time( {year=s.year, month=s.month, day=s.day, hour=s.hour, min=s.min, sec=0} ) ); return { t * 1000, t * 1000 + 999 }; end error('Unsupported precision: ' .. precision ); end --[[ Преобразует строку в булевое значение Принимает: строковое значение (может отсутствовать) Возвращает: булевое значение true или false, если получается распознать значение, или defaultValue во всех остальных случаях ]] local function toBoolean( valueToParse, defaultValue ) if ( valueToParse ~= nil ) then if valueToParse == false or valueToParse == '' or valueToParse == 'false' or valueToParse == '0' then return false end return true end return defaultValue; end --[[ Функция для получения сущности (еntity) для текущей страницы Подробнее о сущностях см. d:Wikidata:Glossary/ru Принимает: строковый индентификатор (типа P18, Q42) Возвращает: объект таблицу, элементы которой индексируются с нуля ]] local function getEntityFromId( id ) local entity; local wbStatus; if id then wbStatus, entity = pcall( mw.wikibase.getEntityObject, id ) else wbStatus, entity = pcall( mw.wikibase.getEntityObject ); end return entity; end --[[ Внутрення функция для формирования сообщения об ошибке Принимает: ключ элемента в таблице config.errors (например entity-not-found) Возвращает: строку сообщения ]] local function throwError( key ) error( getConfig( 'errors', key ) ); end --[[ Функция для получения идентификатора сущностей Принимает: объект таблицу сущности Возвращает: строковый индентификатор (типа P18, Q42) ]] local function getEntityIdFromValue( value ) local prefix = '' if value['entity-type'] == 'item' then prefix = 'Q' elseif value['entity-type'] == 'property' then prefix = 'P' else throwError( 'unknown-entity-type' ) end return prefix .. value['numeric-id'] end -- проверка на наличие специилизированной функции в опциях local function getUserFunction( options, prefix, defaultFunction ) -- проверка на указание специализированных обработчиков в параметрах, -- переданных при вызове if options[ prefix .. '-module' ] or options[ prefix .. '-function' ] then -- проверка на пустые строки в параметрах или их отсутствие if not options[ prefix .. '-module' ] or not options[ prefix .. '-function' ] then throwError( 'unknown-' .. prefix .. '-module' ); end -- динамическая загруза модуля с обработчиком указанным в параметре local formatter = require( 'Module:' .. options[ prefix .. '-module' ] ); if formatter == nil then throwError( prefix .. '-module-not-found' ) end local fun = formatter[ options[ prefix .. '-function' ] ] if fun == nil then throwError( prefix .. '-function-not-found' ) end return fun; end return defaultFunction; end -- Выбирает свойства по property id, дополнительно фильтруя их по рангу local function selectClaims( context, options, propertySelector ) if ( not context ) then error( 'context not specified' ); end; if ( not options ) then error( 'options not specified' ); end; if ( not options.entity ) then error( 'options.entity is missing' ); end; if ( not propertySelector ) then error( 'propertySelector not specified' ); end; result = WDS.filter( options.entity.claims, propertySelector ); if ( not result or #result == 0 ) then return nil; end if options.limit and options.limit ~= '' and options.limit ~= '-' then local limit = tonumber( options.limit, 10 ); while #result > limit do table.remove( result ); end end return result; end --[[ Функция для получения значения свойства элемента в заданный момент времени. Принимает: контекст, элемент, временные границы, таблица ID свойства Возвращает: таблицу соответствующих значений свойства ]] local function getPropertyInBoundaries( context, entityId, boundaries, propertyIds, selectors ) if (type(entityId) ~= 'string') then error('type of entityId argument expected string, but was ' .. type(entityId)); end local results = {}; if not propertyIds or #propertyIds == 0 then return results; end for _, propertyId in ipairs( propertyIds ) do local selector = selectors[_]; local propertyClaims = mw.wikibase.getAllStatements( entityId, propertyId ); local fakeAllClaims = {}; fakeAllClaims[propertyId] = propertyClaims; local filteredClaims = WDS.filter( fakeAllClaims, selector .. '[rank:preferred, rank:normal]' ); if filteredClaims then for _, claim in pairs( filteredClaims ) do if not boundaries then table.insert( results, claim.mainsnak ); else local startBoundaries = p.getTimeBoundariesFromQualifier( context.frame, context, claim, 'P580' ); local endBoundaries = p.getTimeBoundariesFromQualifier( context.frame, context, claim, 'P582' ); if ( (startBoundaries == nil or ( startBoundaries[2] <= boundaries[1])) and (endBoundaries == nil or ( endBoundaries[1] >= boundaries[2]))) then table.insert( results, claim.mainsnak ); end end end end if #results > 0 then break; end end return results; end --[[ TODO ]] function p.getTimeBoundariesFromQualifier( frame, context, statement, qualifierId ) -- only support exact date so far, but need improvment local left = nil; local right = nil; if ( statement.qualifiers and statement.qualifiers[qualifierId] ) then for _, qualifier in pairs( statement.qualifiers[qualifierId] ) do local boundaries = context.parseTimeBoundariesFromSnak( qualifier ); if ( not boundaries ) then return nil; end left = min( left, boundaries[1] ); right = max( right, boundaries[2] ); end end if ( not left or not right ) then return nil; end return { left, right }; end --[[ TODO ]] function p.getTimeBoundariesFromQualifiers( frame, context, statement, qualifierIds ) if not qualifierIds then qualifierIds = { 'P582', 'P580', 'P585' }; end for _, qualifierId in ipairs( qualifierIds ) do local result = p.getTimeBoundariesFromQualifier( frame, context, statement, qualifierId ); if result then return result; end end return nil; end local CONTENT_LANGUAGE_CODE = mw.language.getContentLanguage():getCode(); local getLabelWithLang_DEFAULT_PROPERTIES = { "P1813", "P1448", "P1705" }; local getLabelWithLang_DEFAULT_SELECTORS = { 'P1813[language:' .. CONTENT_LANGUAGE_CODE .. ']', 'P1448[language:' .. CONTENT_LANGUAGE_CODE .. ']', 'P1705[language:' .. CONTENT_LANGUAGE_CODE .. ']' }; --[[ Функция для получения метки элемента в заданный момент времени. Принимает: контекст, элемент, временные границы Возвращает: текстовую метку элемента, язык метки ]] function getLabelWithLang( context, options, entityId, boundaries, propertyIds, selectors ) if (type(entityId) ~= 'string') then error('type of entityId argument expected string, but was ' .. type(entityId)); end if not entityId then return nil; end local langCode = CONTENT_LANGUAGE_CODE; -- name from label local label = nil; if ( options.text and options.text ~= '' ) then label = options.text; else if not propertyIds then propertyIds = getLabelWithLang_DEFAULT_PROPERTIES; selectors = getLabelWithLang_DEFAULT_SELECTORS; end -- name from properties local results = getPropertyInBoundaries( context, entityId, boundaries, propertyIds, selectors ); for _, result in pairs( results ) do if result.datavalue and result.datavalue.value then if result.datavalue.type == 'monolingualtext' and result.datavalue.value.text then label = result.datavalue.value.text; langCode = result.datavalue.value.language; break; elseif result.datavalue.type == 'string' then label = result.datavalue.value; break; end end end if (not label) then label, langCode = mw.wikibase.getLabelWithLang( entityId ); if not langCode then return nil; end end end return label, langCode; end --[[ Функция для оформления утверждений (statement) Подробнее о утверждениях см. d:Wikidata:Glossary/ru Принимает: таблицу параметров Возвращает: строку оформленного текста, предназначенного для отображения в статье ]] local function formatProperty( options ) -- Получение сущности по идентификатору local entity = getEntityFromId( options.entityId ) if not entity then return -- throwError( 'entity-not-found' ) end -- проверка на присутсвие у сущности заявлений (claim) -- подробнее о заявлениях см. d:Викиданные:Глоссарий if (entity.claims == nil) then return '' --TODO error? end -- improve options options.frame = g_frame; options.entity = entity; options.extends = function( self, newOptions ) return copyTo( newOptions, copyTo( self, {} ) ) end if ( options.i18n ) then options.i18n = copyTo( options.i18n, copyTo( getConfig( 'i18n' ), {} ) ); else options.i18n = getConfig( 'i18n' ); end -- create context local context = { entity = options.entity, formatSnak = formatSnak, formatPropertyDefault = formatPropertyDefault, formatStatementDefault = formatStatementDefault } context.cloneOptions = function( options ) local entity = options.entity; options.entity = nil; newOptions = mw.clone( options ); options.entity = entity; newOptions.entity = entity; newOptions.frame = options.frame; -- На склонированном фрейме frame:expandTemplate() return newOptions; end; context.formatProperty = function( options ) local func = getUserFunction( options, 'property', context.formatPropertyDefault ); return func( context, options ) end; context.formatStatement = function( options, statement ) return formatStatement( context, options, statement ) end; context.formatSnak = function( options, snak, circumstances ) return formatSnak( context, options, snak, circumstances ) end; context.formatRefs = function( options, statement ) return formatRefs( context, options, statement ) end; context.parseTimeFromSnak = function( snak ) if ( snak and snak.datavalue and snak.datavalue.value and snak.datavalue.value.time ) then return tonumber(os.time( splitISO8601( tostring( snak.datavalue.value.time ) ) ) ) * 1000; end return nil; end context.parseTimeBoundariesFromSnak = function( snak ) if ( snak and snak.datavalue and snak.datavalue.value and snak.datavalue.value.time and snak.datavalue.value.precision ) then return parseTimeBoundaries( snak.datavalue.value.time, snak.datavalue.value.precision ); end return nil; end context.getSourcingCircumstances = function( statement ) return getSourcingCircumstances( statement ) end; context.selectClaims = function( options, propertyId ) return selectClaims( context, options, propertyId ) end; return context.formatProperty( options ); end function formatPropertyDefault( context, options ) if ( not context ) then error( 'context not specified' ); end; if ( not options ) then error( 'options not specified' ); end; if ( not options.entity ) then error( 'options.entity missing' ); end; local claims; if options.property then -- TODO: Почему тут может не быть property? claims = context.selectClaims( options, options.property ); end if claims == nil then return '' --TODO error? end -- Обход всех заявлений утверждения и с накоплением оформленых предпочтительных -- заявлений в таблице local formattedClaims = {} for i, claim in ipairs(claims) do local formattedStatement = context.formatStatement( options, claim ) -- здесь может вернуться либо оформленный текст заявления, либо строка ошибки, либо nil if ( formattedStatement and formattedStatement ~= '' ) then formattedStatement = '<span class="wikidata-claim" data-wikidata-property-id="' .. string.upper( options.property ) .. '" data-wikidata-claim-id="' .. claim.id .. '">' .. formattedStatement .. '</span>' table.insert( formattedClaims, formattedStatement ) end end -- создание текстовой строки со списком оформленых заявлений из таблицы local out = mw.text.listToText( formattedClaims, options.separator, options.conjunction ) if out ~= '' then if options.before then out = options.before .. out end if options.after then out = out .. options.after end end return out end --[[ Функция для оформления одного утверждения (statement) Принимает: объект-таблицу утверждение и таблицу параметров Возвращает: строку оформленного текста с заявлением (claim) ]] function formatStatement( context, options, statement ) if ( not statement ) then error( 'statement is not specified or nil' ); end if not statement.type or statement.type ~= 'statement' then throwError( 'unknown-claim-type' ) end local functionToCall = getUserFunction( options, 'claim', context.formatStatementDefault ); return functionToCall( context, options, statement ); end function getSourcingCircumstances( statement ) if (not statement) then error('statement is not specified') end; local circumstances = {}; if ( statement.qualifiers and statement.qualifiers.P1480 ) then for i, qualifier in pairs( statement.qualifiers.P1480 ) do if ( qualifier and qualifier.datavalue and qualifier.datavalue.type == 'wikibase-entityid' and qualifier.datavalue.value and qualifier.datavalue.value['entity-type'] == 'item' ) then table.insert(circumstances, qualifier.datavalue.value.id) end end end return circumstances; end --[[ Функция для оформления одного утверждения (statement) Принимает: объект-таблицу утверждение, таблицу параметров, объект-функцию оформления внутренних структур утверждения (snak) и объект-функцию оформления ссылки на источники (reference) Возвращает: строку оформленного текста с заявлением (claim) ]] function formatStatementDefault( context, options, statement ) if (not context) then error('context is not specified') end; if (not options) then error('options is not specified') end; if (not statement) then error('statement is not specified') end; local circumstances = context.getSourcingCircumstances( statement ); options.qualifiers = statement.qualifiers; local result = context.formatSnak( options, statement.mainsnak, circumstances ); if ( options.qualifier and statement.qualifiers and statement.qualifiers[ options.qualifier ] ) then qualConfig = getPropertyParams( options.qualifier, nil, {}) if options.i18n then qualConfig.i18n = options.i18n end local qualifierValues = {}; for _, qualifierSnak in pairs( statement.qualifiers[ options.qualifier ] ) do local snakValue = context.formatSnak( qualConfig, qualifierSnak ); if snakValue and snakValue ~= '' then table.insert( qualifierValues, snakValue ); end end if ( #qualifierValues ) then if qualConfig.invisible then result = result .. table.concat( qualifierValues, ', ' ); else result = result .. ' (' .. table.concat( qualifierValues, ', ' ) .. ')'; end end end if ( result and result ~= '' and options.references ) then result = result .. context.formatRefs( options, statement ); end return result; end --[[ Функция для оформления части утверждения (snak) Подробнее о snak см. d:Викиданные:Глоссарий Принимает: таблицу snak объекта (main snak или же snak от квалификатора) и таблицу опций Возвращает: строку оформленного викитекста ]] function formatSnak( context, options, snak, circumstances ) circumstances = circumstances or {}; local hash = ''; local mainSnakClass = ''; if ( snak.hash ) then hash = ' data-wikidata-hash="' .. snak.hash .. '"'; else mainSnakClass = ' wikidata-main-snak'; end local before = '<span class="wikidata-snak ' .. mainSnakClass .. '"' .. hash .. '>' local after = '</span>' if snak.snaktype == 'somevalue' then if ( options['somevalue'] and options['somevalue'] ~= '' ) then result = options['somevalue']; else result = options.i18n['somevalue']; end elseif snak.snaktype == 'novalue' then if ( options['novalue'] and options['novalue'] ~= '' ) then result = options['novalue']; else result = options.i18n['novalue']; end elseif snak.snaktype == 'value' then result = formatDatavalue( context, options, snak.datavalue, snak.datatype ); for _, item in pairs(circumstances) do if options.i18n[item] then result = options.i18n[item] .. result; end end else throwError( 'unknown-snak-type' ); end if ( not result or result == '' ) then return nil; end return before .. result .. after; end --[[ Функция для оформления объектов-значений с географическими координатами Принимает: объект-значение и таблицу параметров, Возвращает: строку оформленного текста ]] function formatGlobeCoordinate( value, options ) -- проверка на требование в параметрах вызова на возврат сырого значения if options['subvalue'] == 'latitude' then -- широты return value['latitude'] elseif options['subvalue'] == 'longitude' then -- долготы return value['longitude'] elseif options['nocoord'] and options['nocoord'] ~= '' then -- если передан параметр nocoord, то не выводить координаты -- обычно это делается при использовании нескольких карточек на странице return '' else -- в противном случае формируются параметры для вызова шаблона {{coord}} -- нужно дописать в документации шаблона, что он отсюда вызывается, и что -- любое изменние его парамеров должно быть согласовано с кодом тут coord_mod = require( "Module:Coordinates" ); local globe = options.globe or '' if globe == '' and value['globe'] then globes = require( 'Module:Pornbasedata/Globes' ) globe = globes[value['globe']] or '' end local display = 'inline' if options.display and options.display ~= '' then display = options.display elseif ( options.property:upper() == 'P625' ) then display = 'title' end g_frame.args = {tostring(value['latitude']), tostring(value['longitude']), globe = globe, type = options.type and options.type or '', display = display } return coord_mod.coord(g_frame) end end --[[ Функция для оформления объектов-значений с файлами с Викисклада Принимает: объект-значение и таблицу параметров, Возвращает: строку оформленного текста ]] function formatCommonsMedia( value, options ) local image = value; local caption = ''; if options[ 'caption' ] and options[ 'caption' ] ~= '' then caption = options[ 'caption' ]; elseif options[ 'description' ] and options[ 'description' ] ~= '' then caption = options[ 'description' ]; end if caption ~= '' then caption = '<span data-wikidata-qualifier-id="P523" style="display:block">' .. caption .. '</span>'; end if not string.find( value, '[%[%]%{%}]' ) and not string.find( value, 'imagemap' ) then -- если в value не содержится викикод или imagemap, то викифицируем имя файла -- ищем слово imagemap в строке, потому что вставляется плейсхолдер: [[PHAB:T28213]] image = '[[File:' .. value .. '|frameless'; if options[ 'border' ] and options[ 'border' ] ~= '' then image = image .. '|border'; end local size = options[ 'size' ]; if size and size ~= '' then if not string.match( size, 'px$' ) and not string.match( size, 'пкс$' ) -- TODO: использовать перевод для языка вики then size = size .. 'px' end else size = fileDefaultSize; end image = image .. '|' .. size; if options[ 'alt' ] and options[ 'alt' ] ~= '' then image = image .. '|' .. options[ 'alt' ]; end image = image .. ']]'; if caption ~= '' then image = image .. '<br>' .. caption; end if options[ 'local_caption' ] and options[ 'local_caption' ] ~= '' then image = image .. getCategoryByCode( 'media-contains-local-caption' ) end else image = image .. caption .. getCategoryByCode( 'media-contains-markup' ); end if options.entity and options.fixdouble then local page = mw.title.getCurrentTitle() local txt = page:getContent() if txt and txt:match(':' .. value) and mw.title.getCurrentTitle():inNamespace(0) then image = image .. getCategoryByCode( 'media-contains-local-double' ) end end return image end --[[ Fonction for render math formulas @param string Value. @param table Parameters. @return string Formatted string. ]] function formatMath( value, options ) return options.frame:extensionTag{ name = 'math', content = value }; end --[[ Функция для оформления внешних идентификаторов Принимает: объект-значение и таблицу параметров, Возвращает: строку оформленного текста ]] local function formatExternalId( value, options ) local formatter = options.formatter; if not formatter or formatter == '' then local wbStatus, propertyEntity = pcall( mw.wikibase.getEntity, options.property:upper() ) if wbStatus == true and propertyEntity then local isGoodFormat = false; local statements = propertyEntity:getBestStatements( 'P1793' ); for _, statement in pairs( statements ) do if statement.mainsnak.snaktype == 'value' then local pattern = mw.ustring.gsub( statement.mainsnak.datavalue.value, '\\', '%' ); pattern = mw.ustring.gsub( pattern, '{%d+,?%d*}', '+' ); if ( string.find( pattern, '|' ) or string.find( pattern, '%)%?' ) or mw.ustring.match( value, '^' .. pattern .. '$' ) ~= nil ) then isGoodFormat = true; break; end end end if ( isGoodFormat == true ) then statements = propertyEntity:getBestStatements( 'P1630' ); for _, statement in pairs( statements ) do if statement.mainsnak.snaktype == 'value' then formatter = statement.mainsnak.datavalue.value; break end end end end end if formatter and formatter ~= '' then local link = mw.ustring.gsub( mw.ustring.gsub( formatter, '$1', value ), '.', { [' '] = '%20', ['+'] = '%2b' } ) local title = options.title if not title or title == '' then title = '$1' end title = mw.ustring.gsub( title, '$1', value ) return '[' .. link .. ' ' .. title .. ']' end return value end --[[ Функция для оформления числовых значений Принимает: объект-значение и таблицу параметров, Возвращает: строку оформленного текста ]] local function formatQuantity( value, options ) -- диапазон значений local amount = string.gsub( value['amount'], '^%+', '' ); local lang = mw.language.getContentLanguage(); local langCode = lang:getCode(); local function formatNum( number, sigfig ) sigfig = sigfig or 12 -- округление до 12 знаков после запятой, на 13-м возникает ошибка в точности local mult = 10^sigfig; number = math.floor( number * mult + 0.5 ) / mult; return string.gsub( lang:formatNum( number ), '^-', '−' ); end local out = formatNum( tonumber( amount ) ); if value.upperBound then local diff = tonumber( value.upperBound ) - tonumber( amount ) if diff > 0 then -- временная провека, пока у большинства значений не будет убрано ±0 -- Пробуем понять до какого знака округлять local integer, dot, decimals, expstr = value.upperBound:match( '^+?-?(%d*)(%.?)(%d*)(.*)' ) local prec if dot == '' then prec = -integer:match('0*$'):len() else prec = #decimals end bound = formatNum( diff, prec ) if string.match( bound, 'E%-(%d+)' ) then -- если в экспоненциальном формате digits = tonumber( string.match( bound, 'E%-(%d+)' ) ) - 2 bound = formatNum( diff * 10 ^ digits, prec ) bound = string.sub( bound, 0, 2 ) .. string.rep( '0', digits ) .. string.sub( bound, -string.len( bound ) + 2 ) end out = out .. ' ± ' .. bound end end if options.unit and options.unit ~= '' then if options.unit ~= '-' then out = out .. ' ' .. options.unit end elseif value.unit and string.match( value.unit, 'https://pornbasedata.com/wiki/Special:EntityData/' ) then local unitEntityId = string.gsub( value.unit, 'https://pornbasedata.com/wiki/Special:EntityData/', '' ); if unitEntityId ~= 'undefined' then local wbStatus, unitEntity = pcall( mw.wikibase.getEntity, unitEntityId ); if wbStatus == true and unitEntity then if unitEntity.claims.P2370 and unitEntity.claims.P2370[1].mainsnak.snaktype == 'value' and not value.upperBound and options.siConversion then conversionToSIunit = string.gsub( unitEntity.claims.P2370[1].mainsnak.datavalue.value.amount, '^%+', '' ); if math.floor( math.log10( conversionToSIunit )) ~= math.log10( conversionToSIunit ) then -- Если не степени десятки (переводить сантиметры в метры не надо!) outValue = tonumber( amount ) * conversionToSIunit if ( outValue > 0 ) then -- Пробуем понять до какого знака округлять local integer, dot, decimals, expstr = amount:match( '^(%d*)(%.?)(%d*)(.*)' ) local prec if dot == '' then prec = -integer:match('0*$'):len() else prec = #decimals end local adjust = math.log10( math.abs( conversionToSIunit )) + math.log10( 2 ) local minprec = 1 - math.floor( math.log10( outValue ) + 2e-14 ); out = formatNum( outValue, math.max( math.floor( prec + adjust ), minprec )); else out = formatNum( outValue, 0 ) end unitEntityId = string.gsub( unitEntity.claims.P2370[1].mainsnak.datavalue.value.unit, 'https://pornbasedata.com/wiki/Special:EntityData/', '' ); wbStatus, unitEntity = pcall( mw.wikibase.getEntity, unitEntityId ); end end local writingSystemElementId = 'Q266'; local langElementId = 'Q97'; local label = getLabelWithLang( context, options, unitEntity.id, nil, { "P192", "P558", "P558" }, { 'P192[language:' .. langCode .. ']', 'P558[P282:' .. writingSystemElementId .. ', P407:' .. langElementId .. ']', 'P558[!P282][!P407]' } ); out = out .. ' ' .. label; end end end return out; end local DATATYPE_CACHE = {} --[[ Get property datatype by ID. @param string Property ID, e.g. 'P123'. @return string Property datatype, e.g. 'commonsMedia', 'time' or 'url'. ]] local function getPropertyDatatype( propertyId ) if not propertyId or not string.match( propertyId, '^P%d+$' ) then return nil; end local cached = DATATYPE_CACHE[propertyId]; if (cached ~= nil) then return cached; end local wbStatus, propertyEntity = pcall( mw.wikibase.getEntity, propertyId ); if wbStatus ~= true or not propertyEntity then return nil; end mw.log("Loaded datatype " .. propertyEntity.datatype .. " of " .. propertyId .. ' from pornbasedata, consider passing datatype argument to formatProperty call or to Pornbasedata/config' ) DATATYPE_CACHE[propertyId] = propertyEntity.datatype; return propertyEntity.datatype; end local function formatLangRefs( options ) local langRefs = '' if ( options.qualifiers and options.qualifiers.P407 ) then for i, qualifier in pairs( options.qualifiers.P407 ) do if ( qualifier and qualifier.datavalue and qualifier.datavalue.type == 'wikibase-entityid' ) then local langRefEntity = getEntityFromId( qualifier.datavalue.value.id ) if ( langRefEntity and langRefEntity.claims ) then local langRefCodeClaims = WDS.filter( langRefEntity.claims, 'P218' ) if langRefCodeClaims then for _, claim in pairs( langRefCodeClaims ) do if ( claim.mainsnak and claim.mainsnak and claim.mainsnak.datavalue and claim.mainsnak.datavalue.type == 'string' ) then local langRefCode = claim.mainsnak.datavalue.value langRefs = langRefs .. '​' .. options.frame:expandTemplate{ title = 'ref-' ..langRefCode } end end end end end end end return langRefs end local function getDefaultValueFunction( datavalue, datatype ) -- вызов обработчиков по умолчанию для известных типов значений if datavalue.type == 'wikibase-entityid' then -- Entity ID return function( context, options, value ) return formatEntityId( context, options, getEntityIdFromValue( value ) ) end; elseif datavalue.type == 'string' then -- String if datatype and datatype == 'commonsMedia' then -- Media return function( context, options, value ) if options.caption and options.caption ~= '' then options.local_caption = options.caption; elseif options.description and options.description ~= '' then options.local_caption = options.description; end options.caption = '' options.description = '' if options.qualifiers and options.qualifiers.P523 then for i, qualifier in pairs( options.qualifiers.P523 ) do if ( qualifier and qualifier.datavalue and qualifier.datavalue.type == 'monolingualtext' and qualifier.datavalue.value and qualifier.datavalue.value.language == contentLanguageCode ) then options.caption = qualifier.datavalue.value.text options.description = qualifier.datavalue.value.text break end end end if options['appendTimestamp'] and options.qualifiers and options.qualifiers.P585 and options.qualifiers.P585[1] then local moment = formatDatavalue (context, options, options.qualifiers.P585[1].datavalue, 'time') if not options.caption or options.caption == '' then options.caption = moment options.description = moment else options.caption = options.caption .. ', ' .. moment options.description = options.description .. ', ' .. moment end end return formatCommonsMedia( value, options ) end; elseif datatype and datatype == 'external-id' then -- External ID return function( context, options, value ) return formatExternalId( value, options ) end elseif datatype and datatype == 'math' then -- Math formula return function( context, options, value ) return formatMath( value, options ) end elseif datatype and datatype == 'url' then -- URL return function( context, options, value ) local moduleUrl = require( 'Module:URL' ) local langRefs = formatLangRefs( options ) if not options.length or options.length == '' then options.length = math.max( 18, 25 - #langRefs ) end return moduleUrl.formatUrlSingle( context, options, value ) .. langRefs end end return function( context, options, value ) return value end; elseif datavalue.type == 'monolingualtext' then -- моноязычный текст (строка с указанием языка) return function( context, options, value ) if ( options.monolingualLangTemplate == 'lang' ) then if ( value.language == contentLanguageCode ) then return value.text; end return options.frame:expandTemplate{ title = 'lang-' .. value.language, args = { value.text } }; elseif ( options.monolingualLangTemplate == 'ref' ) then return '<span class="lang" lang="' .. value.language .. '">' .. value.text .. '</span>' .. options.frame:expandTemplate{ title = 'ref-' .. value.language }; else return '<span class="lang" lang="' .. value.language .. '">' .. value.text .. '</span>'; end end; elseif datavalue.type == 'globecoordinate' then -- географические координаты return function( context, options, value ) return formatGlobeCoordinate( value, options ) end; elseif datavalue.type == 'quantity' then return function( context, options, value ) return formatQuantity( value, options ) end; elseif datavalue.type == 'time' then return function( context, options, value ) local moduleDate = require( 'Module:Pornbasedata/date' ) return moduleDate.formatDate( context, options, value ); end; else -- во всех стальных случаях возвращаем ошибку throwError( 'unknown-datavalue-type' ) end end --[[ Функция для оформления значений (value) Подробнее о значениях см. d:Wikidata:Glossary/ru Принимает: объект-значение и таблицу параметров, Возвращает: строку оформленного текста ]] function formatDatavalue( context, options, datavalue, datatype ) if ( not context ) then error( 'context not specified' ); end; if ( not options ) then error( 'options not specified' ); end; if ( not datavalue ) then error( 'datavalue not specified' ); end; if ( not datavalue.value ) then error( 'datavalue.value is missng' ); end; -- проверка на указание специализированных обработчиков в параметрах, -- переданных при вызове context.formatValueDefault = getDefaultValueFunction( datavalue, datatype ); local functionToCall = getUserFunction( options, 'value', context.formatValueDefault ); return functionToCall( context, options, datavalue.value ); end local DEFAULT_BOUNDARIES = { os.time() * 1000, os.time() * 1000}; --[[ Функция для оформления идентификатора сущности Принимает: строку индентификатора (типа Q42) и таблицу параметров, Возвращает: строку оформленного текста ]] function formatEntityId( context, options, entityId ) -- получение локализованного названия local boundaries = nil if options.qualifiers then boundaries = p.getTimeBoundariesFromQualifiers( frame, context, { qualifiers = options.qualifiers } ) end if not boundaries then boundaries = DEFAULT_BOUNDARIES; end local label, labelLanguageCode = getLabelWithLang( context, options, entityId, boundaries ) -- определение соответствующей показываемому элементу категории local category = p.extractCategory( context, options, { id = entityId } ) -- получение ссылки по идентификатору local link = mw.wikibase.sitelink( entityId ) if link then -- ссылка на категорию, а не добавление страницы в неё if mw.ustring.match( link, '^' .. mw.site.namespaces[ 14 ].name .. ':' ) then link = ':' .. link end if label and not options.rawArticle then local a = link == label and ('[[' .. link .. ']]') or '[[' .. link .. '|' .. label .. ']]'; if ( contentLanguageCode ~= labelLanguageCode ) then return a .. getCategoryByCode( 'links-to-entities-with-missing-local-language-label' ) .. category; else return a .. category; end else return '[[' .. link .. ']]' .. category; end end if label then -- красная ссылка -- TODO: разобраться, почему не всегда есть options.frame local title = mw.title.new( label ); if title and not title.exists and options.frame then local redLink = options.frame:expandTemplate{ title='Ш:Красная ссылка с рыбой', args = { entityId, label } }; return redLink .. '<sup>[[:d:' .. entityId .. '|[d]]]</sup>' .. category; end -- TODO: перенести до проверки на существование статьи local sup = ''; if ( not options.format or options.format ~= 'text' ) and entityId ~= 'Q267' and entityId ~= 'Q5' -- TODO: переписать на format=text then sup = '' end -- одноимённая статья уже существует - выводится текст и ссылка на ВД return '<span class="iw" data-title="' .. label .. '">' .. label .. sup .. '</span>' .. category end -- сообщение об отсутвии локализованного названия -- not good, but better than nothing return '[[:d:' .. entityId .. '|' .. entityId .. ']]<span style="border-bottom: 1px dotted; cursor: help; white-space: nowrap" title="В PBD нет русской подписи к элементу. Вы можете помочь, указав русский вариант подписи.">?</span>' .. getCategoryByCode( 'links-to-entities-with-missing-label' ) .. category; end --[[ Функция для формирования категории на основе wikidata/config ]] function p.extractCategory( context, options, value ) if ( not options.category ) then return ''; end local propertyId = string.gsub( options.category, '([^Pp0-9].*)$', ''); local wbStatus, claims = pcall( mw.wikibase.getAllStatements, value.id, propertyId ); if ( wbStatus ~= true or not claims ) then return ''; end allClaims = {} allClaims[ propertyId ] = claims claims = WDS.filter( allClaims, options.category ) if not claims then return ''; end for _, claim in pairs( claims ) do if ( claim and claim.mainsnak and claim.mainsnak.datavalue and claim.mainsnak.datavalue.type == 'wikibase-entityid' ) then local catEntityId = claim.mainsnak.datavalue.value.id; local wbStatus, catSiteLink = pcall( mw.wikibase.getSitelink, catEntityId ); if ( wbStatus == true and catSiteLink ) then return '[[' .. catSiteLink .. ']]'; end end end return ''; end --[[ Функция для оформления утверждений (statement) Подробнее о утверждениях см. d:Wikidata:Glossary/ru Принимает: таблицу параметров Возвращает: строку оформленного текста, предназначенного для отображения в статье ]] -- устаревшее имя, не использовать function p.formatStatements( frame ) return p.formatProperty( frame ); end --[[ Получение параметров, которые обычно используются для вывода свойства. ]] function getPropertyParams( propertyId, datatype, params ) local config = getConfig(); -- Различные уровни настройки параметров, по убыванию приоритета local propertyParams = {}; -- 1. Параметры, указанные явно при вызове if params then for key, value in pairs( params ) do if value ~= '' then propertyParams[ key ] = value; end end end -- 2. Настройки конкретного параметра if config[ 'properties' ] and config[ 'properties' ][ propertyId ] then for key, value in pairs( config[ 'properties' ][ propertyId ] ) do if propertyParams[ key ] == nil then propertyParams[ key ] = value; end end end -- 3. Указанный пресет настроек if propertyParams[ 'preset' ] and config[ 'presets' ] and config[ 'presets' ][ propertyParams[ 'preset' ] ] then for key, value in pairs( config[ 'presets' ][ propertyParams[ 'preset' ] ] ) do if propertyParams[ key ] == nil then propertyParams[ key ] = value; end end end local datatype = datatype or params.datatype or propertyParams.datatype or getPropertyDatatype( propertyId ); if propertyParams.datatype == nil then propertyParams.datatype = datatype; end -- 4. Настройки для типа данных if datatype and config[ 'datatypes' ] and config[ 'datatypes' ][ datatype ] then for key, value in pairs( config[ 'datatypes' ][ datatype ] ) do if propertyParams[ key ] == nil then propertyParams[ key ] = value; end end end -- 5. Общие настройки для всех свойств if config[ 'global' ] then for key, value in pairs( config[ 'global' ] ) do if propertyParams[ key ] == nil then propertyParams[ key ] = value; end end end return propertyParams; end function p.formatProperty( frame ) local args = frame.args -- проверка на отсутствие обязательного параметра property if not args.property then throwError( 'property-param-not-provided' ) end local override; local propertyId = mw.language.getContentLanguage():ucfirst( string.gsub( args.property, '([^Pp0-9].*)$', function(w) if string.sub( w, 1, 1 ) == '~' then override = w; end return ''; end ) ) args = getPropertyParams( propertyId, nil, args ); if (override) then args[override:match('[,~]([^=]*)=')] = override:match('=(.*)') args['property'] = propertyId end local datatype = args.datatype; -- проброс всех параметров из шаблона {wikidata} и параметра from откуда угодно p_frame = frame while p_frame do if p_frame:getTitle() == mw.site.namespaces[10].name .. ':PBD' then copyTo( p_frame.args, args, true ); end if p_frame.args and p_frame.args.from and p_frame.args.from ~= '' then args.entityId = p_frame.args.from; end p_frame = p_frame:getParent(); end args.plain = toBoolean( args.plain, false ); args.nocat = toBoolean( args.nocat, false ); args.references = toBoolean( args.references, true ); -- если значение передано в параметрах вызова то выводим только его if args.value and args.value ~= '' then -- специальное значение для скрытия Викиданных if args.value == '-' then return '' end local value = args.value -- опция, запрещающая оформление значения, поэтому никак не трогаем if args.plain then return value end -- обработчики по типу значения local wrapperExtraArgs = '' if args['value-module'] and args['value-function'] and not string.find( value, '[%[%]%{%}]' ) then local func = getUserFunction( args, 'value' ); value = func( {}, args, value ); elseif datatype == 'commonsMedia' then value = formatCommonsMedia( value, args ); elseif datatype == 'external-id' and not string.find( value, '[%[%]%{%}]' ) then wrapperExtraArgs = wrapperExtraArgs .. ' data-wikidata-external-id="' .. mw.text.encode( value ).. '"'; value = formatExternalId( value, args ); elseif datatype == 'math' then value = formatMath( value, args ); elseif datatype == 'url' then local moduleUrl = require( 'Module:URL' ); if not args.length or args.length == '' then args.length = 25 end value = moduleUrl.formatUrlSingle( nil, args, value ); end -- оборачиваем в тег для JS-функций if string.match( propertyId, '^P%d+$' ) then value = mw.text.trim( value ) -- временная штрафная категория для исправления табличных вставок if ( propertyId ~= 'P166' and string.match( value, '<t[dr][ >]' ) and not string.match( value, '<table >]' ) and not string.match( value, '^%{%|' ) ) then value = value .. getCategoryByCode( 'value-contains-table' ) else -- значений с блочными тегами остаются блоком, текст встраиваем в строку if ( string.match( value, '\n' ) or string.match( value, '<t[dhr][ >]' ) or string.match( value, '<div[ >]' ) ) then value = '<div class="no-wikidata"' .. wrapperExtraArgs .. ' data-wikidata-property-id="' .. propertyId .. '">\n' .. value .. '</div>' else value = '<span class="no-wikidata"' .. wrapperExtraArgs .. ' data-wikidata-property-id="' .. propertyId .. '">' .. value .. '</span>' end end end -- добавляем категорию-маркер if not args.nocat then local pageTitle = mw.title.getCurrentTitle(); if pageTitle.namespace == 0 then value = value .. getCategoryByCode( 'local-value-present' ); end end return value end if ( args.plain ) then -- вызова стандартного обработчика без оформления, если передана опция plain local callArgs = { propertyId }; if args.entityId then callArgs.from = args.entityId; end return frame:callParserFunction( '#property', callArgs ); end g_frame = frame -- после проверки всех аргументов -- вызов функции оформления для свойства (набора утверждений) return formatProperty( args ) end --[[ Функция оформления ссылок на источники (reference) Подробнее о ссылках на источники см. d:Wikidata:Glossary/ru Экспортируется в качестве зарезервированной точки для вызова из функций-расширения вида claim-module/claim-function через context Вызов из других модулей напрямую осуществляться не должен (используйте frame:expandTemplate вместе с одним из специлизированных шаблонов вывода значения свойства). Принимает: объект-таблицу утверждение Возвращает: строку оформленных ссылок для отображения в статье ]] function formatRefs( context, options, statement ) if ( not context ) then error( 'context not specified' ); end; if ( not options ) then error( 'options not specified' ); end; if ( not options.entity ) then error( 'options.entity missing' ); end; if ( not statement ) then error( 'statement not specified' ); end; if ( not outputReferences ) then return ''; end local references = {}; if ( statement.references ) then local allReferences = statement.references; local hasPreferred = false; local displayCount = 0; for _, reference in pairs( statement.references ) do if ( reference.snaks and reference.snaks.P248 and reference.snaks.P248[1] and reference.snaks.P248[1].datavalue and reference.snaks.P248[1].datavalue.value.id ) then local entityId = reference.snaks.P248[1].datavalue.value.id; if ( preferredSources[entityId] ) then hasPreferred = true; end end end for _, reference in pairs( statement.references ) do local display = true; if ( hasPreferred ) then if ( reference.snaks and reference.snaks.P248 and reference.snaks.P248[1] and reference.snaks.P248[1].datavalue and reference.snaks.P248[1].datavalue.value.id ) then local entityId = reference.snaks.P248[1].datavalue.value.id; if ( deprecatedSources[entityId] ) then display = false; end end end if ( display == true ) then if ( displayCount > 2 ) then if ( options.entity and options.property ) then table.remove( references ); local moreReferences = '<sup>[[d:' .. options.entity.id .. '#' .. string.upper( options.property ) .. '|[…]]]</sup>'; table.insert( references, moreReferences ); end break; end; local refText = moduleSources.renderReference( g_frame, options.entity, reference ); if ( refText ~= '' ) then table.insert( references, refText ); displayCount = displayCount + 1; end end end end return table.concat( references ); end return p
Summary:
Please note that all contributions to Porn Base Central may be edited, altered, or removed by other contributors. If you do not want your writing to be edited mercilessly, then do not submit it here.
You are also promising us that you wrote this yourself, or copied it from a public domain or similar free resource (see
Porn Base Central:Copyrights
for details).
Do not submit copyrighted work without permission!
Cancel
Editing help
(opens in new window)
Preview page with this template
Template used on this page:
Module:Pornbasedata/doc
(
view source
)