Moduł:Cytuj
Dokumentacja dla tego modułu może zostać utworzona pod nazwą Moduł:Cytuj/opis
local resources = mw.loadData("Moduł:Cytuj/dane") local access = mw.loadData("Moduł:Cytuj/dostymp") local function checkUri(uri) local urilen = #uri for _,v in ipairs(resources.supportedUriSchemas) do if (#v < urilen) and (string.lower(string.sub(uri,1, #v)) == v) then return not string.match(uri, '%s') end end end local function softNoWiki(text) local result, count = string.gsub(text, "['%[%]{|}\"]", { ["\""] = """, ["'"] = "'", ["["] = "[", ["]"] = "]", ["{"] = "{", ["|"] = "|", ["}"] = "}"}) return result end local function escapeUrl(url) local result, count = string.gsub(url, "[ '%[%]]", { [" "] = "%20", ["'"] = "%27", ["["] = "%5B", ["]"] = "%5D"}) return result end local function plainText(text) local result, count = string.gsub(text, "</?[Ss][Pp][Aa][Nn][^>]*>", "") return result end local function first(data) return type(data) == "table" and data[1] or data end local function determineMode(p) local detector = {} local count = 0 for i, v in ipairs(resources.modes) do detector[i] = v count = count + 1 end detector[1] = false -- skip 'auto' count = count - 1 for k, v in pairs(resources.params) do local arg = p.args[v.name] for i, w in ipairs(v.used) do if not w and arg then -- unexpected argument if detector[i] then detector[i] = false count = count - 1 if count == 0 then -- the mode cannot be determined break end end end end if count == 0 then -- the mode cannot be determined break end end for i, v in ipairs(detector) do if detector[i] then -- the type is succesfully determined -- but in case more than one is possible -- use only the first one -- include COinS format if this is the only determined type return i, count == 1 and resources.COinS[i] or false end end -- in case nothing is selected -- use the auto mode as default fallback return 1 end local authorMetatable = {} local authorMethodtable = {} authorMetatable.__index = authorMethodtable local function checkPatterns(author, prefixes, suffixes) if author.exact then return false end if author.prefix and prefixes then for _, v in ipairs(prefixes) do if mw.ustring.match(author.prefix, v) then return true end end end if author.suffix and suffixes then for _, v in ipairs(suffixes) do if mw.ustring.match(author.suffix, v) then return true end end end return false end authorMethodtable.format = function(data, namefirst) if data.exact then return data.exact end if namefirst and data.familynamefirst then namefirst = false end local builder = mw.html.create() local name = data.name and (#data.name > 0) local initials = data.nameinitials and (#data.nameinitials > 0) local namehint = nil if name and initials and (data.name ~= data.nameinitials) then namehint = data.name end if not data.familynamefirst and (name or initials) then local before = namefirst and builder or builder:tag("span"):addClass("cite-name-before") if name then before:tag("span"):addClass("cite-name-full"):wikitext(softNoWiki(data.name)) end if initials then before:tag("span"):css("display", "none"):addClass("cite-name-initials"):attr("title", namehint):wikitext(softNoWiki(data.nameinitials)) end before:wikitext(" ") end builder:tag("span"):addClass("cite-lastname"):wikitext(softNoWiki(data.lastname)) if not namefirst and (name or initials) then local after = data.familynamefirst and builder or builder:tag("span"):css("display", "none"):addClass("cite-name-after") after:wikitext(" ") if name then after:tag("span"):addClass("cite-name-full"):wikitext(softNoWiki(data.name)) end if initials then after:tag("span"):addClass("cite-name-initials"):attr("title", namehint):wikitext(softNoWiki(data.nameinitials)) end if data.js then after:wikitext(",") end end if data.js then builder:wikitext(" ", data.js) end return tostring(builder) end authorMethodtable.towiki = function(data) if data.exact then return data.exact end local result = {} local name = data.name and (#data.name > 0) if not data.familynamefirst and name then table.insert(result,softNoWiki(data.name)) table.insert(result, " ") end table.insert(result, softNoWiki(data.lastname)) if data.familynamefirst and name then table.insert(result, " ") table.insert(result, softNoWiki(data.name)) end return table.concat(result) end local function makeInitials(name) local nameinitials = mw.ustring.gsub(name, "(%w[Hh]?)[%w]*%.?([%s%-–—]?)%s*", "%1. ") -- ôstŏw poczōntki słōw (jedna litera + ôpcyjōnalne 'h' pō nij) nameinitials = mw.ustring.gsub(nameinitials, "%f[%w]%l%.%s", "") -- wyciep inicjały z małych liter nameinitials = mw.ustring.gsub(nameinitials, "([^C%W])[Hh]%.?%s", "%1. ") -- wyciep druge 'h', jeźli nie zaczynŏ sie na 'C' nameinitials = mw.ustring.gsub(nameinitials, "(%u[Hh]?)[%.%s]*", "%1.") -- dodej kropki, co brakujōm, i wyciep niyprzidajne spacyje return mw.text.trim(nameinitials) end local function fixInitials(name) local result, _ = mw.ustring.gsub(name, "^(%uh?)%.?%s+(%uh?)%.", "%1.%2.") -- poprŏw inicjały na poczōntku result, _ = mw.ustring.gsub(result, "%f[%a](%uh?)%.%s+(%uh?)%.", "%1.%2.") -- poprŏw inicjały we postrzodku result, _ = mw.ustring.gsub(result, "%f[%a](%uh?)%.%s+(%uh?)%.", "%1.%2.") -- poprŏw dalsze inicjały we postrzodku return result end local function parseAuthor(author) local result = {} if string.match(author, "\127") then -- wpisy z <nowiki> niy sōm analizowane result.exact = author setmetatable(result, authorMetatable) return result end local author = mw.text.trim(author) local a = string.gsub(author, "\\[\\%.:]", { ["\\\\"]="\\", ["\\."]=",", ["\\:"]=";", }) if a ~= author then result.exact = a setmetatable(result, authorMetatable) return result end if resources.exactAuthors[author] then result.exact = author setmetatable(result, authorMetatable) return result end local exactName = mw.ustring.match(author, "^%s*%*%s*(.*)$") if exactName then result.exact = mw.text.trim(exactName) if #result.exact == 0 then return nil end setmetatable(result, authorMetatable) return result end local prefix0, link, description, suffix0 = mw.ustring.match(author, "^(.-)%[%[(.-)%|(.-)%]%](.*)$") if prefix0 then result.link = link author = description else prefix0, link, suffix0 = mw.ustring.match(author, "^(.-)%[%[(.-)%]%](.*)$") if prefix0 then author = link result.link = link else prefix0 = "" suffix0 = "" end end local prefix1, rest = mw.ustring.match(author, "^([%l%p%s]+)(.+)$") if not prefix1 then rest = author prefix1 = "" end local prefix = mw.text.trim(prefix0.." "..prefix1) if #prefix > 0 then if mw.ustring.sub(prefix, -1) == "#" then result.familynamefirst = true prefix = mw.text.trim(mw.ustring.match(prefix, "^(.-)#$")) end if #prefix > 0 then result.prefix = mw.ustring.gsub(prefix, "%s+", " ") -- collapse spaces end end local rest2, suffix = mw.ustring.match(rest, "^([%w%-%.%s]-)%s([%l%p%s]-)$") if not suffix then rest2 = rest suffix = "" end suffix = mw.text.trim(suffix.." "..suffix0) if #suffix > 0 then result.suffix = mw.ustring.gsub(suffix, "%s+", " ") -- collapse spaces suffix = " "..result.suffix for i, v in ipairs(resources.js) do if mw.ustring.match(suffix, v[1]) then result.suffix = mw.text.trim(mw.ustring.gsub(suffix, v[1], "")) result.js = v[2] break end end else for i, v in ipairs(resources.js) do if mw.ustring.match(rest2, v[1]) then rest2 = mw.text.trim(mw.ustring.gsub(rest2, v[1], "")) result.js = v[2] break end end end local lastname, name = mw.ustring.match(rest2, "%s*([^,]-)%s*,%s*(.-)%s*$") if not lastname then if result.familynamefirst then lastname, name = mw.ustring.match(rest2, "%s*(%u[%l%d%p]*)%s+(.-)%s*$") else local prefix2 name, lastname, prefix2 = mw.ustring.match(rest2, "%s*(.-)%s+((%l[%l%p]%l?)%u[%w%p]-)%s*$") if not resources.lastnamePrefixes[prefix2] then name, lastname = mw.ustring.match(rest2, "%s*(.-)%s+(%u[%w%p]-)%s*$") end end elseif resources.lastnamePrefixes[prefix1] then lastname = prefix1 .. lastname elseif resources.lastnamePrefixes[prefix1] == false then name = name.." "..mw.text.trim(prefix1) end if not lastname then result.lastname = mw.text.trim(rest2) else result.name = fixInitials(name) result.lastname = lastname result.nameinitials = makeInitials(name) end if #result.lastname == 0 then return nil end setmetatable(result, authorMetatable) return result end local function parseDate(date, month, year, patch) local result = {} -- parse full date local y, m, d = false, false, false y, m, d = mw.ustring.match(date, "(%d%d%d%d)[%-%s%./](%d%d?)[%-%s%./](%d%d?)") if y and patch and (date == (y.."-01-01")) then result.year = tonumber(y) result.month = false result.day = false return result, true end if not y then d, m, y = mw.ustring.match(date, "(%d%d?)[%-%s%.](%d%d?)[%-%s%.](%d%d%d%d)") if not y then y, m, d = mw.ustring.match(date, "(%d%d%d%d)%s*(%w+)%s*(%d%d?)") if not y then d, m, y = mw.ustring.match(date, "(%d%d?)%s*(%w+)%s*(%d%d%d%d)") end if m then m = resources.monthparser[mw.ustring.lower(m)] if not m then y = false m = false d = false end end end end if y then y = tonumber(y) m = tonumber(m) d = tonumber(d) end if y and ((d > 31) or (m > 12) or (d < 1) or (m < 1)) then y = false m = false d = false elseif y then result.year = y result.month = m result.day = d return result, false end -- parse year and month y, m = mw.ustring.match(date, "(%d%d%d%d)[%-%s%./](%d%d?)") if not y then m, y = mw.ustring.match(date, "(%d%d?)[%-%s%./](%d%d%d%d)") if not y then y, m = mw.ustring.match(date, "(%d%d%d%d)%s*(%w+)") if not y then m, y = mw.ustring.match(date, "(%w+)%s*(%d%d%d%d)") end if m then m = resources.monthparser[mw.ustring.lower(m)] if not m then y = false m = false end end end end if y then y = tonumber(y) m = tonumber(m) end if y and ((m > 12) or (m < 1)) then y = false m = false elseif y then result.year = y result.month = m return result, false end -- try any method to extract year or month if not y then y = mw.ustring.match(date, "[%s%p%-–]?(%d%d%d%d)[%s%p%-–]?") if y then y = tonumber(y) end if y then result.year = y end end if y then if not m then m = mw.ustring.match(date, "[%s%p%-–]?(%w+)[%s%p%-–]?") if m then m = resources.monthparser[mw.ustring.lower(m)] end if m then result.month = m end end else -- reset only month result.month = nil end if y then return result, false end end local function collectAuthors(author, checkForAltFormat) if not author then return end function findUtf8CharAt(text, at) local back = false if at < 0 then at = #text + at + 1 back = true end while at > 1 do local b = string.byte(text, at, at) if (b < 128) or (b >= 192) then break end at = at - 1 end return back and at - #text - 1 or at end local etal = false local authorTail = #author <= 50 and author or string.sub(author, findUtf8CharAt(author, -50)) for i, p in ipairs(resources.etalPatterns) do local a, e = string.match(authorTail, p) if a then author = string.sub(author, 1, #author-#e) etal = e break end end function decodeEntity(s) local result = nil local hex = string.match(s, "^&#[xX]([0-9A-Fa-f]+);$") if hex then result = mw.ustring.char(tonumber(hex, 16)) else local dec = string.match(s, "^&#([0-9]+);$") if dec then result = mw.ustring.char(tonumber(dec, 10)) elseif resources.htmlEntities[s] then result = mw.ustring.char(resources.htmlEntities[s]) else return string.gsub(s, ";", "\\:") end end if result == ";" then return "\\:" elseif result == "," then return "\\." elseif result == "\\" then return "\\\\" else return result end end local authorHead = #author <= 500 and author or string.sub(author, 1, findUtf8CharAt(author, 500) - 1) local result = {} local esc1 = string.gsub(authorHead, "\\", "\\\\") local esc2 = string.gsub(esc1, "&#?[a-zA-Z0-9]+;", decodeEntity) local splitter = string.match(esc2, ";") and ";" or "," local authors = mw.text.split(esc2, splitter.."%s*", false) local nth = false local count = #authors if (#authorHead < #author) and (count > 4) then if count > 5 then table.remove(authors, count) count = count - 1 end local at, _ = string.find(authorHead, authors[count], 1, true) nth = string.sub(author, at) table.remove(authors, count) count = count - 1 end local alt = false if (splitter == ",") and checkForAltFormat then local altAuthors = {} alt = true for i, v in ipairs(authors) do local n0 = "" local s, n = mw.ustring.match(v, "^(%u%l+)%s(%u+)%.?$") if not s then s, n = mw.ustring.match(v, "^(%u%l+[%s%-–]%u%l+)%s(%u+)%.?$") end if not s then n0, s, n = mw.ustring.match(v, "^(%l%l%l?)%s(%u%l+)%s(%u+)%.?$") -- de, von, van, der etc. end if not s then alt = false break end local initials, _ = mw.ustring.gsub(n, "(%u)", "%1.") if #n0 > 0 then n0 = " "..n0 end table.insert(altAuthors, s..", "..initials..n0) end if alt then authors = altAuthors splitter = ";" end end for i, v in ipairs(authors) do local author = parseAuthor(v) if author then table.insert(result, author) end end if #result == 0 then return end local check = false if alt then check = "alt" elseif (#result == 2) and (separator == ",") then check = true end return { items = result, more=nth, comma = check, etal = etal, separator=splitter.." " } end local function formatAuthors(authors, useDecorations, nextgroup, etalForm) local count = #authors.items if count == 0 then return nil, false end local suffix = function(author) if useDecorations then for _, v in ipairs(resources.authorFunc) do if checkPatterns(author, v.prefixes, v.suffixes) then return v.append end end end return "" end local formatter = function(author) local a = author:format(nextgroup) local r = author.link and ("[["..author.link.."|"..a.."]]") or a return r..suffix(author) end if count == 1 then local a1 = formatter(authors.items[1]) local etal = authors.etal and (etalForm or " i inni") or "" return a1..etal, false end local result = {} table.insert(result, formatter(authors.items[1])) if not authors.etal and (count <= 3) then table.insert(result, ", "); table.insert(result, formatter(authors.items[2])) if count == 3 then table.insert(result, ", "); table.insert(result, formatter(authors.items[3])) end return table.concat(result, ""), false end local title = {} for i = 1, count do table.insert(title, authors.items[i]:towiki()..suffix(authors.items[i])) end if authors.more then table.insert(title, authors.more) end table.insert(result, "<span class=\"cite-at-al\" title=\"") table.insert(result, table.concat(title, authors.separator)) if authors.etal then table.insert(result, etalForm or " i inni") end table.insert(result, "\">") table.insert(result, etalForm or " i inni") table.insert(result, "</span>") return table.concat(result, ""), true end local function collectLanguages(value) if value then local result = {} local values = mw.text.split(value, "%s+") for _, v in ipairs(values) do if #v > 0 then table.insert(result, v) end end if #result > 0 then return result end end return nil end local function splitWikiLink(text) local link, description = mw.ustring.match(text, "^%[%[(.-)%|(.-)%]%]$") if link then return description, link, false end local link = mw.ustring.match(text, "^%[%[(.-)%]%]$") if link then return link, link, false end local link, description = mw.ustring.match(text, "^%[(%S*)%s+(.-)%]$") if link and checkUri(link) then return description, false, link end return text, false, false end local function detectArchive(url) local y, m, d, link = mw.ustring.match(url, "^https?://web%.archive%.org/web/(%d%d%d%d)(%d%d)(%d%d)%d%d%d%d%d%d/(https?://.*)$") if y then return link, y.."-"..m.."-"..d end local y, m, d, link = mw.ustring.match(url, "^https?://webarchive%.nationalarchives%.gov%.uk/(%d%d%d%d)(%d%d)(%d%d)%d%d%d%d%d%d/(https?://.*)$") if y then return link, y.."-"..m.."-"..d end return false, false end local function isAutoGeneratedUrl(url) local address = string.gsub(url, "^https?:", "") for k, v in pairs(resources.params) do if v.link then local links = type(v.link) == "table" and v.link or { v.link } for _, vlink in ipairs(links) do local prefix = string.gsub(vlink, "^https?:", "") if (#address > #prefix) and (string.sub(address, 1, #prefix) == prefix) then return true end end end end return false end local function loadCitation(frame, mode) local result = {} -- copy parameters for k, v in pairs(resources.params) do if v.used[mode] then local value = frame.args[v.name] if value then value = mw.text.trim(value) if #value > 0 then result[k] = value end end if (v.used[mode] == "!") and not result[k] then -- simulate missing mandatory parameter result[k] = "{{{"..v.name.."}}}" if not result.missing then result.missing = v.name end end end end -- check url argument if result.url then if not checkUri(result.url) then local unstrip = mw.text.unstripNoWiki( result.url ) result.url = false if unstrip then result.urlnowiki = checkUri(unstrip) end end end -- translate some parameters local altAuthorParser = false if result.journal and result.pmid and result.author and not result.chapterauthor and not result.editor and not result.others then altAuthorParser = true end result.chapterauthor = collectAuthors(result.chapterauthor, false) result.author = collectAuthors(result.author, altAuthorParser) result.lang = collectLanguages(result.lang) result.editor = collectAuthors(result.editor, false) result.others = collectAuthors(result.others, false) -- parse main bibliographic date if result.date then local bibDate = false local bibDateHint = false local coinsDate = false local odnDate = false for _, v in ipairs(resources.bibDates) do for _, p in ipairs(v.patterns) do local bib, c = mw.ustring.gsub(result.date, p, v.show) if bib and (c > 0) then bibDate = bib bibDateHint = v.hint if v.coins then local cd, cc = mw.ustring.gsub(result.date, p, v.coins) if cd and (cc > 0) then coinsDate = cd end end if v.odn then local od, oc = mw.ustring.gsub(result.date, p, v.odn) if od and (oc > 0) then odnDate = od end end break end if bibDate then break end end end if bibDate then result.date = { bib = bibDate, hint = bibDateHint, coins = coinsDate, odn = odnDate } else local date, patch = parseDate(result.date or "", false, false, true) if date then date.coins = (patch and date.year) or (date.day and string.format("%04d-%02d-%02d", date.year, date.month, date.day)) or (date.month and string.format("%04d-%02d", date.year, date.month)) or date.year date.odn = date.year elseif result.date then result.badDate = true end result.date = date result.patchCitoidDate = patch end end -- fix other dates if result.accessdate then result.accessdate = parseDate(result.accessdate or "", false, false, false) if result.accessdate and not result.accessdate.day then result.badAccessDate = true result.accessdate = nil elseif not result.accessdate then result.badAccessDate = true end end -- allow more ISBN numbers if result.isbn then -- TODO allow "(info)" for custom description followed each identifier result.isbn = mw.text.split(result.isbn, "%s+") end if result.title then local url result.title, result.titlelink, url = splitWikiLink(result.title) if url or result.titlelink then if result.url and (#result.url > 0) and (result.url ~= "{{{url}}}") then result.urlWarning = true end result.url = url end end if result.chapter then result.chapter, result.chapterlink, result.chapterurl = splitWikiLink(result.chapter) end if result.journal then local journalAbbr, _ = mw.ustring.gsub(result.journal, "[%.%s]+", " ") if mw.ustring.match(journalAbbr, "^[%a%s]+:?[%a%s]+$") then -- kandydat na skrōt mŏ mieć ino litery z ôpcyjōnalnymi ôdstympami i nojwyżyj jednym dwukropkym local expandedJournal = mw.loadData("Moduł:Cytuj/cajtōngi")[mw.text.trim(journalAbbr)] if expandedJournal then result.originalJournal = result.journal result.journal = expandedJournal end end result.journal, result.journallink, result.journalurl = splitWikiLink(result.journal) end if result.journal and not result.journallink and not result.journalurl and not result.title and result.url then result.journalurl = result.url result.url = false end if result.url and isAutoGeneratedUrl(result.url) then result.rejectedurl = true result.url = false end if result.chapterurl and isAutoGeneratedUrl(result.chapterurl) then result.rejectedurl = true result.chapterurl = false end if result.journalurl and isAutoGeneratedUrl(result.journalurl) then result.rejectedurl = true result.journalurl = false end if not result.archive and result.url then local al, ad = detectArchive(result.url) if al then result.archive = result.url result.url = al if ad then result.archived = ad end end elseif not result.archive and result.chapterurl then local al, ad = detectArchive(result.chapterurl) if al then result.archive = result.chapterurl result.chapterurl = al if ad then result.archived = ad end end elseif result.archive and not result.archived then local al, ad = detectArchive(result.archive) if ad and not result.archived then result.archived = ad end end if result.archived then result.archived = parseDate(result.archived or "", false, false, false) if result.archived and not result.archived.day then result.badArchivedDate = true result.archived = null elseif not result.archived then result.badArchivedDate = true end end if result.edition and result.journal and not result.volume and not result.issue then local volume, issue = mw.ustring.match(result.edition, "^%s*([^%(]+)%s+%((.-)%)%s*$"); if volume then result.volume = volume result.issue = issue result.edition = nil end end if result.pmc and (#result.pmc > 3) and (mw.ustring.sub(result.pmc, 1, 3) == "PMC") then result.pmc = mw.ustring.sub(result.pmc, 4, #result.pmc) end if result.accessKind then result.accessKind = access.choice[result.accessKind] result.unknownAccess = not result.accessKind else result.accessKind = (result.pmc and "open") or access.doi[doiPrefix] or access.journals[result.journal] end if result.doi then result.doi = mw.text.split(result.doi, '%s+', false) for i, v in ipairs(result.doi) do local doiPrefix local doiSuffix doiPrefix, doiSuffix = mw.ustring.match(v, "^10%.([^/]+)/(.+)$") if (doiPrefix == "2307") and not result.jstor then result.jstor = doiSuffix end if not result.accessKind and not result.unknownAccess then result.accessKind = access.doi[doiPrefix] end end end -- return collected parameters if there is any for k, v in pairs(result) do return result end -- there are no supported parameters return nil end local function prepareOdnIdentifier(data) if not data.odn or (#data.odn == 0) or (data.odn == "nie") then return nil end data.diferentiator = mw.ustring.match(data.odn, "^([a-z])$") or false if data.odn ~= "tak" and not data.diferentiator then -- TODO return only CITEREF... return data.odn end local authors = data.chapterauthor or data.author or data.editor if not authors then -- required custom identifier return nil end return "CITEREF" .. (authors.items[1] and (authors.items[1].lastname or authors.items[1].exact) or "") .. (authors.items[2] and (authors.items[2].lastname or authors.items[2].exact) or "") .. (authors.items[3] and (authors.items[3].lastname or authors.items[3].exact) or "") .. (authors.items[4] and (authors.items[4].lastname or authors.items[4].exact) or "") .. (data.date and data.date.odn or "") .. (data.diferentiator or "") end local function bookCOinS(data) local authors = data.chapterauthor or data.author local result = {} result["rft_val_fmt"] = "info:ofi/fmt:kev:mtx:book" if data.chapter and (#data.chapter > 0) then result["rft.gengre"] = "bookitem" result["rft.atitle"] = plainText(data.chapter) result["rft.btitle"] = plainText(data.title) elseif data.work and (#data.work > 0) then result["rft.gengre"] = "bookitem" result["rft.atitle"] = plainText(data.title) result["rft.btitle"] = plainText(data.work) else result["rft.btitle"] = plainText(data.title) result["rft.gengre"] = "book" end if authors then if authors.items[1].lastname then result["rft.aulast"] = authors.items[1].lastname end if authors.items[1].name then result["rft.aufirst"] = authors.items[1].name end if authors.items[1].exact then result["rft.au"] = authors.items[1].exact end end if data.date and data.date.coins then result["rft.date"] = data.date.coins end if data.edition then result["rft.edition"] = data.edition end if data.publisher then result["rft.pub"] = data.publisher end if data.place then result["rft.place"] = data.place end if data.pages then result["rft.pages"] = data.pages end if data.isbn then result["rft.isbn"] = data.isbn[1] end if data.issn then result["rft.issn"] = data.issn end local params = { "ctx_ver=Z39.88-2004", mw.uri.buildQueryString(result), } if data.oclc then table.insert(params, mw.uri.buildQueryString( {rft_id = "info:oclcnum/"..data.oclc})) end if data.doi then for _, v in ipairs(data.doi) do table.insert(params, mw.uri.buildQueryString( {rft_id = "info:doi/"..v})) end end if data.url then table.insert(params, mw.uri.buildQueryString( {rft_id = data.url})) end if data.pmid then table.insert(params, mw.uri.buildQueryString( {rft_id = "info:pmid/"..data.pmid})) end if data.lccn then table.insert(params, mw.uri.buildQueryString( {rft_id = "info:lccn/"..data.lccn})) end local coinsData = table.concat(params, "&") return coinsData; end local function journalCOinS(data) local result = {} result["rft_val_fmt"] = "info:ofi/fmt:kev:mtx:journal" local gengre = (data.arxiv and (#data.arxiv > 0)) and "preprint" or "article" result["rft.gengre"] = data.title and gengre or "journal" if data.title then result["rft.atitle"] = plainText(data.title) end result["rft.jtitle"] = plainText(data.journal) if data.chapter then result["rft.atitle"] = plainText(data.chapter) end if data.date and data.date.coins then result["rft.date"] = data.date.coins end if data.title and author then if author[1].lastname then result["rft.aulast"] = author[1].lastname end if author[1].name then result["rft.aufirst"] = author[1].name end if author[1].exact then result["rft.au"] = author[1].exact end end if data.volume then result["rft.volume"] = data.volume end if data.issue then result["rft.edition"] = data.issue end if data.publisher then result["rft.pub"] = data.publisher end if data.place then result["rft.place"] = data.place end if data.pages then result["rft.pages"] = data.pages end if data.issn then result["rft.issn"] = data.issn end local params = { "ctx_ver=Z39.88-2004", mw.uri.buildQueryString(result), } if data.pmid then table.insert(params, mw.uri.buildQueryString( {rft_id = "info:pmid/"..data.pmid})) end if data.pmc then table.insert(params, mw.uri.buildQueryString( {rft_id = "info:pmc/"..data.pmc})) end if data.doi then for _, v in ipairs(data.doi) do table.insert(params, mw.uri.buildQueryString( {rft_id = "info:doi/"..v})) end end if data.url then table.insert(params, mw.uri.buildQueryString( {rft_id = data.url})) end local coinsData = table.concat(params, "&") return coinsData; end local function webCOinS(data) local result = {} result["rft_val_fmt"] = "info:ofi/fmt:kev:mtx:journal" result["rft.gengre"] = "unknown" if data.title then result["rft.atitle"] = plainText(data.title) end result["rft.jtitle"] = plainText(data.published) if data.date and data.date.coins then result["rft.date"] = data.date.coins end if data.title and author then if author[1].lastname then result["rft.aulast"] = author[1].lastname end if author[1].name then result["rft.aufirst"] = author[1].name end if author[1].exact then result["rft.au"] = author[1].exact end end local params = { "ctx_ver=Z39.88-2004", mw.uri.buildQueryString(result), } if data.url then table.insert(params, mw.uri.buildQueryString( {rft_id = data.url})) end local coinsData = table.concat(params, "&") return coinsData; end local function COinS(data, coinsFormat) if (coinsFormat == "info:ofi/fmt:kev:mtx:book") and data.title and (#data.title > 0) then -- title is mandatory element for books return bookCOinS(data) elseif coinsFormat == "info:ofi/fmt:kev:mtx:journal" and data.journal and (#data.journal > 0) and (not data.published or (data.journal ~= data.published)) then -- journal title is mandatory element for journals return journalCOinS(data) elseif coinsFormat == "info:ofi/fmt:kev:mtx:journal" and data.published and (#data.published > 0) then return webCOinS(data) elseif data.title and (#data.title > 0) then -- treat web or unrecognized citations as book return bookCOinS(data) else return false end end local function Cite(p, mode) -- debug helper if p.args[3] then mw.log(p.args[3]) end local customMode = mode -- try to determine type basing on passed parameters local coinsFormat = resources.COinS[mode] if not mode then mode, coinsFormat = determineMode(p) end local data = loadCitation(p, mode) if not data then return resources.categories.empty end if data.missing then -- do not produce any COiNS info -- if some mandatory argument is missing coinsFormat = false end local builder = mw.html.create("span") builder :addClass("citation") :attr("id", prepareOdnIdentifier(data)) :wikitext(access.render[data.accessKind], ' ') local needDot = false local nextAuthorGroup = false if data.title then if data.chapter then local authors = data.editor and data.author or data.chapterauthor if authors then local list, etal = formatAuthors(authors, false, nextAuthorGroup, nil) builder:wikitext(list, ", ") nextAuthorGroup = true end local title = softNoWiki(data.chapter) if data.chapterurl then builder:wikitext("[", escapeUrl((not data.url and data.archive) and data.archive or data.chapterurl), " ''", title, "'']") elseif data.chapterlink then builder:wikitext("[[", data.chapterlink, "|''", title, "'']]") else builder:wikitext("''", title, "''") end if data.format then builder:wikitext(" [", data.format, "]") end builder:wikitext(" [w:] ") end local authors = false local editor = false if not data.chapter and data.author then authors = data.author else authors = data.editor or data.author editor = data.editor end if authors then local list, etal = formatAuthors(authors, not (editor or false), nextAuthorGroup, editor and " i inni red." or nil) builder:wikitext(list) nextAuthorGroup = true if editor and not etal and not authors.etal then builder:wikitext(" (red.)") end builder:wikitext(", ") end if customMode and data.authorextra then builder:wikitext(data.authorextra, ", ") end local title = softNoWiki(data.title) if data.url or (not data.chapterurl and data.archive) then builder:wikitext("[", escapeUrl(data.archive or data.url), " ''", title, "'']") elseif data.titlelink then builder:wikitext("[[", data.titlelink, "|''", title, "'']]") else builder:wikitext("''", title, "''") end if not data.chapter and data.format then builder:wikitext(" [", data.format, "]") needDot = true elseif not mw.ustring.match(plainText(title), "%p$") then needDot = true end local showmediatype = data.mediatype and (#data.mediatype > 0) if showmediatype then builder:wikitext(" [", data.mediatype, "]") needDot = true end if not editor and data.editor then local list, etal = formatAuthors(data.editor, false, true, " i inni red.") builder:wikitext(", ", list, (etal or data.editor.etal) and "" or " (red.)") end if data.others then local list, etal = formatAuthors(data.others, true, true, nil) builder:wikitext(", ", list) needDot = true end end if data.work then builder:wikitext(" [w:] ", data.work, (not data.mediatype and data.url) and " [online]" or "") needDot = true end if data.journal and (not data.published or (data.journal ~= data.published)) then builder:wikitext((data.title or data.work) and ", " or "") local title = softNoWiki(data.journal) if data.journalurl then builder:wikitext("[", escapeUrl(data.journalurl), " „", title, "”]") elseif data.journallink then builder:wikitext("„[[", data.journallink, "|", title, "]]”") else builder:wikitext("„", title, "”") end needDot = true end if data.responsibility then builder:wikitext(", ", data.responsibility) needDot = true end if data.edition then builder:wikitext(data.journal and ", " or ", wyd. ", data.edition) needDot = true end if data.volume then builder:wikitext(data.journal and ", " or ", t. ", data.volume) needDot = true end if data.series or data.issue then builder:wikitext(" (", data.series or "", (data.series and data.issue) and "; " or "", data.issue or "", ")") needDot = true end if data.description and (#data.description > 0) then builder:wikitext(", ", data.description) needDot = true end if data.published and not data.publisher then builder:wikitext(", ", data.published) needDot = true end local place = false if data.place then builder:wikitext(", ", data.place) needDot = true place = true end if data.publisher then builder:wikitext(place and ": " or ", ", data.publisher) needDot = true place = false end if data.date then builder:wikitext(place and " " or ", ") local shortDate = data.journal and (data.doi or data.pmid or data.pmc) if data.date.bib and data.date.hint then builder:tag("span"):attr("title", data.date.hint):wikitext(data.date.bib) elseif data.date.bib then builder:wikitext(data.date.bib) elseif data.date.day and shortDate then builder:tag("span"):attr("title", tostring(data.date.day).." "..resources.months[data.date.month].d.." "..tostring(data.date.year)):wikitext(data.date.year) elseif data.date.month and shortDate then builder:tag("span"):attr("title", resources.months[data.date.month].m.." "..tostring(data.date.year)):wikitext(data.date.year) elseif data.date.day then builder:wikitext(place and ", " or "", tostring(data.date.day), " ", resources.months[data.date.month].d, " ", tostring(data.date.year)) elseif data.date.month then builder:wikitext(place and ", " or "", resources.months[data.date.month].m, " ", tostring(data.date.year)) else builder:wikitext(data.date.year) end builder:wikitext(data.diferentiator or "") needDot = true end if data.p and #data.p > 0 then local isNonStandardPageNumber = mw.ustring.match(data.p, "[^%s0-9,%-–]") builder:wikitext(isNonStandardPageNumber and ", " or ", s. ", data.p) needDot = true end if data.doi then local separator = " " builder:addClass("doi"):wikitext(", [[DOI (identyfikator cyfrowy)|DOI]]:") local doiLink = first(resources.params.doi.link) for _, v in ipairs(data.doi) do builder:wikitext(separator, "[", doiLink, mw.uri.encode(v), " ", softNoWiki(v), "]") separator = ", " end needDot = true end if data.isbn then for i,v in ipairs(data.isbn) do builder:wikitext(", ") require("Moduł:ISBN").link(builder, v) end needDot = true end if data.lccn then builder:wikitext(", [[Biblioteka Kongresu|LCCN]] [", first(resources.params.lccn.link), mw.uri.encode(data.lccn), " ", data.lccn, "]") needDot = true end if data.issn then builder:tag("span"):addClass("issn"):wikitext(", [[International Standard Serial Number|ISSN]] [", first(resources.params.issn.link), data.issn, " ", data.issn, "]") needDot = true end if data.pmid then builder:addClass("pmid"):wikitext(", [[PMID]]: [", first(resources.params.pmid.link), data.pmid, " ", data.pmid, "]") needDot = true end if data.pmc then builder:addClass("pmc"):wikitext(", [[PMCID]]: [", first(resources.params.pmc.link), data.pmc, "/ PMC", data.pmc, "]") needDot = true end if data.bibcode then builder:wikitext(", [[Bibcode]]: [", first(resources.params.bibcode.link), data.bibcode, " ", data.bibcode, "]") needDot = true end if data.oclc then builder:wikitext(", [[Online Computer Library Center|OCLC]] [", first(resources.params.oclc.link), mw.uri.encode(data.oclc), " ", data.oclc, "]") needDot = true end if data.arxiv then builder:wikitext(", [[arXiv]]:") local eprint, class = mw.ustring.match(data.arxiv, "^(%S+)%s+%[([^%[%]]+)%]$") if eprint then builder:wikitext("[", first(resources.params.arxiv.link), eprint, " ", eprint, "] [[//arxiv.org/archive/", class, " ", class, "]]" ) else builder:wikitext("[", first(resources.params.arxiv.link), data.arxiv, " ", data.arxiv, "]" ) end needDot = true end if data.jstor then builder:tag("span"):addClass("jstor"):wikitext(", [[JSTOR]]: [", first(resources.params.jstor.link), data.jstor, " ", data.jstor, "]") needDot = true end if data.ol then builder:tag("span"):addClass("open-library"):wikitext(", [[Open Library|OL]]: [", first(resources.params.ol.link), data.ol, " ", data.ol, "]") needDot = true end if data.id then builder:wikitext(", ", data.id) needDot = true end if data.accessdate then builder:tag("span"):addClass("accessdate"):wikitext(" [dostymp ", string.format("%04d-%02d-%02d", data.accessdate.year, data.accessdate.month, data.accessdate.day), "]") needDot = true end if data.archive then builder:wikitext(" [zarchiwizowane") if (data.url or data.chapterurl) then builder:wikitext(" z [", escapeUrl(data.url or data.chapterurl), " adresy]") end if data.archived and data.archived.day then builder:wikitext(" ", string.format("%04d-%02d-%02d", data.archived.year, data.archived.month, data.archived.day)) end builder:wikitext("]") needDot = true end if data.quotation then builder:wikitext(", Cytat: ", data.quotation) needDot = true end local coinsData = COinS(data, coinsFormat) if coinsData then builder:tag("span"):addClass("Z3988"):attr("title",coinsData):css("display","none"):wikitext(" ") end if data.lang then local languages = require("Moduł:Lang").lang({args = data.lang}) builder:wikitext(" ", languages) needDot = true end if needDot then builder:wikitext(".") end -- categories local addCategories = mw.title.getCurrentTitle().namespace == 0 local problems = {} if not customMode and (mode == 1) then builder:wikitext(resources.categories.undetermined) table.insert(problems, "???") end if data.publisher and data.published then table.insert(problems, "p?") if addCategories then table.insert(problems, resources.categories.unusedPublished) end end if data.journal and data.published and (data.journal == data.published) then table.insert(problems, "j?") if addCategories then table.insert(problems, resources.categories.sameJournalAndPublished) end end if (not data.url and not data.chapterurl) or (not data.title and not data.journalurl) then builder:addClass(data.urlnowiki and "urlnowiki" or "nourl") end local missing = false local needurl = ((resources.params.published.used[mode] == "*") and data.published) or (resources.params.url.used[mode] == "*") if data.missing then -- usually missing title, this is the first check for mandatory arguments table.insert(problems, data.missing) missing = true elseif needurl and not data.url and not data.chapterurl and not data.arxiv and not data.archive then -- build in support for missing external link for page citation table.insert(problems, resources.params.url.name) missing = true else -- any other missing value (first catch) for k, v in pairs(resources.params) do if (v.used[mode] == "!") and (not data[k] or (#data[k] == 0)) then table.insert(problems, v.name) missing = true break end end end if missing and addCategories then builder:wikitext(string.format(resources.categories.missingArg, resources.modes[mode])) end if (data.chapterauthor and data.chapterauthor.comma) or (data.author and (data.author.comma == true)) or (data.editor and data.editor.comma) or (data.others and data.others.comma) then table.insert(problems, "!!!") if addCategories then builder:wikitext(resources.categories.suspectedComma) end end if data.author and (data.author.comma == "alt") then table.insert(problems, "a?") if addCategories then builder:wikitext(resources.categories.altAuthor) end end if data.originalJournal then table.insert(problems, "c?") if addCategories then builder:wikitext(resources.categories.altJournal) end end local citewiki = (data.journal and mw.ustring.match(data.journal, "[Ww]ikipedia")) or (data.publisher and mw.ustring.match(data.publisher, "[Ww]ikipedia")) or (data.published and mw.ustring.match(data.published, "[Ww]ikipedia")) or (data.url and mw.ustring.match(data.url, "%.wikipedia%.org")) if citewiki then table.insert(problems, "wiki?") if addCategories then builder:wikitext(resources.categories.wiki) end end if data.unknownAccess then table.insert(problems, "dostymp?") if addCategories then builder:wikitext(resources.categories.unknownAccess) end end if data.rejectedurl then table.insert(problems, "url") if addCategories then builder:wikitext(resources.categories.rejectedUrl) end end if data.urlWarning then table.insert(problems, "Url") if addCategories then builder:wikitext(resources.categories.unusedUrl) end end if data.patchCitoidDate then table.insert(problems, "1 stycznia") end if data.badDate then table.insert(problems, "data?") end if data.badAccessDate then table.insert(problems, "data dostympu?") end if data.badArchivedDate then table.insert(problems, "zarchiwizowano?") end if addCategories and (data.badDate or data.badAccessDate or data.badArchiveDate) then builder:wikitext(resources.categories.badDate) end if (data.author and data.author.etal) or (data.chapterauthor and data.chapterauthor.etal) or (data.editor and data.editor.etal) or (data.others and data.others.etal) then table.insert(problems, "i inni") if addCategories then builder:wikitext(resources.categories.etal) end end if #problems > 0 then local info = builder:tag("span"):addClass("problemy-w-cytuj") if addCategories then info:css("display","none") else info:css("color", "red") end info:wikitext(table.concat(problems,", ")) end return builder:done() end return { auto = function(frame) return Cite(frame:getParent(), nil) end, custom = function(frame) return Cite(frame, 1) end, }