Module:Citation/CS1:修订间差异
小 已还原PassWordPFD(讨论)的编辑至最后由Dmmldychkw修订的版本 |
|||
第1行: | 第1行: | ||
-- [[ | --[[--------------------------< I M P O R T E D _ F U N C T I O N S _ A N D _ V A R I B L E S >----------------- | ||
-- | ]] | ||
-- | local cfg = {}; -- table of configuration tables that are defined in Module:Citation/CS1/Configuration | ||
local whitelist = {}; -- table of tables listing valid template parameter names; defined in Module:Citation/CS1/Whitelist | |||
-- | local dates, year_date_check; -- functions in Module:Citation/CS1/Date_validation | ||
local add_maint_cat, append_error, make_error_tail, reset_error, set_error, select_one, throw_error; | |||
-- error-related functions in Module:Citation/CS1/Error | |||
local first_set, hyphen_to_dash, is_set, in_array, substitute; -- simple functions in Module:Citation/CS1/Utilities | |||
local has_invisible_chars, kern_quotes, pend_separator, safe_join, wrap_style, wrap_msg; | |||
-- style-related functions in Module:Citation/CS1/Utilities | |||
local check_for_external_link, make_external_link, make_internal_link; -- link-related functions in Module:Citation/CS1/Links | |||
local extract_ids, build_id_list, is_embargoed, extract_id_access_levels; -- functions in Module:Citation/CS1/Identifiers | |||
local get_people, format_people; -- functions in Module:Citation/CS1/People | |||
local COinS; -- functions in Module:Citation/CS1/COinS | |||
local script_concatenate, language_parameter; -- functions in Module:Citation/CS1/Language | |||
-- | local function load_modules (module_path, module_suffix) | ||
-- ]] | cfg = mw.loadData (module_path .. 'Configuration' .. module_suffix); | ||
whitelist = mw.loadData (module_path .. 'Whitelist' .. module_suffix); | |||
local validation = require (module_path .. 'Date_validation' .. module_suffix); | |||
local identifiers = require (module_path .. 'Identifiers' .. module_suffix); | |||
local utilities = require (module_path .. 'Utilities' .. module_suffix); | |||
local people = require (module_path .. 'People' .. module_suffix); | |||
local links = require (module_path .. 'Links' .. module_suffix); | |||
local errors = require (module_path .. 'Error' .. module_suffix); | |||
local coins = require (module_path .. 'COinS' .. module_suffix); | |||
local languages = require (module_path .. 'Language' .. module_suffix); | |||
utilities.set_selected_modules (cfg); | |||
links.set_selected_modules (utilities, errors); | |||
errors.set_selected_modules (cfg, utilities, links); | |||
identifiers.set_selected_modules (cfg, utilities, errors, links, validation); | |||
people.set_selected_modules (cfg, utilities, errors, links); | |||
coins.set_selected_modules (cfg, utilities, links); | |||
languages.set_selected_modules (utilities, errors); | |||
dates = validation.dates; -- imported functions | |||
year_date_check = validation.year_date_check; | |||
first_set = utilities.first_set; | |||
hyphen_to_dash = utilities.hyphen_to_dash; | |||
is_set = utilities.is_set; | |||
in_array = utilities.in_array; | |||
substitute = utilities.substitute; | |||
has_invisible_chars = utilities.has_invisible_chars; | |||
kern_quotes = utilities.kern_quotes; | |||
pend_separator = utilities.pend_separator; | |||
safe_join = utilities.safe_join; | |||
wrap_style = utilities.wrap_style; | |||
wrap_msg = utilities.wrap_msg; | |||
make_external_link = links.make_external_link; | |||
make_internal_link = links.make_internal_link; | |||
check_for_external_link = links.check_for_external_link; | |||
add_maint_cat = errors.add_maint_cat; | |||
append_error = errors.append_error; | |||
make_error_tail = errors.make_error_tail; | |||
reset_error = errors.reset_error; | |||
set_error = errors.set_error; | |||
select_one = errors.select_one; | |||
throw_error = errors.throw_error; | |||
extract_ids = identifiers.extract_ids; | |||
build_id_list = identifiers.build_id_list; | |||
is_embargoed = identifiers.is_embargoed; | |||
extract_id_access_levels = identifiers.extract_id_access_levels; | |||
get_people = people.get_people; | |||
format_people = people.format_people; | |||
COinS = coins.COinS; | |||
script_concatenate = languages.script_concatenate; | |||
language_parameter = languages.language_parameter; | |||
end | |||
--[[--------------------------< D E P R E C A T E D _ P A R A M E T E R >-------------------------------------- | |||
Categorize and emit an error message when the citation contains one or more deprecated parameters. The function includes the | |||
offending parameter name to the error message. | |||
]] | |||
local function deprecated_parameter (name) | |||
append_error ('deprecated_params', {name}); | |||
end | |||
--[[--------------------------< D I S C A R D _ P A R A M E T E R >-------------------------------------------- | |||
]] | |||
local function discard_parameter (name, label, new_value) | |||
if is_set (name) then | |||
append_error ('parameter_discarded', label); | |||
end | |||
return new_value; | |||
end | |||
--[[--------------------------< S A N I T I Z E D _ P A R A M E T E R _ V A L U E >------------------------ | |||
This function is used to validate a parameter's assigned value for those parameters that have only a limited number | |||
of allowable values (e.g. yes, y, true, no, etc). If the parameter value is empty or is in the list of allowed values, | |||
the function returns the value; else, it emits an error message and returns the default value. | |||
]] | |||
local function sanitized_parameter_value (value, name, key, default) | |||
if not is_set (value) then | |||
return value; -- an empty parameter is ok | |||
elseif in_array (value:lower(), cfg.keywords[key]) then | |||
return value; | |||
else | |||
append_error ('invalid_param_val', {name, value}); -- not an allowed value so add error message | |||
return default; | |||
end | |||
end | |||
--[[--------------------------< E X T R A _ T E X T _ I N _ P A R A M E T E R _ C H E C K >------------------------------ | |||
]] | |||
local function extra_text_in_parameter_check (value, type) | |||
local good_patterns = cfg.extra_text_pattern[type]['good']; | |||
local bad_patterns = cfg.extra_text_pattern[type]['bad']; | |||
for _, pattern in pairs (good_patterns) do | |||
if value:match (pattern) then | |||
return; | |||
end | |||
end | |||
for _, pattern in pairs (bad_patterns) do | |||
if value:match (pattern) then | |||
add_maint_cat ('extra_text', type); | |||
return; | |||
end | |||
end | |||
end | |||
--[[--------------------------< V A L I D A T E _ D A T E >------------------------------------------------------- | |||
Go test all of the date-holding parameters for valid MOS:DATE format and make sure that dates are real dates. This must be done before we do COinS because here is where | |||
we get the date used in the metadata. | |||
Date validation supporting code is in Module:Citation/CS1/Date_validation | |||
]] | |||
local function validate_date (AccessDate, ArchiveDate, Date, DoiBroken, Embargo, LayDate, PublicationDate, Year, COinS_date, origin) | |||
local error_message = ''; | |||
-- AirDate has been promoted to Date so not necessary to check it | |||
anchor_year, error_message = dates ({['access-date']=AccessDate, ['archive-date']=ArchiveDate, ['date']=Date, ['doi-broken-date']=DoiBroken, | |||
['embargo']=Embargo, ['lay-date']=LayDate, ['publication-date']=PublicationDate, ['year']=Year}, COinS_date); | |||
if is_set (Year) then | |||
if is_set (Date) then -- both |date= and |year= not normally needed; | |||
local mismatch = year_date_check (Year, Date) | |||
if 0 == mismatch then -- |year= does not match a year-value in |date= | |||
append_error ('date_year_mismatch', {origin}); | |||
elseif 1 == mismatch then -- |year= matches year-value in |date= | |||
add_maint_cat ('date_year'); | |||
end | |||
end | |||
anchor_year = Year; -- Year first for legacy citations and for YMD dates that require disambiguation | |||
end | |||
if is_set (error_message) then | |||
append_error ('bad_date', {error_message}); -- add this error message | |||
end | |||
return anchor_year; | |||
end | |||
--[[--------------------------< D I S C A R D _ C H A P T E R >------------------------------------------------------- | |||
仅为保持兼容性而设置。理论上可以直接调用discard_parameter()丢弃相关参数。 | |||
]] | |||
local function discard_chapter (args) | |||
local chap_param; | |||
if is_set (args['Chapter']) then -- get a parameter name from one of these chapter related meta-parameters | |||
chap_param = args:ORIGIN ('Chapter'); | |||
elseif is_set (args['TransChapter']) then | |||
chap_param = args:ORIGIN ('TransChapter'); | |||
elseif is_set (args['ChapterURL']) then | |||
chap_param = args:ORIGIN ('ChapterURL'); | |||
elseif is_set (args['ScriptChapter']) then | |||
chap_param = args:ORIGIN ('ScriptChapter') | |||
elseif is_set (args['ChapterFormat']) then | |||
chap_param = args:ORIGIN ('ChapterFormat') | |||
elseif is_set (args['ChapterUrlAccess']) then | |||
chap_param = args:ORIGIN ('ChapterUrlAccess') | |||
end | |||
if is_set (chap_param) then -- if we found one | |||
append_error ('chapter_ignored', {chap_param}); -- add error message | |||
end | |||
end | |||
--[[--------------------------< C R E A T E _ U R L _ O B J E C T >------------------------------------------ | |||
]] | |||
local function create_url_object (url, source, fmt, fmt_source, access, access_source) | |||
return { | |||
['url'] = is_set (url) and url or '', | |||
['origin'] = is_set (source) and source or '', | |||
['access'] = is_set (access) and sanitized_parameter_value (access, access_source, 'url-access', '') or '', | |||
['access-origin'] = is_set (access_source) and access_source or '', | |||
['format'] = is_set (fmt) and fmt or '', | |||
['format-origin'] = is_set (fmt_source) and fmt_source or '', | |||
['access-text'] = '' | |||
} | |||
end | |||
--[[--------------------------< S E T _ T I T L E T Y P E >---------------------------------------------------- | |||
This function sets default title types (equivalent to the citation including |type=<default value>) for those templates that have defaults. | |||
Also handles the special case where it is desirable to omit the title type from the rendered citation (|type=none). | |||
]] | |||
local function set_titletype (cite_class, title_type) | |||
if is_set (title_type) then | |||
if 'none' == title_type then | |||
title_type = ''; -- if |type=none then type parameter not displayed | |||
end | |||
return title_type; -- if |type= has been set to any other value use that value | |||
end | |||
return cfg.title_types[cite_class] or ''; -- set template's default title type; else empty string for concatenation | |||
end | |||
--[[--------------------------< S E T _ N O _ T R A C K I N G _ C A T S >----------------------------------------- | |||
check this page to see if it is in one of the namespaces that cs1 is not supposed to add to the error categories | |||
]] | |||
local function set_no_tracking_cats (no_tracking, no_tracking_source, this_page) | |||
local no_tracking_cats = sanitized_parameter_value (no_tracking, no_tracking_source, 'yes_true_y', nil); | |||
if not is_set (no_tracking_cats) then -- ignore if we are already not going to categorize this page | |||
if in_array (this_page.nsText, cfg.uncategorized_namespaces) then | |||
return true; -- set no_tracking_cats | |||
end | |||
for _,v in ipairs (cfg.uncategorized_subpages) do -- cycle through page name patterns | |||
if this_page.text:match (v) then -- test page name against each pattern | |||
return true; -- set no_tracking_cats; bail out if one is found | |||
end | |||
end | |||
return false; | |||
else | |||
return true; | |||
end | |||
end | |||
--[[--------------------------< S E T _ C S 1 _ S T Y L E >---------------------------------------------------- | |||
Set style settings for CS1 citation templates. Returns separator and postscript settings | |||
]] | |||
local function set_cs1_style (ps) | |||
if not is_set (ps) then -- unless explicitely set to something | |||
ps = '.'; -- terminate the rendered citation with a period | |||
end | |||
return '.', ps; -- separator is a full stop | |||
end | |||
--[[--------------------------< S E T _ C S 2 _ S T Y L E >---------------------------------------------------- | |||
Set style settings for CS2 citation templates. Returns separator, postscript, ref settings | |||
]] | |||
local function set_cs2_style (ps, ref) | |||
if not is_set (ps) then -- if |postscript= has not been set, set cs2 default | |||
ps = ''; -- make sure it isn't nil | |||
end | |||
if not is_set (ref) then -- if |ref= is not set | |||
ref = 'harv'; -- set default |ref=harv | |||
end | |||
return ',', ps, ref; -- separator is a comma | |||
end | |||
--[[--------------------------< G E T _ S E T T I N G S _ F R O M _ C I T E _ C L A S S >---------------------- | |||
When |mode= is not set or when its value is invalid, use config.CitationClass and parameter values to establish | |||
rendered style. | |||
]] | |||
local function get_settings_from_cite_class (ps, ref, cite_class) | |||
local sep; | |||
if (cite_class == 'citation') then -- for citation templates (CS2) | |||
sep, ps, ref = set_cs2_style (ps, ref); | |||
else -- not a citation template so CS1 | |||
sep, ps = set_cs1_style (ps); | |||
end | |||
return sep, ps, ref -- return them all | |||
end | |||
--[[--------------------------< S E T _ S T Y L E >------------------------------------------------------------ | |||
Establish basic style settings to be used when rendering the citation. Uses |mode= if set and valid or uses | |||
config.CitationClass from the template's #invoke: to establish style. | |||
]] | |||
local function set_style (mode, mode_source, ps, ref, quote, cite_class) | |||
local sep; | |||
mode = sanitized_parameter_value (mode, mode_source, 'mode', ''):lower(); | |||
if 'cs2' == mode then -- if this template is to be rendered in CS2 (citation) style | |||
sep, ps, ref = set_cs2_style (ps, ref); | |||
elseif 'cs1' == mode then -- if this template is to be rendered in CS1 (cite xxx) style | |||
sep, ps = set_cs1_style (ps); | |||
else -- anything but cs1 or cs2 | |||
sep, ps, ref = get_settings_from_cite_class (ps, ref, cite_class); -- get settings based on the template's CitationClass | |||
end | |||
if 'none' == ps:lower() or is_set (quote) then -- if assigned value is 'none' then set it to empty string | |||
ps = ''; -- also cs1|2 does not supply terminal punctuation when |quote= is set | |||
end | |||
return sep, ps, ref | |||
end | |||
--[[--------------------------< S W A P _ U R L S >-------------------------------------------------------------- | |||
]] | |||
local function swap_urls (url_object, chapter_url_object, archive_url_object, dead_url) | |||
local original_url_object = create_url_object (); | |||
local is_dead = in_array (dead_url, cfg.keywords['deadurl-live']); -- used later when assembling archived text | |||
if is_set (archive_url_object['url']) then | |||
if is_set (url_object['url']) then | |||
original_url_object = url_object; | |||
if not is_dead then | |||
url_object = archive_url_object; | |||
end | |||
elseif is_set (chapter_url_object['url']) then -- URL not set so if chapter-url is set apply archive url to it | |||
original_url_object = chapter_url_object; | |||
if not is_dead then | |||
chapter_url_object = archive_url_object; | |||
end | |||
end | |||
end | |||
return original_url_object, url_object, chapter_url_object; | |||
end | |||
--[[--------------------------< F O R M A T _ U R L _ A C C E S S _ T E X T >--------------------------------------- | |||
从x-url-access系列参数生成相应的图标;兼容既有registration, subscription参数,优先级x-url-access > subscription > registration。 | |||
]] | |||
local function format_url_access_text (url_object, subscription_required, registration_required) | |||
local access_text = ''; | |||
local redundant = false; | |||
local access = url_object['access']; | |||
local reg = false; | |||
local sub = false; | |||
if is_set (access) then | |||
if (access == 'limited') then | |||
access_text = cfg.presentation['limited']; -- 有限度免费访问 | |||
elseif (access == 'registration') then | |||
access_text = cfg.presentation['registration']; -- 需要免费注册 | |||
reg = true; | |||
elseif (access == 'subscription') then | |||
access_text = cfg.presentation['subscription']; -- 需要付费订阅 | |||
sub = true; | |||
else | |||
access_text = ''; | |||
end | |||
if is_set (subscription_required) or is_set (registration_required) then | |||
redundant = true; | |||
end | |||
else | |||
if is_set (subscription_required) then | |||
access_text = cfg.presentation['subscription']; -- 需要免费注册 | |||
sub = true; | |||
if is_set (registration_required) then | |||
redundant = true; | |||
end | |||
elseif is_set (registration_required) then | |||
access_text = cfg.presentation['registration']; -- 需要付费订阅 | |||
reg = true | |||
else | |||
access_text = ''; | |||
end | |||
end | |||
if is_set (url_object ['url']) then | |||
url_object['access-text'] = access_text; | |||
if sub then | |||
add_maint_cat ('subscription'); | |||
elseif reg then | |||
add_maint_cat ('registration'); | |||
end | |||
else | |||
-- 预留报错 | |||
end | |||
return redundant; | |||
end | |||
--[[-------------------------< F O R M A T _ V O L U M E _ I S S U E >---------------------------------------- | |||
returns the concatenation of the formatted volume and issue parameters as a single string; or formatted volume | |||
or formatted issue, or an empty string if neither are set. | |||
]] | |||
local function format_volume_issue (volume, issue, cite_class, origin, sepc, lower) | |||
if not is_set (volume) and not is_set (issue) then | |||
return ''; | |||
end | |||
if 'magazine' == cite_class or (cite_class =='map' and 'magazine' == origin) then | |||
if is_set (volume) and is_set (issue) then | |||
return wrap_msg ('vol-no', {sepc, volume, issue}, lower); | |||
elseif is_set (volume) then | |||
return wrap_msg ('vol', {sepc, volume}, lower); | |||
else | |||
return wrap_msg ('issue', {sepc, issue}, lower); | |||
end | |||
end | |||
local vol = ''; | |||
if is_set (volume) then | |||
if (6 < mw.ustring.len (volume)) then | |||
vol = wrap_msg ('j-vol', {sepc, volume}, lower); | |||
else | |||
vol = wrap_style ('vol-bold', hyphen_to_dash (volume)); | |||
end | |||
end | |||
if is_set (issue) then | |||
return vol .. wrap_msg ('j-issue', issue, lower); | |||
end | |||
return vol; | |||
end | |||
--[[-------------------------< F O R M A T _ I N S O U R C E _ L O C A T I O N >---------------------------------- | |||
Build insource_location meta-parameter from |page(s)= , |sheet(s)= , |at= and other relevant parameters. | |||
]] | |||
local function format_insource_location (page, pages, sheet, sheets, at, minutes, time, time_caption, section, sections, inset, cite_class, origin, sepc, nopp, lower) | |||
local text = ''; | |||
if is_set (sheet) then | |||
if 'journal' == origin then | |||
text = wrap_msg ('j-sheet', sheet, lower); | |||
else | |||
text = wrap_msg ('sheet', {sepc, sheet}, lower); | |||
end | |||
elseif is_set (sheets) then | |||
if 'journal' == origin then | |||
text = wrap_msg ('j-sheets', sheets, lower); | |||
else | |||
text = wrap_msg ('sheets', {sepc, sheets}, lower); | |||
end | |||
end | |||
local is_journal = 'journal' == cite_class or (cite_class == 'map' and 'journal' == origin); | |||
if is_set (page) then | |||
if is_journal then | |||
text = wrap_msg ('j-page(s)', page, lower); | |||
elseif not is_set (nopp) then | |||
text = wrap_msg ('p-prefix', {sepc, page}, lower); | |||
else | |||
text = wrap_msg ('nopp', {sepc, page}, lower); | |||
end | |||
elseif is_set (pages) then | |||
if is_journal then | |||
text = wrap_msg ('j-page(s)', pages, lower); | |||
elseif tonumber (pages) ~= nil and not is_set (nopp) then -- if pages is only digits, assume a single page number | |||
text = wrap_msg ('p-prefix', {sepc, pages}, lower); | |||
elseif not is_set (nopp) then | |||
text = wrap_msg ('pp-prefix', {sepc, pages}, lower); | |||
else | |||
text = wrap_msg ('nopp', {sepc, pages}, lower); | |||
end | |||
end | |||
if is_set (minutes) then | |||
text = pend_separator (wrap_msg ('minutes', minutes, lower), sepc, true) .. text; | |||
else | |||
if is_set (time) then | |||
if not is_set (time_caption) then | |||
text = pend_separator (wrap_msg ('event', time, lower), sepc, true) .. text; | |||
else | |||
text = pend_separator (time_caption .. ' ' .. time .. text, sepc, true); | |||
end | |||
end | |||
end | |||
text = text .. pend_separator (at, sepc, true); | |||
text = text .. pend_separator (wrap_msg ('inset', inset, lower), sepc, true); | |||
if is_set (sections) then | |||
text = text .. pend_separator (wrap_msg ('sections', sections, lower), sepc, true); | |||
elseif is_set (section) then | |||
text = text .. pend_separator (wrap_msg ('section', section, lower), sepc, true); | |||
end | |||
return text; | |||
end | |||
--[[-------------------------< F O R M A T _ P U B L I S H E R >------------------------------------------ | |||
]] | |||
local function format_publisher (publisher_name, publication_place, periodical, cite_class, sepc) | |||
local publisher = ''; | |||
if is_set (publisher_name) then | |||
if is_set (publication_place) then | |||
publisher = publication_place .. ': ' .. publisher_name; | |||
else | |||
publisher = publisher_name; | |||
end | |||
elseif is_set (publication_place) then | |||
publisher = publication_place; | |||
end | |||
if is_set (publisher) then | |||
if is_set (periodical) and | |||
not in_array (cite_class, {'encyclopaedia', 'web', 'pressrelease', 'podcast'}) then | |||
publisher = ' (' .. publisher .. ')'; | |||
else | |||
publisher = pend_separator (publisher, sepc, true); | |||
end | |||
end | |||
return publisher; | |||
end | |||
--[[-------------------------< F O R M A T _ L I N K >------------------------------------------------- | |||
Format an external link that may or may not be raw. | |||
]] | |||
local function format_external_link (text, url_object, sepc) | |||
if is_set (text) then | |||
if is_set (url_object['url']) then | |||
text = make_external_link (url_object['url'], text, url_object['origin']); | |||
end | |||
text = pend_separator (text .. url_object['format'], sepc, true); | |||
elseif is_set (url_object['url']) then | |||
text = make_external_link (url_object['url'], nil, url_object['origin']); | |||
end | |||
return text; | |||
end | |||
--[[-------------------------< F O R M A T _ C O N F E R E N C E >---------------------------------------- | |||
]] | |||
local function format_conference (conference, conference_url_object, periodical, cite_class, sepc) | |||
local conf_text = format_external_link (conference, conference_url_object, sepc); | |||
if 'speech' == cite_class and is_set (periodical) then | |||
-- if cite speech, periodical (perhaps because of an included |website= or |journal= parameter) is set; | |||
conf_text = pend_separator (conf_text, sepc, false); -- then add appropriate punctuation to the end of the conference variable if set. | |||
end | |||
return conf_text; | |||
end | |||
--[[--------------------------< F O R M A T _ C H A P T E R _ T I T L E >-------------------------------------- | |||
Format the four chapter parameters: |script-chapter=, |chapter=, |trans-chapter=, and |chapter-url= into a single Chapter meta- | |||
parameter (chapter_url_source used for error messages). | |||
]] | |||
local function format_chapter_title (scriptchapter, chapter, transchapter, chapter_url_object, no_quotes, cite_class, title_type, sepc) | |||
local chapter_error = ''; | |||
if not is_set (chapter) then | |||
chapter = ''; -- to be safe for concatenation | |||
else | |||
if false == no_quotes then | |||
chapter = kern_quotes (chapter); -- if necessary, separate chapter title's leading and trailing quote marks from Module provided quote marks | |||
chapter = wrap_style ('quoted-title', chapter); | |||
end | |||
end | |||
chapter = script_concatenate (chapter, scriptchapter) -- <bdi> tags, lang atribute, categorization, etc; must be done after title is wrapped | |||
if is_set (transchapter) then | |||
transchapter = wrap_style ('trans-quoted-title', transchapter); | |||
if is_set (chapter) then | |||
chapter = chapter .. ' ' .. transchapter; | |||
else -- here when transchapter without chapter or script-chapter | |||
chapter = transchapter; | |||
chapter_error = ' ' .. set_error ('trans_missing_title', {'chapter'}); | |||
end | |||
end | |||
if is_set (chapter_url_object['url']) then | |||
chapter = make_external_link (chapter_url_object['url'], chapter, chapter_url_object['origin']) .. chapter_url_object['access-text']; | |||
-- adds bare_url_missing_title error if appropriate | |||
end | |||
chapter = chapter .. chapter_error; | |||
if is_set (chapter) then | |||
if 'map' == cite_class and is_set (title_type) then | |||
chapter = chapter .. ' ' .. title_type; | |||
end | |||
chapter = pend_separator (chapter .. chapter_url_object['format'], sepc, false); | |||
else -- |chapter= not set but |chapter-format= is so ... | |||
chapter = pend_separator (chapter_url_object['format'], sepc, false); -- ... ChapterFormat has error message, we want to see it | |||
end | |||
return chapter; | |||
end | |||
--[[--------------------------< F O R M A T _ M A I N _ T I T L E >------------------------------------------ | |||
Format the five title parameters: |script-title=, |title=, |trans-title=, |title-link=, and |url= into a single Title meta- | |||
parameter (url_origin and title_link_origin used for error messages). | |||
]] | |||
local function format_main_title (title, title_link, title_link_origin, script_title, trans_title, url_object, no_chapter_format, cite_class, periodical) | |||
if is_set (title_link) and is_set (title) then | |||
title = make_internal_link (title_link, title, title_link_origin); | |||
end | |||
if no_chapter_format or | |||
('map' == cite_class and is_set (periodical)) then -- special case for cite map when the map is in a periodical treat as an article | |||
title = kern_quotes (title); -- if necessary, separate title's leading and trailing quote marks from Module provided quote marks | |||
title = wrap_style ('quoted-title', title); | |||
title = script_concatenate (title, script_title); -- <bdi> tags, lang atribute, categorization, etc; must be done after title is wrapped | |||
trans_title= wrap_style ('trans-quoted-title', trans_title ); | |||
elseif 'report' == cite_class then -- no styling for cite report | |||
title = script_concatenate (title, script_title); -- <bdi> tags, lang atribute, categorization, etc; must be done after title is wrapped | |||
trans_title= wrap_style ('trans-quoted-title', trans_title ); -- for cite report, use this form for trans-title | |||
else | |||
title = wrap_style ('italic-title', title); | |||
title = script_concatenate (title, script_title); -- <bdi> tags, lang atribute, categorization, etc; must be done after title is wrapped | |||
trans_title = wrap_style ('trans-italic-title', trans_title); | |||
end | |||
local trans_error = ''; | |||
if is_set (trans_title) then | |||
if is_set (title) then | |||
trans_title = ' ' .. trans_title; | |||
else | |||
trans_error = ' ' .. set_error ('trans_missing_title', {'title'}); | |||
end | |||
end | |||
title = title .. trans_title; | |||
if is_set (title) then | |||
if not is_set (title_link) and is_set (url_object['url']) then | |||
title = make_external_link (url_object['url'], title, url_object['origin']) .. url_object['access-text'] .. trans_error .. url_object['format']; | |||
url_object = create_url_object (); | |||
else | |||
title = title .. trans_error; | |||
end | |||
end | |||
return title, url_object; | |||
end | |||
--[[--------------------------< F O R M A T _ F O R M A T >-------------------------------------------------------- | |||
Applies css style to |format=, |chapter-format=, etc. Also emits an error message if the format parameter does | |||
not have a matching url parameter. If the format parameter is not set and the url contains a file extension that | |||
is recognized as a pdf document by MediaWiki's commons.css, this code will set the format parameter to (PDF) with | |||
the appropriate styling. | |||
]] | |||
local function format_format (args) | |||
for _, url_object in pairs (args) do | |||
if is_set (url_object['format']) then | |||
url_object['format'] = wrap_style ('format', url_object['format']); -- add leading space, parenthases, resize | |||
if not is_set (url_object['url']) then | |||
url_object['format'] = url_object['format'] .. set_error ('format_missing_url', {url_object['format-origin'], url_object['origin']}); | |||
-- add an error message | |||
end | |||
elseif is_set (url_object['url']) then | |||
if url_object['url']:match ('%.pdf[%?#]?') or url_object['url']:match ('%.PDF[%?#]?') then | |||
-- format is not set so if url is a pdf file then | |||
url_object['format'] = wrap_style ('format', 'PDF'); -- set format to pdf | |||
end | |||
end | |||
end | |||
end | |||
--[[--------------------------< F O R M A T _ A C C E S S D A T E >---------------------------------------------- | |||
]] | |||
local function format_accessdate (accessdate, sepc, lower) | |||
if is_set (accessdate) then -- first, wrap in nowrap span if date in appropriate format | |||
if accessdate:match ('^%d%d%d%d%-%d%d%-%d%d$') then | |||
accessdate = wrap_style ('nowrap1', accessdate); -- when accessdate is YYYY-MM-DD format wrap in nowrap span: <span ...>YYYY-MM-DD</span>. | |||
elseif accessdate:match('^%a+%s*%d%d?,%s+%d%d%d%d$') or accessdate:match ('^%d%d?%s*%a+%s+%d%d%d%d$') then | |||
local cap, cap2 = string.match (accessdate, '^(.*)%s+(%d%d%d%d)$'); | |||
accessdate = wrap_style ('nowrap2', {cap, cap2}); -- when accessdate is DD MMMM YYYY or is MMMM DD, YYYY then wrap in nowrap span: <span ...>DD MMMM</span> YYYY or <span ...>MMMM DD,</span> YYYY | |||
end | |||
accessdate = ' ' .. wrap_msg ('retrieved', accessdate, lower); -- add retrieved text | |||
accessdate = wrap_style ('accessdate', {sepc, accessdate}); -- allow editors to hide accessdates | |||
end | |||
return accessdate; | |||
end | |||
--[[--------------------------< F O R M A T _ I D >---------------------------------------------------- | |||
]] | |||
local function format_id (id, docket, sepc, lower) | |||
id = pend_separator (id, sepc, true); | |||
return pend_separator (wrap_msg ('docket', docket, lower), sepc, true) .. id; | |||
end | |||
--[[--------------------------< F O R M A T _ Q U O T E >---------------------------------------------- | |||
]] | |||
local function format_quote (quote, sepc) | |||
if is_set (quote) then | |||
if quote:sub (1, 1) == '"' and quote:sub (-1, -1) == '"' then -- if first and last characters of quote are quote marks | |||
quote = quote:sub (2, -2); -- strip them off | |||
end | |||
return pend_separator (wrap_style ('quoted-text', quote), sepc, true); -- wrap in <q>...</q> tags | |||
end | |||
return ''; | |||
end | |||
--[[--------------------------< F O R M A T _ A R C H I V E >------------------------------------------ | |||
]] | |||
local function format_archive (archive_url_object, original_url_object, archive_date, dead_url, sepc, lower) | |||
local archived = ''; | |||
if is_set (archive_url_object['url']) then | |||
if not is_set (archive_date) then | |||
archive_date = set_error ('archive_missing_date'); | |||
end | |||
if in_array (dead_url, cfg.keywords['deadurl-live']) then | |||
local arch_text = cfg.messages['archived']; | |||
if (lower) then arch_text = arch_text:lower(); end; | |||
archived = pend_separator (wrap_msg ('archived-not-dead', {make_external_link (archive_url_object['url'], arch_text, archive_url_object['origin']) .. archive_url_object['format'], archive_date }, lower), sepc, true); | |||
if not is_set (original_url_object['url']) then | |||
archived = archived .. ' ' .. set_error ('archive_missing_url'); | |||
end | |||
elseif is_set (original_url_object['url']) then -- dead_url is not live, so it should be empty, dead or unfit | |||
if in_array (dead_url, cfg.keywords['deadurl-unfit']) then | |||
archived = pend_separator (wrap_msg('archived-unfit', archive_date, lower), sepc, true); | |||
-- format already styled | |||
else -- dead_url is empty or dead | |||
archived = pend_separator (wrap_msg ('archived-dead', | |||
{make_external_link (original_url_object['url'], cfg.messages['original'], original_url_object['origin']) .. original_url_object['access-text'] .. original_url_object['format'], archive_date }, lower), sepc, true); | |||
-- format already styled | |||
end | |||
else | |||
archived = pend_separator (wrap_msg ('archived-missing', | |||
{set_error ('archive_missing_url'), archive_date }, lower), sepc, true); | |||
end | |||
elseif is_set (original_url_object['format']) then | |||
archived = original_url_object['format']; -- if set and archive_url not set archive_format has error message | |||
end | |||
return archived; | |||
end | |||
--[[--------------------------< F O R M A T _ L A Y >--------------------------------------------------- | |||
]] | |||
local function format_lay (lay_url_object, lay_date, lay_source, sepc, lower) | |||
local lay = ''; | |||
if is_set (lay_url_object['url']) then | |||
if is_set (lay_date) then lay_date = ' (' .. lay_date .. ')' end | |||
if is_set (lay_source) then | |||
lay_source = wrap_msg ('lay source', lay_source, lower); | |||
else | |||
lay_source = ''; | |||
end | |||
local lay_sum = cfg.messages['lay summary']; | |||
if lower then | |||
lay_sum = lay_sum:lower(); | |||
end | |||
lay = pend_separator (make_external_link (lay_url_object['url'], lay_sum, lay_url_object['origin']) .. lay_url_object['format'] .. lay_source .. lay_date, sepc, true); | |||
else -- Test if |lay-format= is given without giving a |lay-url= | |||
lay = pend_separator (lay_url_object['format'], sepc, true); -- if set and LayURL not set, then LayFormat has error message | |||
end | |||
return lay; | |||
end | |||
--[[--------------------------< F O R M A T _ P E R I O D I C A L >------------------------------------ | |||
]] | |||
local function format_periodical (periodical, title, title_note, sepc) | |||
if is_set (periodical) then | |||
if is_set (title) or is_set (title_note) then | |||
return pend_separator (wrap_style ('italic-title', periodical), sepc, true); | |||
else | |||
return wrap_style ('italic-title', periodical); | |||
end | |||
end | |||
return ''; | |||
end | |||
--[[--------------------------< A N C H O R _ I D >------------------------------------------------------------ | |||
Generates a CITEREF anchor ID if we have at least one name or a date. Otherwise returns an empty string. | |||
namelist is one of the contributor-, author-, or editor-name lists chosen in that order. year is Year or anchor_year. | |||
]] | |||
local function anchor_id (namelist, year) | |||
local names={}; -- a table for the one to four names and year | |||
for i,v in ipairs (namelist) do -- loop through the list and take up to the first four last names | |||
names[i] = v.last | |||
if i == 4 then break end -- if four then done | |||
end | |||
table.insert (names, year); -- add the year at the end | |||
local id = table.concat (names); -- concatenate names and year for CITEREF id | |||
if is_set (id) then -- if concatenation is not an empty string | |||
return 'CITEREF' .. id; -- add the CITEREF portion | |||
else | |||
return ''; -- return an empty string; no reason to include CITEREF id in this citation | |||
end | |||
end | |||
--[[--------------------------< F O R M A T _ C I T A T I O N >-------------------------------------------- | |||
]] | |||
local function format_citation (body, cite_class, ref, namelist, year, ocins_output, no_tracking_cats) | |||
local options = {}; | |||
if is_set (cite_class) and cite_class ~= 'citation' then | |||
options.class = 'citation ' .. cite_class; -- class=citation required for blue highlight when used with |ref= | |||
else | |||
options.class = 'citation'; | |||
end | |||
if is_set (ref) and ref:lower() ~= 'none' then -- set reference anchor if appropriate | |||
local id = ref | |||
if ('harv' == ref ) then | |||
id = anchor_id (namelist, year); -- go make the CITEREF anchor | |||
end | |||
options.id = id; | |||
end | |||
if string.len (body:gsub ('<span[^>/]*>.-</span>', ''):gsub ('%b<>','')) <= 2 then | |||
reset_error ({'err_cats'}); | |||
body = set_error ('empty_citation'); | |||
reset_error ({'msg_tail'}); | |||
end | |||
local text; | |||
if is_set (options.id) then | |||
text = wrap_style ('citation-with-id', {mw.uri.anchorEncode (options.id), mw.text.nowiki (options.class), body}); | |||
else | |||
text = wrap_style ('citation-no-id', {mw.text.nowiki (options.class), body}); | |||
end | |||
text = text .. wrap_style ('OCinS', ocins_output); | |||
text = text .. make_error_tail (no_tracking_cats); -- append error/maintenance messages/categories to the citation | |||
return text; | |||
end | |||
--[[--------------------------< D E D U C E _ C I T A T I O N _ C L A S S >-------------------------------------- | |||
如果citation_class为citation({{citation}}),根据periodical系列参数的设置情况推断实际的引用类型。 | |||
]] | |||
local function deduce_citation_class (A, naive_class) | |||
local deduced_class; | |||
local periodical = A['Periodical']; | |||
local origin = A:ORIGIN ('Periodical'); | |||
for cite_class, aliases in pairs (cfg.periodical.parameters) do | |||
if cite_class ~= '_general' then | |||
for _, aliase in pairs (aliases) do | |||
if origin == aliase then | |||
deduced_class = cite_class; | |||
end | |||
end | |||
end | |||
end | |||
if (naive_class == 'citation') then | |||
if is_set (deduced_class) then | |||
return deduced_class, true; | |||
end | |||
elseif (naive_class ~= deduced_class) then | |||
local check_list = cfg.periodical.compatibility[naive_class]; | |||
if is_set (check_list) then | |||
if is_set (check_list['drop']) and in_array (deduced_class, check_list['drop']) then | |||
A['Periodical'] = discard_parameter (periodical, origin, nil); | |||
elseif is_set (check_list['warn']) and in_array (deduced_class, check_list['warn']) then | |||
append_error ('periodical', {origin, naive_class, deduced_class, check_list['suggest']}); | |||
end | |||
end | |||
end | |||
return naive_class, false; | |||
end | |||
--[[--------------------------< A R G U M E N T _ W R A P P E R >---------------------------------------------- | |||
Argument wrapper. This function provides support for argument mapping defined in the configuration file so that | |||
multiple names can be transparently aliased to single internal variable. | |||
]] | |||
local function argument_wrapper (args) | |||
local origin = {}; | |||
return setmetatable ({ | |||
ORIGIN = function (self, k) | |||
local dummy = self[k]; --force the variable to be loaded. | |||
return origin[k]; | |||
end | |||
}, | |||
{ | |||
__index = function (tbl, k) | |||
if origin[k] ~= nil then | |||
return nil; | |||
end | |||
local args, list, v = args, cfg.aliases[k]; | |||
if type (list) == 'table' then | |||
v, origin[k] = select_one (args, list, 'redundant_parameters'); | |||
if origin[k] == nil then | |||
origin[k] = ''; -- Empty string, not nil | |||
end | |||
elseif list ~= nil then | |||
v, origin[k] = args[list], list; | |||
else | |||
-- maybe let through instead of raising an error? | |||
-- v, origin[k] = args[k], k; | |||
throw_error ('unknown_argument_map'); | |||
end | |||
-- Empty strings, not nil; | |||
if v == nil then | |||
v = cfg.defaults[k] or ''; | |||
origin[k] = ''; | |||
end | |||
tbl = rawset (tbl, k, v); | |||
return v; | |||
end, | |||
}); | |||
end | |||
--[[--------------------------< D O _ C I T A T I O N >--------------------------------------------------------- | |||
This is the main function doing the majority of the citation formatting. | |||
]] | |||
local function do_citation (config, args) | |||
--local variables that are not cs1 parameters | |||
local this_page = mw.title.getCurrentTitle(); -- also used for COinS and for language | |||
--[[ | |||
Load Input Parameters | |||
The argument_wrapper facilitates the mapping of multiple aliases to single internal variable. | |||
]] | |||
local A = argument_wrapper (args); | |||
local citation_class, did_duduction = deduce_citation_class (A, config.CitationClass); | |||
-- Pick out the relevant fields from the arguments. Different citation templates | |||
-- define different field names for the same underlying things. | |||
------------------------------------------------- Get dates | |||
local Year = A['Year']; | |||
local PublicationDate = A['PublicationDate']; | |||
local OrigYear = A['OrigYear']; | |||
local Date = A['Date']; | |||
local Dateorigin = A:ORIGIN ('Date'); | |||
local LayDate = A['LayDate']; | |||
------------------------------------------------- Get title data | |||
local Title = A['Title']; | |||
local ScriptTitle = A['ScriptTitle']; | |||
local Conference = A['Conference']; | |||
local TransTitle = A['TransTitle']; | |||
local TitleNote = A['TitleNote']; | |||
local TitleLink = A['TitleLink']; | |||
local TitleLinkorigin = A:ORIGIN ('TitleLink'); | |||
local Periodical = A['Periodical']; | |||
local Periodical_origin = A:ORIGIN ('Periodical'); -- get the name of the periodical parameter | |||
local Series = A['Series']; | |||
local ConferenceURLobject = create_url_object (A['ConferenceURL'], A:ORIGIN ('ConferenceURL'), A['ConferenceFormat'], A:ORIGIN ('ConferenceFormat')); | |||
local ArchiveURLobject = create_url_object (A['ArchiveURL'], A:ORIGIN ('ArchiveURL'), A['ArchiveFormat'], A:ORIGIN ('ArchiveFormat')); | |||
local URLobject = create_url_object (A['URL'], A:ORIGIN ('URL'), A['Format'], A:ORIGIN ('Format'),A['UrlAccess'], A:ORIGIN ('UrlAccess')); | |||
local TranscriptURLobject = create_url_object (A['TranscriptURL'], A:ORIGIN ('TranscriptURL'), A['TranscriptFormat'], A:ORIGIN ('TranscriptFormat')); | |||
local LayURLobject = create_url_object (A['LayURL'], A:ORIGIN ('LayURL'), A['LayFormat'], A:ORIGIN ('LayFormat')); | |||
local Volume = A['Volume']; | |||
local Issue = A['Issue']; | |||
local Page = A['Page']; | |||
local Pages = hyphen_to_dash (A['Pages']); | |||
local At = A['At']; | |||
if not in_array (citation_class, cfg.args_support['templates_using_volume']) then | |||
Volume = discard_parameter (Volume, A:ORIGIN ('Volume'), nil); | |||
end | |||
if not in_array (citation_class, cfg.args_support['templates_using_issue']) then | |||
if (A:ORIGIN ('Issue') ~= 'number') then | |||
Issue = discard_parameter (Issue, A:ORIGIN ('Issue'), nil); | |||
else | |||
Issue = nil; | |||
end | |||
end | |||
if in_array (citation_class, cfg.args_support['templates_not_using_page']) then | |||
Page = discard_parameter (Page, A:ORIGIN ('Page'), nil); | |||
Pages = discard_parameter (Pages, A:ORIGIN ('Pages'), nil); | |||
At = discard_parameter (At, A:ORIGIN ('At'), nil); | |||
end | |||
local Minutes = A['Minutes']; | |||
local Time = A['Time']; | |||
local TimeCaption = A['TimeCaption']; | |||
if not in_array (citation_class, cfg.args_support['templates_involving_time']) then | |||
Minutes = discard_parameter (Minutes, A:ORIGIN ('Minutes'), nil); | |||
Time = discard_parameter (Time, A:ORIGIN ('Time'), nil); | |||
TimeCaption = discard_parameter (TimeCaption, A:ORIGIN ('TimeCaption'), nil); | |||
end | |||
local Sheet = A['Sheet']; | |||
local Sheets = A['Sheets']; | |||
local Section = A['Section']; | |||
local Sections = A['Sections']; | |||
local Inset = A['Inset']; | |||
if not ('map' == citation_class) then | |||
Sheet = discard_parameter (Sheet, A:ORIGIN ('Sheet'), nil); | |||
Sheets = discard_parameter (Sheets, A:ORIGIN ('Sheets'), nil); | |||
Sections = discard_parameter (Sections, A:ORIGIN ('Sections'), nil); | |||
Inset = discard_parameter (Inset, A:ORIGIN ('Inset'), nil); | |||
end | |||
--[[ | |||
不知道哪个“天才”想出来的点子,现行引用模板里,section一个参数多个涵义。 | |||
在书籍类引用中,section是章节名称,在地图引用中,section是地图的区域编号。 | |||
所以一旦知道citation_class不是地图,就可以丢弃上述几乎全部参数, | |||
唯独section参数需要留到检查章节相关参数时一并进行检查。 | |||
]] | |||
local Chapter = ''; | |||
local ScriptChapter = ''; | |||
local TransChapter = ''; | |||
local ChapterURLobject; | |||
local no_chapter_format = in_array (citation_class, cfg.args_support['templates_not_using_chapter_format']); | |||
if in_array (citation_class, cfg.args_support['templates_not_using_chapter']) then | |||
ChapterURLobject = create_url_object (); | |||
discard_chapter (A); | |||
if not ('map' == citation_class) then | |||
Section = discard_parameter (Section, A:ORIGIN ('Section'), nil); | |||
end | |||
else | |||
Chapter = A['Chapter']; | |||
ScriptChapter = A['ScriptChapter']; | |||
TransChapter = A['TransChapter']; | |||
ChapterURLobject = create_url_object (A['ChapterURL'], A:ORIGIN ('ChapterURL'), A['ChapterFormat'], A:ORIGIN ('ChapterFormat'), A['ChapterUrlAccess'], A:ORIGIN ('ChapterUrlAccess')); | |||
if is_set (Chapter) then | |||
if is_set (Section) then | |||
select_one (args, {'chapter', 'contribution', 'section'}, 'redundant_parameters'); | |||
end | |||
else | |||
Chapter = Section; | |||
Section = nil; | |||
end | |||
end | |||
local Edition = A['Edition']; | |||
local PublicationPlace = A['PublicationPlace'] | |||
local Place = A['Place']; | |||
local PublisherName = A['PublisherName']; | |||
local RegistrationRequired = sanitized_parameter_value (A['RegistrationRequired'], A:ORIGIN ('RegistrationRequired'), 'yes_true_y', nil); | |||
local SubscriptionRequired = sanitized_parameter_value (A['SubscriptionRequired'], A:ORIGIN ('SubscriptionRequired'), 'yes_true_y', nil); | |||
local Via = A['Via']; | |||
local AccessDate = A['AccessDate']; | |||
local ArchiveDate = A['ArchiveDate']; | |||
local Agency = A['Agency']; | |||
local DeadURL = sanitized_parameter_value (A['DeadURL'], A:ORIGIN ('DeadURL'), 'deadurl', ''); | |||
local Language = A['Language']; | |||
local DoiBroken = A['DoiBroken']; | |||
local ID = A['ID']; | |||
local ASINTLD = A['ASINTLD']; | |||
local IgnoreISBN = sanitized_parameter_value (A['IgnoreISBN'], A:ORIGIN ('IgnoreISBN'), 'yes_true_y', nil); | |||
local Embargo = A['Embargo']; | |||
local Class = A['Class']; -- arxiv class identifier | |||
local ID_list = extract_ids (args); | |||
local ID_access_levels = extract_id_access_levels (args, ID_list); | |||
local LaySource = A['LaySource']; | |||
local Transcript = A['Transcript']; | |||
local no_tracking_cats = set_no_tracking_cats (A['NoTracking'], A:ORIGIN ('NoTracking'), this_page); | |||
local Quote = A['Quote']; | |||
local sepc, PostScript, Ref = set_style (A['Mode'], A:ORIGIN ('Mode'), A['PostScript'], A['Ref'], Quote, config.CitationClass); | |||
if is_set (Quote) and is_set (A['PostScript']) then | |||
select_one (args, {'postscript', 'quote', 'quotation'}, 'redundant_parameters'); | |||
end | |||
local use_lowercase = ( sepc == ',' ); -- used to control capitalization for certain static text | |||
-- check for insource-location-related parameters like |page=, |pages= or |at=. 请注意section参数有歧义,如果section跟书有关系,上面已经被清空了,这里不纳入检查。 | |||
select_one (args, {'at', 'time', 'minutes'}, 'redundant_parameters'); | |||
select_one (args, {'page', 'p', 'pp', 'pages', 'at', 'time', 'minutes', 'sheet', 'sheets'}, 'redundant_parameters'); | |||
if is_set (Section) then | |||
select_one (args, {'at', 'section', 'sections'}, 'redundant_parameters'); | |||
else | |||
select_one (args, {'at', 'sections'}, 'redundant_parameters'); | |||
end | |||
-- Dummy calls simply to get the error messages and categories | |||
local NoPP = sanitized_parameter_value (A['NoPP'], A:ORIGIN ('NoPP'), 'yes_true_y', nil); | |||
if is_set (Page) then | |||
if is_set (Pages) or is_set (At) then | |||
Pages = ''; -- unset the others | |||
At = ''; | |||
end | |||
extra_text_in_parameter_check (Page, 'page'); -- add this page to maint cat if |page= value begins with what looks like p. or pp. | |||
elseif is_set (Pages) then | |||
if is_set (At) then | |||
At = ''; -- unset | |||
end | |||
extra_text_in_parameter_check (Pages, 'page'); -- add this page to maint cat if |pages= value begins with what looks like p. or pp. | |||
end | |||
-- both |publication-place= and |place= (|location=) allowed if different | |||
if not is_set (PublicationPlace) and is_set (Place) then | |||
PublicationPlace = Place; -- promote |place= (|location=) to |publication-place | |||
end | |||
if PublicationPlace == Place then Place = ''; end -- don't need both if they are the same | |||
if is_set (Edition) then | |||
extra_text_in_parameter_check (Edition, 'edition'); | |||
end | |||
------------------------------------------------- Get people | |||
local NameListFormat = sanitized_parameter_value (A['NameListFormat'], A:ORIGIN ('NameListFormat'), 'name-list-format', ''); | |||
local LastAuthorAmp = sanitized_parameter_value (A['LastAuthorAmp'], A:ORIGIN ('LastAuthorAmp'), 'yes_true_y', nil); | |||
local contributors_valid = in_array (citation_class, cfg.args_support['templates_using_contributor']); | |||
local Authors, Contributors, Editors, Translators, Contribution, NameList, multiple_editors, has_contributors = | |||
get_people ( | |||
{ | |||
vauthors = A['Vauthors'], authors = A['Authors'], veditors = A['Veditors'], editors = A['Editors'], contribution = A['Contribution'], coauthors = A['Coauthors'] | |||
}, { | |||
displayauthors = A['DisplayAuthors'], displayeditors = A['DisplayEditors'], contributorsvalid = contributors_valid, namelistformat = NameListFormat, lastauthoramp = LastAuthorAmp | |||
}, args, this_page.name | |||
); -- (co-)authors, contributors, editors and translators | |||
local TitleType = set_titletype (citation_class, A['TitleType']); -- handle type parameter for those CS1 citations that have default values | |||
-- special case for cite thesis | |||
local Degree = A['Degree']; | |||
if 'thesis' == citation_class then | |||
if (is_set (Degree)) then | |||
TitleType = wrap_msg ('thesis with type', Degree, use_lowercase); | |||
else | |||
TitleType = wrap_msg ('thesis no type', 'placeholder', use_lowercase); | |||
end | |||
else | |||
Degree = discard_parameter (Degree, A:ORIGIN ('Degree'), nil); | |||
end | |||
local Others = A['Others']; | |||
--[[ | |||
Parameter remapping for cite encyclopedia: | |||
When the citation has these parameters: | |||
|encyclopedia and |title then map |title to |article and |encyclopedia to |title | |||
|encyclopedia and |article then map |encyclopedia to |title | |||
|encyclopedia then map |encyclopedia to |title | |||
|trans_title maps to |trans_chapter when |title is re-mapped | |||
|url maps to |chapterurl when |title is remapped | |||
All other combinations of |encyclopedia, |title, and |article are not modified | |||
]] | |||
local Encyclopedia; | |||
local Entry = A['Entry']; | |||
if (citation_class == 'encyclopaedia') then -- test code for citation | |||
local entry_redundant = false | |||
Encyclopedia = Periodical; | |||
if is_set (Periodical) then | |||
if is_set (Title) or is_set (ScriptTitle) then | |||
Chapter = Title; | |||
ScriptChapter = ScriptTitle; | |||
ScriptTitle = ''; | |||
TransChapter = TransTitle; | |||
TransTitle = ''; | |||
ChapterURLobject = URLobject; | |||
URLobject = create_url_object (); | |||
if not is_set (ChapterURLobject['url']) and is_set (TitleLink) then | |||
Chapter = make_internal_link (TitleLink, Chapter, TitleLinkorigin); | |||
end | |||
TitleLink = ''; | |||
entry_redundant = is_set (Entry); | |||
elseif is_set (Entry) then | |||
Chapter = Entry; | |||
ChapterURLobject = URLobject; | |||
URLobject = create_url_object (); | |||
end | |||
Title = Periodical; -- |encyclopedia set and |article set or not set so map |encyclopedia to |title | |||
Periodical = ''; -- redundant so unset | |||
else | |||
if is_set (Title) or is_set (ScriptTitle) then | |||
entry_redundant = is_set (Entry); | |||
else | |||
Title = Entry; | |||
end | |||
end | |||
if entry_redundant then | |||
select_one (args, {'title', 'script-title', 'article', 'entry'}, 'redundant_parameters'); | |||
end | |||
else | |||
Entry = discard_parameter (Entry, A:ORIGIN ('Entry'), nil); | |||
end | |||
-- Special case for cite report. | |||
local Docket = A['Docket']; | |||
if citation_class == 'report' then | |||
if is_set (Docket) then | |||
if is_set (ID) then | |||
select_one (args, {'id', 'docket'}, 'redundant_parameters'); | |||
end | |||
ID = Docket; -- for cite report when |docket= is set, overwrite ID even if |id= is set | |||
Docket = ''; | |||
end | |||
elseif citation_class ~= 'thesis' then | |||
discard_parameter (Docket, A:ORIGIN ('Docket'), ''); | |||
end | |||
-- Special case for cite techreport. | |||
local Num = A['Number']; | |||
if citation_class == 'techreport' then -- special case for cite techreport | |||
if is_set (Num) then -- cite techreport uses 'number', which other citations alias to 'issue' | |||
if not is_set (ID) then -- can we use ID for the "number"? | |||
ID = Num; -- yes, use it | |||
else -- ID has a value so emit error message | |||
select_one (args, {'id', 'number'}, 'redundant_parameters'); | |||
end | |||
end | |||
elseif not is_set (Issue) then | |||
Num = discard_parameter (Num, A:ORIGIN ('Number'), nil); | |||
end | |||
-- special case for cite interview | |||
local Callsign = A['Callsign']; | |||
local City = A['City']; | |||
local Program = A['Program']; | |||
if (citation_class == 'interview') then | |||
if is_set (Program) then | |||
ID = ' ' .. Program; | |||
end | |||
if is_set (Callsign) then | |||
if is_set (ID) then | |||
ID = ID .. pend_separator (Callsign, sepc, true); | |||
else | |||
ID = ' ' .. Callsign; | |||
end | |||
end | |||
if is_set (City) then | |||
if is_set (ID) then | |||
ID = ID .. pend_separator (City, sepc, true); | |||
else | |||
ID = ' ' .. City; | |||
end | |||
end | |||
if is_set (Others) then | |||
Others = wrap_msg ('interview', {TitleType, Others}, use_lowercase); | |||
TitleType = ''; | |||
end | |||
else | |||
Callsign = discard_parameter (Callsign, A:ORIGIN ('Callsign'), nil ); | |||
City = discard_parameter (City, A:ORIGIN ('City'), nil ); | |||
Program = discard_parameter (Program, A:ORIGIN ('Program'), nil); | |||
end | |||
if is_set (TitleType) then -- if type parameter is specified | |||
TitleType = wrap_msg ('type', TitleType, use_lowercase); -- display it in parentheses | |||
end | |||
-- Account for the oddity that is {{cite conference}} or {{cite speech}}. | |||
local BookTitle = A['BookTitle']; | |||
if 'conference' == citation_class then | |||
if is_set (BookTitle) then | |||
ChapterURLobject = URLobject; | |||
URLobject = create_url_object (); | |||
TransChapter = TransTitle; | |||
TransTitle = ''; | |||
Chapter = Title; | |||
Title = BookTitle; | |||
end | |||
else | |||
BookTitle = discard_parameter (BookTitle, A:ORIGIN ('BookTitle'), nil); | |||
if 'speech' == citation_class then | |||
TitleNote = discard_parameter (TitleNote, A:ORIGIN ('TitleNote'), TitleType); | |||
-- override whatever may be the value assigned to TitleNote (through |department=) and forces it to be " (Speech)" so that the annotation directly follows the |title= parameter value in the citation rather than the |event= parameter value (if provided). | |||
TitleType = ''; -- annotate the citation | |||
else | |||
Conference = discard_parameter (Conference, A:ORIGIN ('Conference'), ''); | |||
-- not cite conference or cite speech so make sure this is empty string | |||
end | |||
end | |||
-- cite map oddities | |||
local Cartography = A['Cartography']; | |||
local Scale = A['Scale']; | |||
if citation_class == 'map' then | |||
Chapter = A['Map']; | |||
TransChapter = A['TransMap']; | |||
ChapterURLobject = create_url_object (A['MapURL'], A:ORIGIN ('MapURL'), A['MapFormat'], A:ORIGIN ('MapFormat'), A['MapUrlAccess'], A:ORIGIN ('MapUrlAccess')); | |||
Cartography = pend_separator (wrap_msg ('cartography', Cartography, use_lowercase), sepc, true); | |||
Scale = pend_separator (Scale, sepc, true); | |||
else | |||
Cartography = discard_parameter (Cartography, A:ORIGIN ('Cartography'), ''); | |||
Scale = discard_parameter (Scale, A:ORIGIN ('Scale'), ''); | |||
discard_parameter (A['Map'], A:ORIGIN ('Map'), nil); | |||
discard_parameter (A['MapURL'], A:ORIGIN ('MapURL'), nil); | |||
discard_parameter (A['TransMap'], A:ORIGIN ('TransMap'), nil); | |||
discard_parameter (A['MapFormat'], A:ORIGIN ('MapFormat'), nil); | |||
discard_parameter (A['MapUrlAccess'], A:ORIGIN ('MapUrlAccess'), nil); | |||
end | |||
-- Account for the oddities that are {{cite episode}} and {{cite serial}}, before generation of COinS data. | |||
if 'episode' == citation_class or 'serial' == citation_class then | |||
local AirDate = A['AirDate']; | |||
local SeriesLink = A['SeriesLink']; | |||
local Network = A['Network']; | |||
local Station = A['Station']; | |||
local s, n = {}, {}; | |||
-- do common parameters first | |||
if is_set (Network) then table.insert (n, Network); end | |||
if is_set (Station) then table.insert (n, Station); end | |||
ID = table.concat (n, sepc .. ' '); | |||
if is_set (AirDate) then | |||
if not is_set (Date) then -- promote airdate to date | |||
Date = AirDate; | |||
Dateorigin = A:ORIGIN ('AirDate'); | |||
else | |||
select_one (args, {'date', 'air-date', 'airdate'}, 'redundant_parameters'); | |||
end | |||
end | |||
if 'episode' == citation_class then -- handle the oddities that are strictly {{cite episode}} | |||
local Season = A['Season']; | |||
local SeriesNumber = A['SeriesNumber']; | |||
if is_set (Season) and is_set (SeriesNumber) then -- these are mutually exclusive so if both are set | |||
select_one (args, {'season', 'series-number', 'series-no', 'seriesnumber', 'seriesno'}, 'redundant_parameters'); | |||
-- add error message | |||
SeriesNumber = ''; -- unset; prefer |season= over |seriesno= | |||
end | |||
-- assemble a table of parts concatenated later into Series | |||
if is_set (Season) then table.insert (s, wrap_msg ('season', Season, use_lowercase)); end | |||
if is_set (SeriesNumber) then table.insert (s, wrap_msg ('series', SeriesNumber, use_lowercase)); end | |||
if is_set (Issue) then table.insert (s, wrap_msg ('episode', Issue, use_lowercase)); end | |||
Issue = ''; -- unset because this is not a unique parameter | |||
Chapter = Title; -- promote title parameters to chapter | |||
ScriptChapter = ScriptTitle; | |||
local ChapterLink = TitleLink; -- alias episodelink | |||
local ChapterLinkorigin = TitleLinkorigin; | |||
TransChapter = TransTitle; | |||
ChapterURLobject = URLobject; | |||
Title = Series; -- promote series to title | |||
TitleLink = SeriesLink; | |||
TitleLinkorigin = A:ORIGIN ('SeriesLink'); | |||
Series = table.concat (s, sepc .. ' '); -- this is concatenation of season, seriesno, episode number | |||
if is_set (ChapterLink) and not is_set (ChapterURL) then -- link but not URL | |||
Chapter = make_internal_link (ChapterLink, Chapter, ChapterLinkorigin); | |||
-- ok to wikilink | |||
elseif is_set (ChapterLink) and is_set (ChapterURL) then -- if both are set, URL links episode; | |||
Series = make_internal_link (ChapterLink, Series, ChapterLinkorigin); | |||
-- series links with ChapterLink (episodelink -> TitleLink -> ChapterLink) ugly | |||
end | |||
URLobject = create_url_object (); -- unset | |||
TransTitle = ''; | |||
ScriptTitle = ''; | |||
else -- now oddities that are cite serial | |||
Chapter = A['Episode']; -- TODO: make |episode= available to cite episode someday? | |||
if is_set (Series) and is_set (SeriesLink) then | |||
Series = make_internal_link (SeriesLink, Series, A:ORIGIN ('SeriesLink')); | |||
end | |||
Series = wrap_style ('italic-title', Series); -- series is italicized | |||
end | |||
end | |||
-- end of {{cite episode}} stuff | |||
-- Account for the oddities that are {{cite arxiv}}, before generation of COinS data. | |||
if 'arxiv' == citation_class then | |||
if not is_set (ID_list['ARXIV']) then -- |arxiv= or |eprint= required for cite arxiv | |||
append_error ('arxiv_missing', {}); -- add error message | |||
elseif is_set (Series) then -- series is an alias of version | |||
ID_list['ARXIV'] = ID_list['ARXIV'] .. Series; -- concatenate version onto the end of the arxiv identifier | |||
Series = ''; -- unset | |||
deprecated_parameter ('version'); -- deprecated parameter but only for cite arxiv | |||
end | |||
if first_set ({AccessDate, At, URLobject['format'], Page, Pages, PublisherName, URLobject['url'], -- a crude list of parameters that are not supported by cite arxiv | |||
ID_list['ASIN'], ID_list['BIBCODE'], ID_list['DOI'], ID_list['ISBN'], ID_list['ISSN'], | |||
ID_list['JFM'], ID_list['JSTOR'], ID_list['LCCN'], ID_list['MR'], ID_list['OCLC'], ID_list['OL'], | |||
ID_list['OSTI'], ID_list['PMC'], ID_list['PMID'], ID_list['RFC'], ID_list['SSRN'], ID_list['USENETID'], ID_list['ZBL']},27) then | |||
append_error ('arxiv_params_not_supported', {}); -- add error message | |||
AccessDate= ''; -- set these to empty string; not supported in cite arXiv | |||
PublisherName = ''; -- (if the article has been published, use cite journal, or other) | |||
URLobject = create_url_object (); | |||
Page = ''; Pages = ''; At = ''; | |||
end | |||
Periodical = 'arXiv'; -- periodical not allowed in cite arxiv; if article has been published, use cite journal | |||
-- set to arXiv for COinS; after that, must be set to empty string | |||
end | |||
-- legacy: promote concatenation of |month=, and |year= to Date if Date not set; or, promote PublicationDate to Date if neither Date nor Year are set. | |||
if not is_set (Date) then | |||
if is_set (Year) then | |||
Date = Year; | |||
Dateorigin = A:ORIGIN ('Year'); -- promote Year to Date | |||
Year = nil; -- make nil so Year as empty string isn't used for CITEREF | |||
elseif is_set (PublicationDate) then -- use PublicationDate when |date= and |year= are not set | |||
Date = PublicationDate; | |||
Dateorigin = A:ORIGIN ('PublicationDate'); -- promote PublicationDate to Date | |||
PublicationDate = ''; | |||
end | |||
else | |||
if is_set (PublicationDate) and PublicationDate ~= Date then | |||
PublicationDate = wrap_msg ('publication-date', PublicationDate, use_lowercase); | |||
else | |||
PublicationDate = ''; -- if PublicationDate is same as Date, don't display in rendered citation | |||
end | |||
end | |||
local COinS_date = {}; -- holds date info extracted from |date= for the COinS metadata by Module:Date verification | |||
local anchor_year = validate_date (AccessDate, ArchiveDate, Date, DoiBroken, Embargo, LayDate, PublicationDate, Year, COinS_date, Dateorigin); | |||
-- used in the CITEREF identifier | |||
-- Account for the oddity that is {{cite journal}} with |pmc= set and |url= not set. Do this after date check but before COInS. | |||
-- Here we unset Embargo if PMC not embargoed (|embargo= not set in the citation) or if the embargo time has expired. Otherwise, holds embargo date | |||
Embargo = is_embargoed (Embargo); -- | |||
if citation_class == 'journal' and not is_set (URLobject['url']) and is_set (ID_list['PMC']) then | |||
if not is_set (Embargo) then -- if not embargoed or embargo has expired | |||
URLobject['url'] =cfg.id_handlers['PMC'].prefix .. ID_list['PMC']; -- set url to be the same as the PMC external link if not embargoed | |||
URLobject['origin'] = cfg.id_handlers['PMC'].parameters[1]; -- set URLorigin to parameter name for use in error message if citation is missing a |title= | |||
end | |||
end | |||
if not is_set (URLobject['url']) then | |||
if in_array (citation_class, cfg.args_support['templates_requiring_url']) then | |||
append_error ('cite_web_url', {}); | |||
end | |||
-- Test if accessdate is given without giving a URL | |||
if is_set (AccessDate) and not is_set (ChapterURLobject['url']) then -- ChapterURL may be set when the others are not set; TODO: move this to a separate test? | |||
append_error ('accessdate_missing_url', {}); | |||
AccessDate = ''; | |||
end | |||
end | |||
-- At this point fields may be nil if they weren't specified in the template use. We can use that fact. | |||
-- Test if citation has no title | |||
if not is_set (Title) and | |||
not is_set (TransTitle) and | |||
not is_set (ScriptTitle) then | |||
if 'episode' == citation_class then -- special case for cite episode; TODO: is there a better way to do this? | |||
append_error ('citation_missing_title', {'series'}); | |||
else | |||
append_error ('citation_missing_title', {'title'}); | |||
end | |||
end | |||
if 'none' == Title and citation_class == 'journal' then -- special case for journal cites | |||
Title = ''; -- set title to empty string | |||
add_maint_cat ('untitled'); | |||
end | |||
check_for_external_link ({ -- add error message when any of these parameters contains a URL | |||
['title'] = Title, | |||
[A:ORIGIN ('Chapter')] = Chapter, | |||
[A:ORIGIN ('Periodical')] = Periodical, | |||
[A:ORIGIN ('PublisherName')] = PublisherName, | |||
}); | |||
-- COinS metadata (see <http://ocoins.info/>) for automated parsing of citation information. | |||
-- handle the oddity that is cite encyclopedia and {{citation |encyclopedia=something}}. Here we presume that | |||
-- when Periodical, Title, and Chapter are all set, then Periodical is the book (encyclopedia) title, Title | |||
-- is the article title, and Chapter is a section within the article. So, we remap | |||
local coins_chapter = Chapter; -- default assuming that remapping not required | |||
local coins_title = Title; -- et tu | |||
if 'encyclopaedia' == citation_class then | |||
if is_set (Chapter) and is_set (Title) and is_set (Periodical) then -- if all are used then | |||
coins_chapter = Title; -- remap | |||
coins_title = Periodical; | |||
end | |||
end | |||
-- this is the function call to COinS() | |||
local OCinSoutput = COinS ({ | |||
['Periodical'] = Periodical, | |||
['Encyclopedia'] = Encyclopedia, | |||
['Chapter'] = coins_chapter, | |||
['ScriptChapter'] = ScriptChapter, | |||
['Map'] = Map, | |||
['Degree'] = Degree; -- cite thesis only | |||
['Title'] = coins_title, | |||
['ScriptTitle'] = ScriptTitle, | |||
['PublicationPlace'] = PublicationPlace, | |||
['Date'] = COinS_date.rftdate, -- COinS_date has correctly formatted date if Date is valid; | |||
['Season'] = COinS_date.rftssn, | |||
['Chron'] = COinS_date.rftchron or (not COinS_date.rftdate and Date) or '', -- chron but if not set and invalid date format use Date; keep this last bit? | |||
['Series'] = Series, | |||
['Volume'] = Volume, | |||
['Issue'] = Issue, | |||
['Pages'] = first_set ({Sheet, Sheets, Page, Pages, At}, 5), | |||
['Edition'] = Edition, | |||
['PublisherName'] = PublisherName, | |||
['URL'] = first_set ({ChapterURLobject['url'], URLobject['url']}, 2), | |||
['Authors'] = NameList, | |||
['ID_list'] = ID_list, | |||
['RawPage'] = this_page.prefixedText, | |||
}, config.CitationClass); | |||
-- Account for the oddities that are {{cite arxiv}}, AFTER generation of COinS data. | |||
if 'arxiv' == citation_class then -- we have set rft.jtitle in COinS to arXiv, now unset so it isn't displayed | |||
Periodical = ''; | |||
end | |||
-- special case for cite newsgroup. Do this after COinS because we are modifying Publishername to include some static text | |||
if 'newsgroup' == citation_class then | |||
if is_set (PublisherName) then | |||
PublisherName = wrap_msg ('newsgroup', make_external_link ('news:' .. PublisherName, PublisherName, A:ORIGIN ('PublisherName')), use_lowercase); | |||
end | |||
end | |||
-- Now perform various field substitutions. | |||
-- We also add leading spaces and surrounding markup and punctuation to the | |||
-- various parts of the citation, but only when they are non-nil. | |||
-- apply |[xx-]format= styling; at the end, these parameters hold correctly styled format annotation, | |||
-- an error message if the associated url is not set, or an empty string for concatenation | |||
format_format ({ArchiveURLobject, ConferenceURLobject, URLobject, LayURLobject, TranscriptURLobject, ChapterURLobject}); | |||
-- special case for chapter format so no error message or cat when chapter not supported | |||
if format_url_access_text (URLobject, SubscriptionRequired, RegistrationRequired) then | |||
select_one (args, {'url-access', 'urlaccess', 'registration', 'subscription'}, 'redundant_parameters'); | |||
end -- 只需其一 | |||
format_url_access_text (ChapterURLobject, nil, nil); | |||
local OriginalURLobject; -- TODO: swap chapter and title here so that archive applies to most specific if both are set? | |||
OriginalURLobject, URLobject, ChapterURLobject = | |||
swap_urls (URLobject, ChapterURLobject, ArchiveURLobject, DeadURL); | |||
local chapter_no_quotes = false; -- default assume that we will be quoting the chapter parameter value | |||
if is_set (Contribution) and has_contributors then -- if this is a contribution with contributor(s) | |||
if in_array (Contribution:lower(), cfg.keywords['contribution']) then -- and a generic contribution title | |||
chapter_no_quotes = true; -- then render it unquoted | |||
end | |||
end | |||
Chapter = format_chapter_title (ScriptChapter, Chapter, TransChapter, ChapterURLobject, chapter_no_quotes, citation_class, TitleType, sepc); | |||
-- Contribution is also in Chapter | |||
-- Format main title. | |||
Title, URLobject = format_main_title (Title, TitleLink, TitleLinkorigin, ScriptTitle, TransTitle, URLobject, no_chapter_format, citation_class, Periodical); | |||
Place = pend_separator (wrap_msg ('written', Place, use_lowercase), sepc, false); | |||
Conference = format_conference (Conference, ConferenceURLobject, Periodical, citation_class, sepc); | |||
local Insource_location = format_insource_location (Page, Pages, Sheet, Sheets, At, Minutes, Time, TimeCaption, Section, Sections, Inset, citation_class, Periodical_origin, sepc, NoPP, use_lowercase); | |||
Language = language_parameter (Language); -- format, categories, name from ISO639-1, etc | |||
Others = pend_separator (Others, sepc, true); | |||
Others = pend_separator (wrap_msg ('translated', Translators, use_lowercase), sepc, true) .. Others; | |||
if 'speech' ~= citation_class then | |||
TitleNote = pend_separator (TitleNote, sepc, true); | |||
end | |||
Edition = wrap_msg ('edition', Edition, use_lowercase); | |||
Series = pend_separator (Series, sepc, true); | |||
OrigYear = wrap_msg ('orig year', OrigYear, use_lowercase); | |||
Agency = pend_separator (Agency, sepc, true); | |||
Volume = format_volume_issue (Volume, Issue, citation_class, Periodical_origin, sepc, use_lowercase); | |||
------------------------------------ totally unrelated data | |||
Via = wrap_msg ('via', Via, use_lowercase); | |||
AccessDate = format_accessdate (AccessDate, sepc, use_lowercase); | |||
ID = format_id (ID, Docket, sepc, use_lowercase); | |||
ID_list = build_id_list (ID_list, {IdAccessLevels=ID_access_levels, DoiBroken = DoiBroken, ASINTLD = ASINTLD, IgnoreISBN = IgnoreISBN, Embargo=Embargo, Class = Class}); | |||
local URL = ''; | |||
if is_set (URLobject['url']) then | |||
URL = ' ' .. make_external_link (URLobject['url'], nil, URLobject['origin']) .. URLobject['access-text']; | |||
end | |||
local Format = URLobject['format']; | |||
Quote = format_quote (Quote, sepc); | |||
local Archived = format_archive (ArchiveURLobject, OriginalURLobject, ArchiveDate, DeadURL, sepc, use_lowercase); | |||
local Lay = format_lay (LayURLobject, LayDate, LaySource, sepc, use_lowercase); | |||
Transcript = format_external_link (Transcript, TranscriptURLobject, sepc); | |||
local Publisher = format_publisher (PublisherName, PublicationPlace, Periodical, citation_class, sepc); | |||
local use_in = is_set (Chapter) and (not has_contributors); | |||
Authors, Editors, Contributors = format_people (Authors, Editors, Contributors, multiple_editors, use_in, sepc); | |||
-- Several of the above rely upon detecting this as nil, so do it last. | |||
Periodical = format_periodical (Periodical, Title, TitleNote, sepc); | |||
-- Piece all bits together at last. Here, all should be non-nil. | |||
-- We build things this way because it is more efficient in LUA | |||
-- not to keep reassigning to the same string variable over and over. | |||
local tcommon; | |||
local tcommon2; -- used for book cite when |contributor= is set | |||
if citation_class == 'journal' and is_set (Periodical) then | |||
Others = pend_separator (Others, sepc, false); | |||
tcommon = safe_join ({Others, Title, TitleNote, Conference, Periodical, Format, TitleType, Series, Edition, Publisher, Agency}, sepc); | |||
elseif contributors_valid then -- special cases for book cites where contributors are allowed | |||
if is_set (Contributors) then -- when we are citing foreword, preface, introduction, etc | |||
tcommon = safe_join ({Title, TitleNote}, sepc); -- author and other stuff will come after this and before tcommon2 | |||
tcommon2 = safe_join ({Conference, Periodical, Format, TitleType, Series, Volume, Others, Edition, Publisher, Agency}, sepc); | |||
else | |||
tcommon = safe_join ({Title, TitleNote, Conference, Periodical, Format, TitleType, Series, Volume, Others, Edition, Publisher, Agency}, sepc); | |||
end | |||
elseif 'map' == citation_class then -- special cases for cite map | |||
if is_set (Chapter) then -- map in a book; TitleType is part of Chapter | |||
tcommon = safe_join ({Title, Format, Edition, Scale, Series, Cartography, Others, Publisher, Volume}, sepc); | |||
elseif is_set (Periodical) then -- map in a periodical | |||
tcommon = safe_join ({Title, TitleType, Format, Periodical, Scale, Series, Cartography, Others, Publisher, Volume}, sepc); | |||
else -- a sheet or stand-alone map | |||
tcommon = safe_join ({Title, TitleType, Format, Edition, Scale, Series, Cartography, Others, Publisher}, sepc); | |||
end | |||
elseif 'episode' == citation_class then -- special case for cite episode | |||
tcommon = safe_join ({Title, TitleNote, TitleType, Series, Transcript, Edition, Publisher}, sepc); | |||
else -- all other CS1 templates | |||
tcommon = safe_join ({Title, TitleNote, Conference, Periodical, Format, TitleType, Series, Volume, Others, Edition, Publisher, Agency}, sepc); | |||
end | |||
if #ID_list > 0 then | |||
ID_list = safe_join ({sepc .. ' ', table.concat (ID_list, sepc .. ' '), ID}, sepc); | |||
else | |||
ID_list = ID; | |||
end | |||
-- LOCAL | |||
local xDate; | |||
if (is_set (Periodical) and is_set (Date) and | |||
not in_array (citation_class, {'encyclopaedia', 'web'})) | |||
or (in_array (citation_class, {'book', 'news'})) then | |||
if in_array (citation_class, {'journal', 'citation'}) and is_set (Volume) then | |||
xDate = safe_join ({Date .. ',' .. Volume, Insource_location, PublicationDate, OrigYear, AccessDate}, sepc); | |||
else | |||
xDate = safe_join ({Date, Insource_location, PublicationDate, OrigYear, AccessDate}, sepc); | |||
end | |||
Insource_location = '' | |||
else | |||
xDate = safe_join ({Date, PublicationDate, OrigYear, AccessDate}, sepc); | |||
end | |||
xDate = pend_separator (xDate, sepc, true); | |||
-- END LOCAL | |||
local idcommon = safe_join ({URL, xDate, ID_list, Archived, Via, Lay, Language, Quote}, sepc); | |||
local text; | |||
if is_set (Authors) then | |||
if is_set (Contributors) then | |||
text = safe_join ({Contributors, Chapter, tcommon, Authors, Place, Editors, tcommon2, Insource_location, idcommon }, sepc); | |||
else | |||
text = safe_join ({Authors, Chapter, Place, Editors, tcommon, Insource_location, idcommon }, sepc); | |||
end | |||
else | |||
text = safe_join ({Editors, Chapter, Place, tcommon, Insource_location, idcommon}, sepc); | |||
end | |||
if is_set (PostScript) and PostScript ~= sepc then | |||
text = safe_join ({text, sepc}, sepc); --Deals with italics, spaces, etc. | |||
text = text:sub (1, -sepc:len()-1); | |||
end | |||
text = safe_join ({text, PostScript}, sepc); | |||
-- Now enclose the whole thing in a <cite/> element | |||
return format_citation (text, config.CitationClass, Ref, NameList, anchor_year, OCinSoutput, no_tracking_cats); | |||
end | |||
--[[--------------------------< V A L I D A T E >-------------------------------------------------------------- | |||
Looks for a parameter's name in the whitelist. | |||
Parameters in the whitelist can have three values: | |||
true - active, supported parameters | |||
false - deprecated, supported parameters | |||
nil - unsupported parameters | |||
]] | |||
local function validate (name) | |||
local name = tostring (name); | |||
local state = whitelist.basic_arguments[name]; | |||
-- Normal arguments | |||
if true == state then return true; end -- valid actively supported parameter | |||
if false == state then | |||
deprecated_parameter (name); -- parameter is deprecated but still supported | |||
return true; | |||
end | |||
-- Arguments with numbers in them | |||
name = name:gsub ('%d+', '#'); -- replace digit(s) with # (last25 becomes last# | |||
state = whitelist.numbered_arguments[name]; | |||
if true == state then return true; end -- valid actively supported parameter | |||
if false == state then | |||
deprecated_parameter (name); -- parameter is deprecated but still supported | |||
return true; | |||
end | |||
return false; -- Not supported because not found or name is set to nil | |||
end | |||
--[[--------------------------< C I T A T I O N >-------------------------------------------------------------- | |||
This is used by templates such as {{cite book}} to create the actual citation text. | |||
]] | |||
function citation (frame) | |||
local pframe = frame:getParent(); | |||
local module_path = 'Module:Citation/CS1/' | |||
local module_suffix = frame:getTitle():gsub ('^Module:Citation/CS1', ''); | |||
load_modules (module_path, module_suffix); | |||
local args = {}; | |||
local suggestions = {}; | |||
local error_reported = false; | |||
local config = {}; | |||
for k, v in pairs (frame.args) do | |||
config[k] = v; | |||
args[k] = v; | |||
end | |||
local capture; -- the single supported capture when matching unknown parameters using patterns | |||
for k, v in pairs (pframe.args) do | |||
if v ~= '' then | |||
if not validate (k) then | |||
error_reported = false; | |||
if type (k) ~= 'string' then | |||
-- Exclude empty numbered parameters | |||
if v:match ('%S+') ~= nil then | |||
append_error ('text_ignored', {v}); | |||
error_reported = true; | |||
end | |||
elseif validate (k:lower()) then | |||
append_error ('parameter_ignored_suggest', {k, k:lower()}); | |||
error_reported = true; | |||
else | |||
if nil == suggestions.suggestions then -- if this table is nil then we need to load it | |||
suggestions = mw.loadData (module_path .. 'Suggestions' .. module_suffix); | |||
end | |||
for pattern, param in pairs (suggestions.patterns) do -- loop through the patterns to see if we can suggest a proper parameter | |||
capture = k:match (pattern); -- the whole match if no caputre in pattern else the capture if a match | |||
if capture then -- if the pattern matches | |||
param = substitute (param, capture); -- add the capture to the suggested parameter (typically the enumerator) | |||
append_error ('parameter_ignored_suggest', {k, param}); | |||
-- set the error message | |||
error_reported = true; | |||
break; | |||
end | |||
end | |||
if not error_reported then -- couldn't match with a pattern, is there an expicit suggestion? | |||
if suggestions.suggestions[k:lower()] ~= nil then | |||
append_error ('parameter_ignored_suggest', {k, suggestions.suggestions[k:lower()]}); | |||
error_reported = true; | |||
else | |||
append_error ('parameter_ignored', {k}); | |||
error_reported = true; | |||
end | |||
end | |||
end | |||
end | |||
args[k] = v; | |||
elseif args[k] ~= nil or (k == 'postscript') then | |||
args[k] = v; | |||
end | |||
end | |||
local error_msg; | |||
for k, v in pairs (args) do | |||
if 'string' == type (k) then -- don't evaluate positional parameters | |||
error_msg = has_invisible_chars (k, v); | |||
if is_set (error_msg) then | |||
append_error ('invisible_char', error_msg); | |||
end | |||
end | |||
end | |||
return do_citation (config, args) | |||
end | |||
--[[--------------------------< E X P O R T E D F U N C T I O N S >------------------------------------------ | |||
]] | |||
return {citation = citation}; |