|
|
| Line 1: |
Line 1: |
| − | local z = {
| |
| − | error_categories = {};
| |
| − | error_ids = {};
| |
| − | message_tail = {};
| |
| − | }
| |
| | | | |
| − | -- Include translation message hooks, ID and error handling configuration settings.
| |
| − | --local cfg = mw.loadData( 'Module:Citation/CS1/Configuration/sandbox' );
| |
| − |
| |
| − | -- Contains a list of all recognized parameters
| |
| − | --local whitelist = mw.loadData( 'Module:Citation/CS1/Whitelist/sandbox' );
| |
| − |
| |
| − | --local dates = require('Module:Citation/CS1/Date_validation/sandbox').dates -- location of date validation code
| |
| − |
| |
| − | -- Whether variable is set or not
| |
| − | function is_set( var )
| |
| − | return not (var == nil or var == '');
| |
| − | end
| |
| − |
| |
| − | -- First set variable or nil if none
| |
| − | function first_set(...)
| |
| − | local list = {...};
| |
| − | for _, var in pairs(list) do
| |
| − | if is_set( var ) then
| |
| − | return var;
| |
| − | end
| |
| − | end
| |
| − | end
| |
| − |
| |
| − | -- Whether needle is in haystack
| |
| − | function inArray( needle, haystack )
| |
| − | if needle == nil then
| |
| − | return false;
| |
| − | end
| |
| − | for n,v in ipairs( haystack ) do
| |
| − | if v == needle then
| |
| − | return n;
| |
| − | end
| |
| − | end
| |
| − | return false;
| |
| − | end
| |
| − |
| |
| − | --[[
| |
| − | Categorize and emit an error message when the citation contains one or more deprecated parameters. Because deprecated parameters (currently |day=, |month=,
| |
| − | |coauthor=, and |coauthors=) aren't related to each other and because these parameters may be concatenated into the variables used by |date= and |author#= (and aliases)
| |
| − | details of which parameter caused the error message are not provided. Only one error message is emitted regarless of the number of deprecated parameters in the citation.
| |
| − | ]]
| |
| − | function deprecated_parameter()
| |
| − | if true ~= Page_in_deprecated_cat then -- if we haven't been here before then set a
| |
| − | Page_in_deprecated_cat=true; -- sticky flag so that if there are more than one deprecated parameter the category is added only once
| |
| − | -- table.insert( z.message_tail, { seterror( 'deprecated_params', {error_message}, true ) } ); -- add error message
| |
| − | table.insert( z.message_tail, { seterror( 'deprecated_params', {}, true ) } ); -- add error message
| |
| − | end
| |
| − | end
| |
| − |
| |
| − | -- Populates numbered arguments in a message string using an argument table.
| |
| − | function substitute( msg, args )
| |
| − | -- return args and tostring( mw.message.newRawMessage( msg, args ) ) or msg;
| |
| − | return args and mw.message.newRawMessage( msg, args ):plain() or msg;
| |
| − | end
| |
| − |
| |
| − | --[[
| |
| − | Apply kerning to open the space between the quote mark provided by the Module and a leading or trailing quote mark contained in a |title= or |chapter= parameter's value.
| |
| − | This function will positive kern either single or double quotes:
| |
| − | "'Unkerned title with leading and trailing single quote marks'"
| |
| − | " 'Kerned title with leading and trailing single quote marks' " (in real life the kerning isn't as wide as this example)
| |
| − | ]]
| |
| − | function kern_quotes (str)
| |
| − | local left='<span style="padding-left:0.2em;">%1</span>'; -- spacing to use when title contains leading single or double quote mark
| |
| − | local right='<span style="padding-right:0.2em;">%1</span>'; -- spacing to use when title contains trailing single or double quote mark
| |
| − |
| |
| − | if str:match ("^[\"\'][^\']") then
| |
| − | str = string.gsub( str, "^[\"\']", left, 1 ); -- replace (captured) leading single or double quote with left-side <span>
| |
| − | end
| |
| − | if str:match ("[^\'][\"\']$") then
| |
| − | str = string.gsub( str, "[\"\']$", right, 1 ); -- replace (captured) trailing single or double quote with right-side <span>
| |
| − | end
| |
| − | return str;
| |
| − | end
| |
| − |
| |
| − | -- Wraps a string using a message_list configuration taking one argument
| |
| − | function wrap( key, str, lower )
| |
| − | if not is_set( str ) then
| |
| − | return "";
| |
| − | elseif inArray( key, { 'italic-title', 'trans-italic-title' } ) then
| |
| − | str = safeforitalics( str );
| |
| − | end
| |
| − | if lower == true then
| |
| − | return substitute( cfg.messages[key]:lower(), {str} );
| |
| − | else
| |
| − | return substitute( cfg.messages[key], {str} );
| |
| − | end
| |
| − | end
| |
| − |
| |
| − | --[[
| |
| − | 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.
| |
| − | ]]
| |
| − | 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] = selectone( 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;
| |
| − | error( cfg.messages['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
| |
| − |
| |
| − | --[[
| |
| − | 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
| |
| − | ]]
| |
| − | 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 (); -- 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 (); -- parameter is deprecated but still supported
| |
| − | return true;
| |
| − | end
| |
| − |
| |
| − | return false; -- Not supported because not found or name is set to nil
| |
| − | end
| |
| − |
| |
| − | -- Formats a comment for error trapping
| |
| − | function errorcomment( content, hidden )
| |
| − | return wrap( hidden and 'hidden-error' or 'visible-error', content );
| |
| − | end
| |
| − |
| |
| − | --[[
| |
| − | Sets an error condition and returns the appropriate error message. The actual placement
| |
| − | of the error message in the output is the responsibility of the calling function.
| |
| − | ]]
| |
| − | function seterror( error_id, arguments, raw, prefix, suffix )
| |
| − | local error_state = cfg.error_conditions[ error_id ];
| |
| − |
| |
| − | prefix = prefix or "";
| |
| − | suffix = suffix or "";
| |
| − |
| |
| − | if error_state == nil then
| |
| − | error( cfg.messages['undefined_error'] );
| |
| − | elseif is_set( error_state.category ) then
| |
| − | table.insert( z.error_categories, error_state.category );
| |
| − | end
| |
| − |
| |
| − | local message = substitute( error_state.message, arguments );
| |
| − |
| |
| − | message = message .. " ([[" .. cfg.messages['help page link'] ..
| |
| − | "#" .. error_state.anchor .. "|" ..
| |
| − | cfg.messages['help page label'] .. "]])";
| |
| − |
| |
| − | z.error_ids[ error_id ] = true;
| |
| − | if inArray( error_id, { 'bare_url_missing_title', 'trans_missing_title' } )
| |
| − | and z.error_ids['citation_missing_title'] then
| |
| − | return '', false;
| |
| − | end
| |
| − |
| |
| − | message = table.concat({ prefix, message, suffix });
| |
| − |
| |
| − | if raw == true then
| |
| − | return message, error_state.hidden;
| |
| − | end
| |
| − |
| |
| − | return errorcomment( message, error_state.hidden );
| |
| − | end
| |
| − |
| |
| − | -- Formats a wiki style external link
| |
| − | function externallinkid(options)
| |
| − | local url_string = options.id;
| |
| − | if options.encode == true or options.encode == nil then
| |
| − | url_string = mw.uri.encode( url_string );
| |
| − | end
| |
| − | return mw.ustring.format( '[[%s|%s]]%s[%s%s%s %s]',
| |
| − | options.link, options.label, options.separator or " ",
| |
| − | options.prefix, url_string, options.suffix or "",
| |
| − | mw.text.nowiki(options.id)
| |
| − | );
| |
| − | end
| |
| − |
| |
| − | -- Formats a wiki style internal link
| |
| − | function internallinkid(options)
| |
| − | return mw.ustring.format( '[[%s|%s]]%s[[%s%s%s|%s]]',
| |
| − | options.link, options.label, options.separator or " ",
| |
| − | options.prefix, options.id, options.suffix or "",
| |
| − | mw.text.nowiki(options.id)
| |
| − | );
| |
| − | end
| |
| − |
| |
| − | -- Format an external link with error checking
| |
| − | function externallink( URL, label, source )
| |
| − | local error_str = "";
| |
| − | if not is_set( label ) then
| |
| − | label = URL;
| |
| − | if is_set( source ) then
| |
| − | error_str = seterror( 'bare_url_missing_title', { wrap( 'parameter', source ) }, false, " " );
| |
| − | else
| |
| − | error( cfg.messages["bare_url_no_origin"] );
| |
| − | end
| |
| − | end
| |
| − | if not checkurl( URL ) then
| |
| − | error_str = seterror( 'bad_url', {}, false, " " ) .. error_str;
| |
| − | end
| |
| − | return table.concat({ "[", URL, " ", safeforurl( label ), "]", error_str });
| |
| − | end
| |
| − |
| |
| − | -- Formats a link to Amazon
| |
| − | function amazon(id, domain)
| |
| − | if not is_set(domain) then
| |
| − | domain = "com"
| |
| − | elseif ( "jp" == domain or "uk" == domain ) then
| |
| − | domain = "co." .. domain
| |
| − | end
| |
| − | local handler = cfg.id_handlers['ASIN'];
| |
| − | return externallinkid({link = handler.link,
| |
| − | label=handler.label , prefix="//www.amazon."..domain.."/dp/",id=id,
| |
| − | encode=handler.encode, separator = handler.separator})
| |
| − | end
| |
| − |
| |
| − | --[[
| |
| − | Format LCCN link and do simple error checking. LCCN is a character string 8-12 characters long. The length of the LCCN dictates the character type of the first 1-3 characters; the
| |
| − | rightmost eight are always digits. http://info-uri.info/registry/OAIHandler?verb=GetRecord&metadataPrefix=reg&identifier=info:lccn/
| |
| − |
| |
| − | length = 8 then all digits
| |
| − | length = 9 then lccn[1] is alpha
| |
| − | length = 10 then lccn[1] and lccn[2] are both alpha or both digits
| |
| − | length = 11 then lccn[1] is alpha, lccn[2] and lccn[3] are both alpha or both digits
| |
| − | length = 12 then lccn[1] and lccn[2] are both alpha
| |
| − |
| |
| − | ]]
| |
| − | function lccn(id)
| |
| − | local handler = cfg.id_handlers['LCCN'];
| |
| − | local err_cat = ''; -- presume that LCCN is valid
| |
| − |
| |
| − | local len = id:len(); -- get the length of the lccn
| |
| − |
| |
| − | if 8 == len then
| |
| − | if id:match("[^%d]") then -- if LCCN has anything but digits (nil if only digits)
| |
| − | err_cat = ' ' .. seterror( 'bad_lccn' ); -- set an error message
| |
| − | end
| |
| − | elseif 9 == len then -- LCCN should be adddddddd
| |
| − | if nil == id:match("%a%d%d%d%d%d%d%d%d") then -- does it match our pattern?
| |
| − | err_cat = ' ' .. seterror( 'bad_lccn' ); -- set an error message
| |
| − | end
| |
| − | elseif 10 == len then -- LCCN should be aadddddddd or dddddddddd
| |
| − | if id:match("[^%d]") then -- if LCCN has anything but digits (nil if only digits) ...
| |
| − | if nil == id:match("^%a%a%d%d%d%d%d%d%d%d") then -- ... see if it matches our pattern
| |
| − | err_cat = ' ' .. seterror( 'bad_lccn' ); -- no match, set an error message
| |
| − | end
| |
| − | end
| |
| − | elseif 11 == len then -- LCCN should be aaadddddddd or adddddddddd
| |
| − | if not (id:match("^%a%a%a%d%d%d%d%d%d%d%d") or id:match("^%a%d%d%d%d%d%d%d%d%d%d")) then -- see if it matches one of our patterns
| |
| − | err_cat = ' ' .. seterror( 'bad_lccn' ); -- no match, set an error message
| |
| − | end
| |
| − | elseif 12 == len then -- LCCN should be aadddddddddd
| |
| − | if not id:match("^%a%a%d%d%d%d%d%d%d%d%d%d") then -- see if it matches our pattern
| |
| − | err_cat = ' ' .. seterror( 'bad_lccn' ); -- no match, set an error message
| |
| − | end
| |
| − | else
| |
| − | err_cat = ' ' .. seterror( 'bad_lccn' ); -- wrong length, set an error message
| |
| − | end
| |
| − |
| |
| − | return externallinkid({link = handler.link, label = handler.label,
| |
| − | prefix=handler.prefix,id=id,separator=handler.separator, encode=handler.encode}) .. err_cat;
| |
| − | end
| |
| − |
| |
| − | --[[
| |
| − | Format PMID and do simple error checking. PMIDs are sequential numbers beginning at 1 and counting up. This code checks the PMID to see that it
| |
| − | contains only digits and is less than test_limit; the value in local variable test_limit will need to be updated periodically as more PMIDs are issued.
| |
| − | ]]
| |
| − | function pmid(id)
| |
| − | local test_limit = 30000000; -- update this value as PMIDs approach
| |
| − | local handler = cfg.id_handlers['PMID'];
| |
| − | local err_cat = ''; -- presume that PMID is valid
| |
| − |
| |
| − | if id:match("[^%d]") then -- if PMID has anything but digits
| |
| − | err_cat = ' ' .. seterror( 'bad_pmid' ); -- set an error message
| |
| − | else -- PMID is only digits
| |
| − | local id_num = tonumber(id); -- convert id to a number for range testing
| |
| − | if 1 > id_num or test_limit < id_num then -- if PMID is outside test limit boundaries
| |
| − | err_cat = ' ' .. seterror( 'bad_pmid' ); -- set an error message
| |
| − | end
| |
| − | end
| |
| − |
| |
| − | return externallinkid({link = handler.link, label = handler.label,
| |
| − | prefix=handler.prefix,id=id,separator=handler.separator, encode=handler.encode}) .. err_cat;
| |
| − | end
| |
| − |
| |
| − | --[[
| |
| − | Determines if a PMC identifier's online version is embargoed. Compares the date in |embargo= against today's date. If embargo date is
| |
| − | in the future, returns true; otherwse, returns false because the embargo has expired or |embargo= not set in this cite.
| |
| − | ]]
| |
| − | function is_embargoed(embargo)
| |
| − | if is_set(embargo) then
| |
| − | local lang = mw.getContentLanguage();
| |
| − | local good1, embargo_date, good2, todays_date;
| |
| − | good1, embargo_date = pcall( lang.formatDate, lang, 'U', embargo );
| |
| − | good2, todays_date = pcall( lang.formatDate, lang, 'U' );
| |
| − |
| |
| − | if good1 and good2 and tonumber( embargo_date ) >= tonumber( todays_date ) then --is embargo date is in the future?
| |
| − | return true; -- still embargoed
| |
| − | end
| |
| − | end
| |
| − | return false; -- embargo expired or |embargo= not set
| |
| − | end
| |
| − |
| |
| − | --[[
| |
| − | Format a PMC, do simple error checking, and check for embargoed articles.
| |
| − |
| |
| − | The embargo parameter takes a date for a value. If the embargo date is in the future
| |
| − | the PMC identifier will not be linked to the article. If the embargo specifies a date in the past, or if it is empty or omitted, then
| |
| − | the PMC identifier is linked to the article through the link at cfg.id_handlers['PMC'].prefix.
| |
| − |
| |
| − | PMCs are sequential numbers beginning at 1 and counting up. This code checks the PMC to see that it contains only digits and is less
| |
| − | than test_limit; the value in local variable test_limit will need to be updated periodically as more PMCs are issued.
| |
| − | ]]
| |
| − | function pmc(id, embargo)
| |
| − | local test_limit = 5000000; -- update this value as PMCs approach
| |
| − | local handler = cfg.id_handlers['PMC'];
| |
| − | local err_cat = ''; -- presume that PMC is valid
| |
| − |
| |
| − | local text;
| |
| − |
| |
| − | if id:match("[^%d]") then -- if PMC has anything but digits
| |
| − | err_cat = ' ' .. seterror( 'bad_pmc' ); -- set an error message
| |
| − | else -- PMC is only digits
| |
| − | local id_num = tonumber(id); -- convert id to a number for range testing
| |
| − | if 1 > id_num or test_limit < id_num then -- if PMC is outside test limit boundaries
| |
| − | err_cat = ' ' .. seterror( 'bad_pmc' ); -- set an error message
| |
| − | end
| |
| − | end
| |
| − |
| |
| − | if is_embargoed(embargo) then
| |
| − | text="[[" .. handler.link .. "|" .. handler.label .. "]]:" .. handler.separator .. id .. err_cat; --still embargoed so no external link
| |
| − | else
| |
| − | text = externallinkid({link = handler.link, label = handler.label, --no embargo date, ok to link to article
| |
| − | prefix=handler.prefix,id=id,separator=handler.separator, encode=handler.encode}) .. err_cat;
| |
| − | end
| |
| − | return text;
| |
| − | end
| |
| − |
| |
| − | -- Formats a DOI and checks for DOI errors.
| |
| − |
| |
| − | -- DOI names contain two parts: prefix and suffix separated by a forward slash.
| |
| − | -- Prefix: directory indicator '10.' followed by a registrant code
| |
| − | -- Suffix: character string of any length chosen by the registrant
| |
| − |
| |
| − | -- This function checks a DOI name for: prefix/suffix. If the doi name contains spaces or endashes,
| |
| − | -- or, if it ends with a period or a comma, this function will emit a bad_doi error message.
| |
| − |
| |
| − | -- DOI names are case-insensitive and can incorporate any printable Unicode characters so the test for spaces, endash,
| |
| − | -- and terminal punctuation may not be technically correct but it appears, that in practice these characters are rarely if ever used in doi names.
| |
| − |
| |
| − | function doi(id, inactive)
| |
| − | local cat = ""
| |
| − | local handler = cfg.id_handlers['DOI'];
| |
| − |
| |
| − | local text;
| |
| − | if is_set(inactive) then
| |
| − | local inactive_year = inactive:match("%d%d%d%d") or ''; -- try to get the year portion from the inactive date
| |
| − | text = "[[" .. handler.link .. "|" .. handler.label .. "]]:" .. id;
| |
| − | if is_set(inactive_year) then
| |
| − | table.insert( z.error_categories, "Pages with DOIs inactive since " .. inactive_year );
| |
| − | else
| |
| − | table.insert( z.error_categories, "Pages with inactive DOIs" ); -- when inactive doesn't contain a recognizable year
| |
| − | end
| |
| − | inactive = " (" .. cfg.messages['inactive'] .. " " .. inactive .. ")"
| |
| − | else
| |
| − | text = externallinkid({link = handler.link, label = handler.label,
| |
| − | prefix=handler.prefix,id=id,separator=handler.separator, encode=handler.encode})
| |
| − | inactive = ""
| |
| − | end
| |
| − |
| |
| − | if nil == id:match("^10%.[^%s–]-/[^%s–]-[^%.,]$") then -- doi must begin with '10.', must contain a fwd slash, must not contain spaces or endashes, and must not end with period or comma
| |
| − | cat = ' ' .. seterror( 'bad_doi' );
| |
| − | end
| |
| − | return text .. inactive .. cat
| |
| − | end
| |
| − |
| |
| − | -- Formats an OpenLibrary link, and checks for associated errors.
| |
| − | function openlibrary(id)
| |
| − | local code = id:sub(-1,-1)
| |
| − | local handler = cfg.id_handlers['OL'];
| |
| − | if ( code == "A" ) then
| |
| − | return externallinkid({link=handler.link, label=handler.label,
| |
| − | prefix="http://openlibrary.org/authors/OL",id=id, separator=handler.separator,
| |
| − | encode = handler.encode})
| |
| − | elseif ( code == "M" ) then
| |
| − | return externallinkid({link=handler.link, label=handler.label,
| |
| − | prefix="http://openlibrary.org/books/OL",id=id, separator=handler.separator,
| |
| − | encode = handler.encode})
| |
| − | elseif ( code == "W" ) then
| |
| − | return externallinkid({link=handler.link, label=handler.label,
| |
| − | prefix= "http://openlibrary.org/works/OL",id=id, separator=handler.separator,
| |
| − | encode = handler.encode})
| |
| − | else
| |
| − | return externallinkid({link=handler.link, label=handler.label,
| |
| − | prefix= "http://openlibrary.org/OL",id=id, separator=handler.separator,
| |
| − | encode = handler.encode}) ..
| |
| − | ' ' .. seterror( 'bad_ol' );
| |
| − | end
| |
| − | end
| |
| − |
| |
| − | --[[
| |
| − | Validate and format an issn. This code fixes the case where an editor has included an ISSN in the citation but has separated the two groups of four
| |
| − | digits with a space. When that condition occurred, the resulting link looked like this:
| |
| − |
| |
| − | |issn=0819 4327 gives: [http://www.worldcat.org/issn/0819 4327 0819 4327] -- can't have spaces in an external link
| |
| − |
| |
| − | This code now prevents that by inserting a hyphen at the issn midpoint. It also validates the issn for length and makes sure that the checkdigit agrees
| |
| − | with the calculated value. Incorrect length (8 digits), characters other than 0-9 and X, or checkdigit / calculated value mismatch will all cause a check issn
| |
| − | error message. The issn is always displayed with a hyphen, even if the issn was given as a single group of 8 digits.
| |
| − | ]]
| |
| − | function issn(id)
| |
| − | local issn_copy = id; -- save a copy of unadulterated issn; use this version for display if issn does not validate
| |
| − | local handler = cfg.id_handlers['ISSN'];
| |
| − | local text;
| |
| − | local valid_issn = true;
| |
| − |
| |
| − | id=id:gsub( "[%s-–]", "" ); -- strip spaces, hyphens, and ndashes from the issn
| |
| − |
| |
| − | if 8 ~= id:len() or nil == id:match( "^%d*X?$" ) then -- validate the issn: 8 didgits long, containing only 0-9 or X in the last position
| |
| − | valid_issn=false; -- wrong length or improper character
| |
| − | else
| |
| − | valid_issn=is_valid_isxn(id, 8); -- validate issn
| |
| − | end
| |
| − |
| |
| − | if true == valid_issn then
| |
| − | id = string.sub( id, 1, 4 ) .. "-" .. string.sub( id, 5 ); -- if valid, display correctly formatted version
| |
| − | else
| |
| − | id = issn_copy; -- if not valid, use the show the invalid issn with error message
| |
| − | end
| |
| − |
| |
| − | text = externallinkid({link = handler.link, label = handler.label,
| |
| − | prefix=handler.prefix,id=id,separator=handler.separator, encode=handler.encode})
| |
| − |
| |
| − | if false == valid_issn then
| |
| − | text = text .. ' ' .. seterror( 'bad_issn' ) -- add an error message if the issn is invalid
| |
| − | end
| |
| − |
| |
| − | return text
| |
| − | end
| |
| − |
| |
| − | --[[
| |
| − | This function sets default title types (equivalent to the citation including |type=<default value>) for those citations that have defaults.
| |
| − | Also handles the special case where it is desireable to omit the title type from the rendered citation (|type=none).
| |
| − | ]]
| |
| − | 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
| |
| − |
| |
| − | if "AV media notes" == cite_class or "DVD notes" == cite_class then -- if this citation is cite AV media notes or cite DVD notes
| |
| − | return "Media notes"; -- display AV media notes / DVD media notes annotation
| |
| − |
| |
| − | elseif "podcast" == cite_class then -- if this citation is cite podcast
| |
| − | return "Podcast"; -- display podcast annotation
| |
| − |
| |
| − | elseif "pressrelease" == cite_class then -- if this citation is cite press release
| |
| − | return "Press release"; -- display press release annotation
| |
| − |
| |
| − | elseif "techreport" == cite_class then -- if this citation is cite techreport
| |
| − | return "Technical report"; -- display techreport annotation
| |
| − |
| |
| − | elseif "thesis" == cite_class then -- if this citation is cite thesis (degree option handled after this function returns)
| |
| − | return "Thesis"; -- display simple thesis annotation (without |degree= modification)
| |
| − | end
| |
| − | end
| |
| − |
| |
| − | --[[
| |
| − | Determines whether a URL string is valid
| |
| − |
| |
| − | At present the only check is whether the string appears to
| |
| − | be prefixed with a URI scheme. It is not determined whether
| |
| − | the URI scheme is valid or whether the URL is otherwise well
| |
| − | formed.
| |
| − | ]]
| |
| − | function checkurl( url_str )
| |
| − | -- Protocol-relative or URL scheme
| |
| − | return url_str:sub(1,2) == "//" or url_str:match( "^[^/]*:" ) ~= nil;
| |
| − | end
| |
| − |
| |
| − | -- Removes irrelevant text and dashes from ISBN number
| |
| − | -- Similar to that used for Special:BookSources
| |
| − | function cleanisbn( isbn_str )
| |
| − | return isbn_str:gsub( "[^-0-9X]", "" );
| |
| − | end
| |
| − |
| |
| − | -- Extract page numbers from external wikilinks in any of the |page=, |pages=, or |at= parameters for use in COinS.
| |
| − | function get_coins_pages (pages)
| |
| − | if not is_set (pages) then return pages; end -- if no page numbers then we're done
| |
| − |
| |
| − | while true do
| |
| − | pattern = pages:match("%[([%w/:\.]+%s+)[%w%d].*%]"); -- pattern is the opening bracket, the url and following space(s): "[url "
| |
| − | if nil == pattern then break; end -- no more urls
| |
| − | pages = pages:gsub(pattern, ""); -- remove as many instances of pattern as possible
| |
| − | end
| |
| − | pages = pages:gsub("[%[%]]", ""); -- remove the brackets
| |
| − | pages = pages:gsub("–", "-" ); -- replace endashes with hyphens
| |
| − | pages = pages:gsub("&%w+;", "-" ); -- and replace html entities (– etc) with hyphens; do we need to replace numerical entities like   and the like?
| |
| − | return pages;
| |
| − | end
| |
| − |
| |
| − | --[[
| |
| − | ISBN-10 and ISSN validator code calculates checksum across all isbn/issn digits including the check digit. ISBN-13 is checked in checkisbn().
| |
| − | If the number is valid the result will be 0. Before calling this function, issbn/issn must be checked for length and stripped of dashes,
| |
| − | spaces and other non-isxn characters.
| |
| − | ]]
| |
| − | function is_valid_isxn (isxn_str, len)
| |
| − | local temp = 0;
| |
| − | isxn_str = { isxn_str:byte(1, len) }; -- make a table of bytes
| |
| − | len = len+1; -- adjust to be a loop counter
| |
| − | for i, v in ipairs( isxn_str ) do -- loop through all of the bytes and calculate the checksum
| |
| − | if v == string.byte( "X" ) then -- if checkdigit is X
| |
| − | temp = temp + 10*( len - i ); -- it represents 10 decimal
| |
| − | else
| |
| − | temp = temp + tonumber( string.char(v) )*(len-i);
| |
| − | end
| |
| − | end
| |
| − | return temp % 11 == 0; -- returns true if calculation result is zero
| |
| − | end
| |
| − |
| |
| − | -- Determines whether an ISBN string is valid
| |
| − | function checkisbn( isbn_str )
| |
| − | if nil ~= isbn_str:match("[^%s-0-9X]") then return false; end -- fail if isbn_str contains anything but digits, hyphens, or the uppercase X
| |
| − | isbn_str = isbn_str:gsub( "-", "" ):gsub( " ", "" ); -- remove hyphens and spaces
| |
| − | local len = isbn_str:len();
| |
| − |
| |
| − | if len ~= 10 and len ~= 13 then
| |
| − | return false;
| |
| − | end
| |
| − |
| |
| − | if len == 10 then
| |
| − | if isbn_str:match( "^%d*X?$" ) == nil then return false; end
| |
| − | return is_valid_isxn(isbn_str, 10);
| |
| − | else
| |
| − | local temp = 0;
| |
| − | if isbn_str:match( "^97[89]%d*$" ) == nil then return false; end -- isbn13 begins with 978 or 979
| |
| − | isbn_str = { isbn_str:byte(1, len) };
| |
| − | for i, v in ipairs( isbn_str ) do
| |
| − | temp = temp + (3 - 2*(i % 2)) * tonumber( string.char(v) );
| |
| − | end
| |
| − | return temp % 10 == 0;
| |
| − | end
| |
| − | end
| |
| − |
| |
| − | -- Gets the display text for a wikilink like [[A|B]] or [[B]] gives B
| |
| − | function removewikilink( str )
| |
| − | return (str:gsub( "%[%[([^%[%]]*)%]%]", function(l)
| |
| − | return l:gsub( "^[^|]*|(.*)$", "%1" ):gsub("^%s*(.-)%s*$", "%1");
| |
| − | end));
| |
| − | end
| |
| − |
| |
| − | -- Escape sequences for content that will be used for URL descriptions
| |
| − | function safeforurl( str )
| |
| − | if str:match( "%[%[.-%]%]" ) ~= nil then
| |
| − | table.insert( z.message_tail, { seterror( 'wikilink_in_url', {}, true ) } );
| |
| − | end
| |
| − |
| |
| − | return str:gsub( '[%[%]\n]', {
| |
| − | ['['] = '[',
| |
| − | [']'] = ']',
| |
| − | ['\n'] = ' ' } );
| |
| − | end
| |
| − |
| |
| − | -- Converts a hyphen to a dash
| |
| − | function hyphentodash( str )
| |
| − | if not is_set(str) or str:match( "[%[%]{}<>]" ) ~= nil then
| |
| − | return str;
| |
| − | end
| |
| − | return str:gsub( '-', '–' );
| |
| − | end
| |
| − |
| |
| − | -- Protects a string that will be wrapped in wiki italic markup '' ... ''
| |
| − | function safeforitalics( str )
| |
| − | --[[ Note: We can not use <i> for italics, as the expected behavior for
| |
| − | italics specified by ''...'' in the title is that they will be inverted
| |
| − | (i.e. unitalicized) in the resulting references. In addition, <i> and ''
| |
| − | tend to interact poorly under Mediawiki's HTML tidy. ]]
| |
| − |
| |
| − | if not is_set(str) then
| |
| − | return str;
| |
| − | else
| |
| − | if str:sub(1,1) == "'" then str = "<span />" .. str; end
| |
| − | if str:sub(-1,-1) == "'" then str = str .. "<span />"; end
| |
| − |
| |
| − | -- Remove newlines as they break italics.
| |
| − | return str:gsub( '\n', ' ' );
| |
| − | end
| |
| − | end
| |
| − |
| |
| − | --[[
| |
| − | Joins a sequence of strings together while checking for duplicate separation
| |
| − | characters.
| |
| − | ]]
| |
| − | function safejoin( tbl, duplicate_char )
| |
| − | --[[
| |
| − | Note: we use string functions here, rather than ustring functions.
| |
| − |
| |
| − | This has considerably faster performance and should work correctly as
| |
| − | long as the duplicate_char is strict ASCII. The strings
| |
| − | in tbl may be ASCII or UTF8.
| |
| − | ]]
| |
| − |
| |
| − | local str = '';
| |
| − | local comp = '';
| |
| − | local end_chr = '';
| |
| − | local trim;
| |
| − | for _, value in ipairs( tbl ) do
| |
| − | if value == nil then value = ''; end
| |
| − |
| |
| − | if str == '' then
| |
| − | str = value;
| |
| − | elseif value ~= '' then
| |
| − | if value:sub(1,1) == '<' then
| |
| − | -- Special case of values enclosed in spans and other markup.
| |
| − | comp = value:gsub( "%b<>", "" );
| |
| − | else
| |
| − | comp = value;
| |
| − | end
| |
| − |
| |
| − | if comp:sub(1,1) == duplicate_char then
| |
| − | trim = false;
| |
| − | end_chr = str:sub(-1,-1);
| |
| − | -- str = str .. "<HERE(enchr=" .. end_chr.. ")"
| |
| − | if end_chr == duplicate_char then
| |
| − | str = str:sub(1,-2);
| |
| − | elseif end_chr == "'" then
| |
| − | if str:sub(-3,-1) == duplicate_char .. "''" then
| |
| − | str = str:sub(1, -4) .. "''";
| |
| − | elseif str:sub(-5,-1) == duplicate_char .. "]]''" then
| |
| − | trim = true;
| |
| − | elseif str:sub(-4,-1) == duplicate_char .. "]''" then
| |
| − | trim = true;
| |
| − | end
| |
| − | elseif end_chr == "]" then
| |
| − | if str:sub(-3,-1) == duplicate_char .. "]]" then
| |
| − | trim = true;
| |
| − | elseif str:sub(-2,-1) == duplicate_char .. "]" then
| |
| − | trim = true;
| |
| − | end
| |
| − | elseif end_chr == " " then
| |
| − | if str:sub(-2,-1) == duplicate_char .. " " then
| |
| − | str = str:sub(1,-3);
| |
| − | end
| |
| − | end
| |
| − |
| |
| − | if trim then
| |
| − | if value ~= comp then
| |
| − | local dup2 = duplicate_char;
| |
| − | if dup2:match( "%A" ) then dup2 = "%" .. dup2; end
| |
| − |
| |
| − | value = value:gsub( "(%b<>)" .. dup2, "%1", 1 )
| |
| − | else
| |
| − | value = value:sub( 2, -1 );
| |
| − | end
| |
| − | end
| |
| − | end
| |
| − | str = str .. value;
| |
| − | end
| |
| − | end
| |
| − | return str;
| |
| − | end
| |
| − |
| |
| − | -- Attempts to convert names to initials.
| |
| − | function reducetoinitials(first)
| |
| − | local initials = {}
| |
| − | for word in string.gmatch(first, "%S+") do
| |
| − | table.insert(initials, string.sub(word,1,1)) -- Vancouver format does not include full stops.
| |
| − | end
| |
| − | return table.concat(initials) -- Vancouver format does not include spaces.
| |
| − | end
| |
| − |
| |
| − | -- Formats a list of people (e.g. authors / editors)
| |
| − | function listpeople(control, people)
| |
| − | local sep = control.sep;
| |
| − | local namesep = control.namesep
| |
| − | local format = control.format
| |
| − | local maximum = control.maximum
| |
| − | local lastauthoramp = control.lastauthoramp;
| |
| − | local text = {}
| |
| − | local etal = false;
| |
| − |
| |
| − | if sep:sub(-1,-1) ~= " " then sep = sep .. " " end
| |
| − | if maximum ~= nil and maximum < 1 then return "", 0; end
| |
| − |
| |
| − | for i,person in ipairs(people) do
| |
| − | if is_set(person.last) then
| |
| − | local mask = person.mask
| |
| − | local one
| |
| − | local sep_one = sep;
| |
| − | if maximum ~= nil and i > maximum then
| |
| − | etal = true;
| |
| − | break;
| |
| − | elseif (mask ~= nil) then
| |
| − | local n = tonumber(mask)
| |
| − | if (n ~= nil) then
| |
| − | one = string.rep("—",n)
| |
| − | else
| |
| − | one = mask;
| |
| − | sep_one = " ";
| |
| − | end
| |
| − | else
| |
| − | one = person.last
| |
| − | local first = person.first
| |
| − | if is_set(first) then
| |
| − | if ( "vanc" == format ) then first = reducetoinitials(first) end
| |
| − | one = one .. namesep .. first
| |
| − | end
| |
| − | if is_set(person.link) then one = "[[" .. person.link .. "|" .. one .. "]]" end
| |
| − | if is_set(person.link) and nil ~= person.link:find("//") then one = one .. " " .. seterror( 'bad_authorlink' ) end -- check for url in author link;
| |
| − | end
| |
| − | table.insert( text, one )
| |
| − | table.insert( text, sep_one )
| |
| − | end
| |
| − | end
| |
| − |
| |
| − | local count = #text / 2;
| |
| − | if count > 0 then
| |
| − | if count > 1 and is_set(lastauthoramp) and not etal then
| |
| − | text[#text-2] = " & ";
| |
| − | end
| |
| − | text[#text] = nil;
| |
| − | end
| |
| − |
| |
| − | local result = table.concat(text) -- construct list
| |
| − | if etal then
| |
| − | local etal_text = cfg.messages['et al'];
| |
| − | result = result .. " " .. etal_text;
| |
| − | end
| |
| − |
| |
| − | -- if necessary wrap result in <span> tag to format in Small Caps
| |
| − | if ( "scap" == format ) then result =
| |
| − | '<span class="smallcaps" style="font-variant:small-caps">' .. result .. '</span>';
| |
| − | end
| |
| − | return result, count
| |
| − | end
| |
| − |
| |
| − | -- Generates a CITEREF anchor ID.
| |
| − | function anchorid( options )
| |
| − | return "CITEREF" .. table.concat( options );
| |
| − | end
| |
| − |
| |
| − | -- Gets name list from the input arguments
| |
| − | function extractnames(args, list_name)
| |
| − | local names = {};
| |
| − | local i = 1;
| |
| − | local last;
| |
| − |
| |
| − | while true do
| |
| − | last = selectone( args, cfg.aliases[list_name .. '-Last'], 'redundant_parameters', i );
| |
| − | if not is_set(last) then
| |
| − | -- just in case someone passed in an empty parameter
| |
| − | break;
| |
| − | end
| |
| − | names[i] = {
| |
| − | last = last,
| |
| − | first = selectone( args, cfg.aliases[list_name .. '-First'], 'redundant_parameters', i ),
| |
| − | link = selectone( args, cfg.aliases[list_name .. '-Link'], 'redundant_parameters', i ),
| |
| − | mask = selectone( args, cfg.aliases[list_name .. '-Mask'], 'redundant_parameters', i )
| |
| − | };
| |
| − | i = i + 1;
| |
| − | end
| |
| − | return names;
| |
| − | end
| |
| − |
| |
| − | -- Populates ID table from arguments using configuration settings
| |
| − | function extractids( args )
| |
| − | local id_list = {};
| |
| − | for k, v in pairs( cfg.id_handlers ) do
| |
| − | v = selectone( args, v.parameters, 'redundant_parameters' );
| |
| − | if is_set(v) then id_list[k] = v; end
| |
| − | end
| |
| − | return id_list;
| |
| − | end
| |
| − |
| |
| − | -- Takes a table of IDs and turns it into a table of formatted ID outputs.
| |
| − | function buildidlist( id_list, options )
| |
| − | local new_list, handler = {};
| |
| − |
| |
| − | function fallback(k) return { __index = function(t,i) return cfg.id_handlers[k][i] end } end;
| |
| − |
| |
| − | for k, v in pairs( id_list ) do
| |
| − | -- fallback to read-only cfg
| |
| − | handler = setmetatable( { ['id'] = v }, fallback(k) );
| |
| − |
| |
| − | if handler.mode == 'external' then
| |
| − | table.insert( new_list, {handler.label, externallinkid( handler ) } );
| |
| − | elseif handler.mode == 'internal' then
| |
| − | table.insert( new_list, {handler.label, internallinkid( handler ) } );
| |
| − | elseif handler.mode ~= 'manual' then
| |
| − | error( cfg.messages['unknown_ID_mode'] );
| |
| − | elseif k == 'DOI' then
| |
| − | table.insert( new_list, {handler.label, doi( v, options.DoiBroken ) } );
| |
| − | elseif k == 'ASIN' then
| |
| − | table.insert( new_list, {handler.label, amazon( v, options.ASINTLD ) } );
| |
| − | elseif k == 'LCCN' then
| |
| − | table.insert( new_list, {handler.label, lccn( v ) } );
| |
| − | elseif k == 'OL' then
| |
| − | table.insert( new_list, {handler.label, openlibrary( v ) } );
| |
| − | elseif k == 'PMC' then
| |
| − | table.insert( new_list, {handler.label, pmc( v, options.Embargo ) } );
| |
| − | elseif k == 'PMID' then
| |
| − | table.insert( new_list, {handler.label, pmid( v ) } );
| |
| − | elseif k == 'ISSN' then
| |
| − | table.insert( new_list, {handler.label, issn( v ) } );
| |
| − | elseif k == 'ISBN' then
| |
| − | local ISBN = internallinkid( handler );
| |
| − | if not checkisbn( v ) and not is_set(options.IgnoreISBN) then
| |
| − | ISBN = ISBN .. seterror( 'bad_isbn', {}, false, " ", "" );
| |
| − | end
| |
| − | table.insert( new_list, {handler.label, ISBN } );
| |
| − | else
| |
| − | error( cfg.messages['unknown_manual_ID'] );
| |
| − | end
| |
| − | end
| |
| − |
| |
| − | function comp( a, b ) -- used in following table.sort()
| |
| − | return a[1] < b[1];
| |
| − | end
| |
| − |
| |
| − | table.sort( new_list, comp );
| |
| − | for k, v in ipairs( new_list ) do
| |
| − | new_list[k] = v[2];
| |
| − | end
| |
| − |
| |
| − | return new_list;
| |
| − | end
| |
| − |
| |
| − | -- Chooses one matching parameter from a list of parameters to consider
| |
| − | -- Generates an error if more than one match is present.
| |
| − | function selectone( args, possible, error_condition, index )
| |
| − | local value = nil;
| |
| − | local selected = '';
| |
| − | local error_list = {};
| |
| − |
| |
| − | if index ~= nil then index = tostring(index); end
| |
| − |
| |
| − | -- Handle special case of "#" replaced by empty string
| |
| − | if index == '1' then
| |
| − | for _, v in ipairs( possible ) do
| |
| − | v = v:gsub( "#", "" );
| |
| − | if is_set(args[v]) then
| |
| − | if value ~= nil and selected ~= v then
| |
| − | table.insert( error_list, v );
| |
| − | else
| |
| − | value = args[v];
| |
| − | selected = v;
| |
| − | end
| |
| − | end
| |
| − | end
| |
| − | end
| |
| − |
| |
| − | for _, v in ipairs( possible ) do
| |
| − | if index ~= nil then
| |
| − | v = v:gsub( "#", index );
| |
| − | end
| |
| − | if is_set(args[v]) then
| |
| − | if value ~= nil and selected ~= v then
| |
| − | table.insert( error_list, v );
| |
| − | else
| |
| − | value = args[v];
| |
| − | selected = v;
| |
| − | end
| |
| − | end
| |
| − | end
| |
| − |
| |
| − | if #error_list > 0 then
| |
| − | local error_str = "";
| |
| − | for _, k in ipairs( error_list ) do
| |
| − | if error_str ~= "" then error_str = error_str .. cfg.messages['parameter-separator'] end
| |
| − | error_str = error_str .. wrap( 'parameter', k );
| |
| − | end
| |
| − | if #error_list > 1 then
| |
| − | error_str = error_str .. cfg.messages['parameter-final-separator'];
| |
| − | else
| |
| − | error_str = error_str .. cfg.messages['parameter-pair-separator'];
| |
| − | end
| |
| − | error_str = error_str .. wrap( 'parameter', selected );
| |
| − | table.insert( z.message_tail, { seterror( error_condition, {error_str}, true ) } );
| |
| − | end
| |
| − |
| |
| − | return value, selected;
| |
| − | end
| |
| − |
| |
| − | -- COinS metadata (see <http://ocoins.info/>) allows automated tools to parse
| |
| − | -- the citation information.
| |
| − | function COinS(data)
| |
| − | if 'table' ~= type(data) or nil == next(data) then
| |
| − | return '';
| |
| − | end
| |
| − |
| |
| − | local ctx_ver = "Z39.88-2004";
| |
| − |
| |
| − | -- treat table strictly as an array with only set values.
| |
| − | local OCinSoutput = setmetatable( {}, {
| |
| − | __newindex = function(self, key, value)
| |
| − | if is_set(value) then
| |
| − | rawset( self, #self+1, table.concat{ key, '=', mw.uri.encode( removewikilink( value ) ) } );
| |
| − | end
| |
| − | end
| |
| − | });
| |
| − |
| |
| − | if is_set(data.Chapter) then
| |
| − | OCinSoutput.rft_val_fmt = "info:ofi/fmt:kev:mtx:book";
| |
| − | OCinSoutput["rft.genre"] = "bookitem";
| |
| − | OCinSoutput["rft.btitle"] = data.Chapter;
| |
| − | OCinSoutput["rft.atitle"] = data.Title;
| |
| − | elseif is_set(data.Periodical) then
| |
| − | OCinSoutput.rft_val_fmt = "info:ofi/fmt:kev:mtx:journal";
| |
| − | OCinSoutput["rft.genre"] = "article";
| |
| − | OCinSoutput["rft.jtitle"] = data.Periodical;
| |
| − | OCinSoutput["rft.atitle"] = data.Title;
| |
| − | else
| |
| − | OCinSoutput.rft_val_fmt = "info:ofi/fmt:kev:mtx:book";
| |
| − | OCinSoutput["rft.genre"] = "book"
| |
| − | OCinSoutput["rft.btitle"] = data.Title;
| |
| − | end
| |
| − |
| |
| − | OCinSoutput["rft.place"] = data.PublicationPlace;
| |
| − | OCinSoutput["rft.date"] = data.Date;
| |
| − | OCinSoutput["rft.series"] = data.Series;
| |
| − | OCinSoutput["rft.volume"] = data.Volume;
| |
| − | OCinSoutput["rft.issue"] = data.Issue;
| |
| − | OCinSoutput["rft.pages"] = data.Pages;
| |
| − | OCinSoutput["rft.edition"] = data.Edition;
| |
| − | OCinSoutput["rft.pub"] = data.PublisherName;
| |
| − |
| |
| − | for k, v in pairs( data.ID_list ) do
| |
| − | local id, value = cfg.id_handlers[k].COinS;
| |
| − | if k == 'ISBN' then value = cleanisbn( v ); else value = v; end
| |
| − | if string.sub( id or "", 1, 4 ) == 'info' then
| |
| − | OCinSoutput["rft_id"] = table.concat{ id, "/", v };
| |
| − | else
| |
| − | OCinSoutput[ id ] = value;
| |
| − | end
| |
| − | end
| |
| − |
| |
| − | local last, first;
| |
| − | for k, v in ipairs( data.Authors ) do
| |
| − | last, first = v.last, v.first;
| |
| − | if k == 1 then
| |
| − | if is_set(last) then
| |
| − | OCinSoutput["rft.aulast"] = last;
| |
| − | end
| |
| − | if is_set(first) then
| |
| − | OCinSoutput["rft.aufirst"] = first;
| |
| − | end
| |
| − | end
| |
| − | if is_set(last) and is_set(first) then
| |
| − | OCinSoutput["rft.au"] = table.concat{ last, ", ", first };
| |
| − | elseif is_set(last) then
| |
| − | OCinSoutput["rft.au"] = last;
| |
| − | end
| |
| − | end
| |
| − |
| |
| − | OCinSoutput.rft_id = data.URL;
| |
| − | OCinSoutput.rfr_id = table.concat{ "info:sid/", mw.site.server:match( "[^/]*$" ), ":", data.RawPage };
| |
| − | OCinSoutput = setmetatable( OCinSoutput, nil );
| |
| − |
| |
| − | -- sort with version string always first, and combine.
| |
| − | table.sort( OCinSoutput );
| |
| − | table.insert( OCinSoutput, 1, "ctx_ver=" .. ctx_ver ); -- such as "Z39.88-2004"
| |
| − | return table.concat(OCinSoutput, "&");
| |
| − | end
| |
| − |
| |
| − | --[[
| |
| − | This is the main function doing the majority of the citation
| |
| − | formatting.
| |
| − | ]]
| |
| − | function citation0( config, args)
| |
| − | --[[
| |
| − | Load Input Parameters
| |
| − | The argment_wrapper facillitates the mapping of multiple
| |
| − | aliases to single internal variable.
| |
| − | ]]
| |
| − | local A = argument_wrapper( args );
| |
| − |
| |
| − | local i
| |
| − | local PPrefix = A['PPrefix']
| |
| − | local PPPrefix = A['PPPrefix']
| |
| − | if is_set( A['NoPP'] ) then PPPrefix = "" PPrefix = "" end
| |
| − |
| |
| − | -- Pick out the relevant fields from the arguments. Different citation templates
| |
| − | -- define different field names for the same underlying things.
| |
| − | local Authors = A['Authors'];
| |
| − | local a = extractnames( args, 'AuthorList' );
| |
| − |
| |
| − | local Coauthors = A['Coauthors'];
| |
| − | local Others = A['Others'];
| |
| − | local Editors = A['Editors'];
| |
| − | local e = extractnames( args, 'EditorList' );
| |
| − |
| |
| − | local Year = A['Year'];
| |
| − | local PublicationDate = A['PublicationDate'];
| |
| − | local OrigYear = A['OrigYear'];
| |
| − | local Date = A['Date'];
| |
| − | local LayDate = A['LayDate'];
| |
| − | ------------------------------------------------- Get title data
| |
| − | local Title = A['Title'];
| |
| − | local BookTitle = A['BookTitle'];
| |
| − | local Conference = A['Conference'];
| |
| − | local TransTitle = A['TransTitle'];
| |
| − | local TitleNote = A['TitleNote'];
| |
| − | local TitleLink = A['TitleLink'];
| |
| − | local Chapter = A['Chapter'];
| |
| − | local ChapterLink = A['ChapterLink'];
| |
| − | local TransChapter = A['TransChapter'];
| |
| − | local TitleType = A['TitleType'];
| |
| − | local Degree = A['Degree'];
| |
| − | local Docket = A['Docket'];
| |
| − | local ArchiveURL = A['ArchiveURL'];
| |
| − | local URL = A['URL']
| |
| − | local URLorigin = A:ORIGIN('URL');
| |
| − | local ChapterURL = A['ChapterURL'];
| |
| − | local ChapterURLorigin = A:ORIGIN('ChapterURL');
| |
| − | local ConferenceURL = A['ConferenceURL'];
| |
| − | local ConferenceURLorigin = A:ORIGIN('ConferenceURL');
| |
| − | local Periodical = A['Periodical'];
| |
| − |
| |
| − | local Series = A['Series'];
| |
| − | local Volume = A['Volume'];
| |
| − | local Issue = A['Issue'];
| |
| − | local Position = '';
| |
| − | local Page = A['Page'];
| |
| − | local Pages = hyphentodash( A['Pages'] );
| |
| − | local At = A['At'];
| |
| − |
| |
| − | local Edition = A['Edition'];
| |
| − | local PublicationPlace = A['PublicationPlace']
| |
| − | local Place = A['Place'];
| |
| − |
| |
| − | local PublisherName = A['PublisherName'];
| |
| − | local RegistrationRequired = A['RegistrationRequired'];
| |
| − | local SubscriptionRequired = A['SubscriptionRequired'];
| |
| − | local Via = A['Via'];
| |
| − | local AccessDate = A['AccessDate'];
| |
| − | local ArchiveDate = A['ArchiveDate'];
| |
| − | local Agency = A['Agency'];
| |
| − | local DeadURL = A['DeadURL']
| |
| − | local Language = A['Language'];
| |
| − | local Format = A['Format'];
| |
| − | local Ref = A['Ref'];
| |
| − | local DoiBroken = A['DoiBroken'];
| |
| − | local ID = A['ID'];
| |
| − | local ASINTLD = A['ASINTLD'];
| |
| − | local IgnoreISBN = A['IgnoreISBN'];
| |
| − | local Embargo = A['Embargo'];
| |
| − |
| |
| − | local ID_list = extractids( args );
| |
| − |
| |
| − | local Quote = A['Quote'];
| |
| − | local PostScript = A['PostScript'];
| |
| − |
| |
| − | local LayURL = A['LayURL'];
| |
| − | local LaySource = A['LaySource'];
| |
| − | local Transcript = A['Transcript'];
| |
| − | local TranscriptURL = A['TranscriptURL']
| |
| − | local TranscriptURLorigin = A:ORIGIN('TranscriptURL');
| |
| − | local sepc = A['Separator'];
| |
| − |
| |
| − | local LastAuthorAmp = A['LastAuthorAmp'];
| |
| − | local no_tracking_cats = A['NoTracking'];
| |
| − |
| |
| − | --these are used by cite interview
| |
| − | local Callsign = A['Callsign'];
| |
| − | local City = A['City'];
| |
| − | local Cointerviewers = A['Cointerviewers']; -- deprecated
| |
| − | local Interviewer = A['Interviewer']; -- deprecated
| |
| − | local Program = A['Program'];
| |
| − |
| |
| − | --local variables that are not cs1 parameters
| |
| − | local page_type; -- is this needed? Doesn't appear to be used anywhere;
| |
| − | local use_lowercase = ( sepc ~= '.' );
| |
| − | local this_page = mw.title.getCurrentTitle(); --Also used for COinS and for language
| |
| − | local anchor_year; -- used in the CITEREF identifier
| |
| − | local COinS_date; -- used in the COinS metadata
| |
| − |
| |
| − | -- Set postscript default.
| |
| − | if not is_set (PostScript) then -- if |postscript= has not been set (Postscript is nil which is the default for {{citation}}) and
| |
| − | if (config.CitationClass ~= "citation") then -- this template is not a citation template
| |
| − | PostScript = '.'; -- must be a cite xxx template so set postscript to default (period)
| |
| − | end
| |
| − | else
| |
| − | if PostScript:lower() == 'none' then -- if |postscript=none then
| |
| − | PostScript = ''; -- no postscript
| |
| − | end
| |
| − | end
| |
| − |
| |
| − | --check this page to see if it is in one of the namespaces that cs1 is not supposed to add to the error categories.
| |
| − | if not is_set(no_tracking_cats) then -- ignore if we are already not going to categorize this page
| |
| − | for k, v in pairs( cfg.uncategorized_namespaces ) do -- otherwise, spin through the list of namespaces we don't include in error categories
| |
| − | if this_page.nsText == v then -- if we find one
| |
| − | no_tracking_cats = "true"; -- set no_trackin_cats
| |
| − | break; -- and we're done
| |
| − | end
| |
| − | end
| |
| − | end
| |
| − |
| |
| − | -- check for extra |page=, |pages= or |at= parameters.
| |
| − | if is_set(Page) then
| |
| − | if is_set(Pages) or is_set(At) then
| |
| − | Page = Page .. " " .. seterror('extra_pages'); -- add error message
| |
| − | Pages = ''; -- unset the others
| |
| − | At = '';
| |
| − | end
| |
| − | elseif is_set(Pages) then
| |
| − | if is_set(At) then
| |
| − | Pages = Pages .. " " .. seterror('extra_pages'); -- add error messages
| |
| − | At = ''; -- unset
| |
| − | end
| |
| − | 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
| |
| − |
| |
| − | --[[
| |
| − | 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
| |
| − |
| |
| − | All other combinations of |encyclopedia, |title, and |article are not modified
| |
| − | ]]
| |
| − | if ( config.CitationClass == "encyclopaedia" ) then
| |
| − | if is_set(Periodical) then -- Periodical is set when |encyclopedia is set
| |
| − | if is_set(Title) then
| |
| − | if not is_set(Chapter) then
| |
| − | Chapter = Title; -- |encyclopedia and |title are set so map |title to |article and |encyclopedia to |title
| |
| − | TransChapter = TransTitle;
| |
| − | Title = Periodical;
| |
| − | Periodical = ''; -- redundant so unset
| |
| − | TransTitle = ''; -- redundant so unset
| |
| − | end
| |
| − | else -- |title not set
| |
| − | Title = Periodical; -- |encyclopedia set and |article set or not set so map |encyclopedia to |title
| |
| − | Periodical = ''; -- redundant so unset
| |
| − | end
| |
| − | end
| |
| − | end
| |
| − |
| |
| − | --special cases for citation.
| |
| − | if (config.CitationClass == "citation") then -- for citation templates
| |
| − | if not is_set (Ref) then -- if |ref= is not set
| |
| − | Ref = "harv"; -- set default |ref=harv
| |
| − | end
| |
| − | if not is_set (sepc) then -- if |separator= is not set
| |
| − | sepc = ','; -- set citation separator to its default (comma)
| |
| − | end
| |
| − | else -- not a citation template
| |
| − | if not is_set (sepc) then -- if |separator= has not been set
| |
| − | sepc = '.'; -- set cite xxx separator to its default (period)
| |
| − | end
| |
| − | end
| |
| − |
| |
| − | -- check for specital case where |separator=none
| |
| − | if 'none' == sepc:lower() then -- if |separator=none
| |
| − | sepc = ''; -- then set it to a empty string
| |
| − | end
| |
| − |
| |
| − | -- Special case for cite techreport.
| |
| − | if (config.CitationClass == "techreport") then -- special case for cite techreport
| |
| − | if is_set(Issue) then -- cite techreport uses 'number', which other citations aliase to 'issue'
| |
| − | if not is_set(ID) then -- can we use ID for the "number"?
| |
| − | ID = Issue; -- yes, use it
| |
| − | Issue = ""; -- unset Issue so that "number" isn't duplicated in the rendered citation or COinS metadata
| |
| − | else -- can't use ID so emit error message
| |
| − | ID = ID .. " " .. seterror('redundant_parameters', '<code>|id=</code> and <code>|number=</code>');
| |
| − | end
| |
| − | end
| |
| − | end
| |
| − |
| |
| − | -- special case for cite interview
| |
| − | if (config.CitationClass == "interview") then
| |
| − | if is_set(Program) then
| |
| − | ID = ' ' .. Program;
| |
| − | end
| |
| − | if is_set(Callsign) then
| |
| − | if is_set(ID) then
| |
| − | ID = ID .. sepc .. ' ' .. Callsign;
| |
| − | else
| |
| − | ID = ' ' .. Callsign;
| |
| − | end
| |
| − | end
| |
| − | if is_set(City) then
| |
| − | if is_set(ID) then
| |
| − | ID = ID .. sepc .. ' ' .. City;
| |
| − | else
| |
| − | ID = ' ' .. City;
| |
| − | end
| |
| − | end
| |
| − |
| |
| − | if is_set(Interviewer) then
| |
| − | if is_set(TitleType) then
| |
| − | Others = ' ' .. TitleType .. ' with ' .. Interviewer;
| |
| − | TitleType = '';
| |
| − | else
| |
| − | Others = ' ' .. 'Interview with ' .. Interviewer;
| |
| − | end
| |
| − | if is_set(Cointerviewers) then
| |
| − | Others = Others .. sepc .. ' ' .. Cointerviewers;
| |
| − | end
| |
| − | else
| |
| − | Others = '(Interview)';
| |
| − | end
| |
| − | end
| |
| − |
| |
| − | --Account for the oddity that is {{cite journal}} with |pmc= set and |url= not set
| |
| − | if config.CitationClass == "journal" and not is_set(URL) and is_set(ID_list['PMC']) then
| |
| − | if not is_embargoed(Embargo) then
| |
| − | URL=cfg.id_handlers['PMC'].prefix .. ID_list['PMC']; -- set url to be the same as the PMC external link if not embargoed
| |
| − | URLorigin = cfg.id_handlers['PMC'].parameters[1]; -- set URLorigin to parameter name for use in error message if citation is missing a |title=
| |
| − | end
| |
| − | end
| |
| − |
| |
| − | -- Account for the oddity that is {{cite conference}}, before generation of COinS data.
| |
| − | --TODO: if this is only for {{cite conference}}, shouldn't we be checking? (if config.CitationClass=='conference' then ...)
| |
| − | if is_set(BookTitle) then
| |
| − | Chapter = Title;
| |
| − | ChapterLink = TitleLink;
| |
| − | TransChapter = TransTitle;
| |
| − | Title = BookTitle;
| |
| − | TitleLink = '';
| |
| − | TransTitle = '';
| |
| − | end
| |
| − |
| |
| − | -- Account for the oddity that is {{cite episode}}, before generation of COinS data.
| |
| − | --[[ -- {{cite episode}} is not currently supported by this module
| |
| − | if config.CitationClass == "episode" then
| |
| − | local AirDate = A['AirDate'];
| |
| − | local SeriesLink = A['SeriesLink'];
| |
| − | local Season = A['Season'];
| |
| − | local SeriesNumber = A['SeriesNumber'];
| |
| − | local Network = A['Network'];
| |
| − | local Station = A['Station'];
| |
| − | local s, n = {}, {};
| |
| − | local Sep = (first_set(A["SeriesSeparator"], A["Separator"]) or "") .. " ";
| |
| − |
| |
| − | if is_set(Issue) then table.insert(s, cfg.messages["episode"] .. " " .. Issue); Issue = ''; end
| |
| − | if is_set(Season) then table.insert(s, cfg.messages["season"] .. " " .. Season); end
| |
| − | if is_set(SeriesNumber) then table.insert(s, cfg.messages["series"] .. " " .. SeriesNumber); end
| |
| − | if is_set(Network) then table.insert(n, Network); end
| |
| − | if is_set(Station) then table.insert(n, Station); end
| |
| − |
| |
| − | Date = Date or AirDate;
| |
| − | Chapter = Title;
| |
| − | ChapterLink = TitleLink;
| |
| − | TransChapter = TransTitle;
| |
| − | Title = Series;
| |
| − | TitleLink = SeriesLink;
| |
| − | TransTitle = '';
| |
| − |
| |
| − | Series = table.concat(s, Sep);
| |
| − | ID = table.concat(n, Sep);
| |
| − | end
| |
| − | -- end of {{cite episode}} stuff]]
| |
| − |
| |
| − | -- legacy: promote concatenation of |day=, |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
| |
| − | Date = Year; -- promote Year to Date
| |
| − | Year = nil; -- make nil so Year as empty string isn't used for CITEREF
| |
| − | if is_set(Date) then
| |
| − | local Month = A['Month'];
| |
| − | if is_set(Month) then
| |
| − | Date = Month .. " " .. Date;
| |
| − | local Day = A['Day']
| |
| − | if is_set(Day) then Date = Day .. " " .. Date end
| |
| − | end
| |
| − | elseif is_set(PublicationDate) then -- use PublicationDate when |date= and |year= are not set
| |
| − | Date = PublicationDate; -- promonte PublicationDate to Date
| |
| − | PublicationDate = ''; -- unset, no longer needed
| |
| − | end
| |
| − | end
| |
| − |
| |
| − | if PublicationDate == Date then PublicationDate = ''; end -- if PublicationDate is same as Date, don't display in rendered citation
| |
| − |
| |
| − |
| |
| − | --[[
| |
| − | 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
| |
| − | ]]
| |
| − | anchor_year, COinS_date, error_message = dates({['accessdate']=AccessDate, ['airdate']=AirDate, ['archivedate']=ArchiveDate, ['date']=Date, ['doi_brokendate']=DoiBroken,
| |
| − | ['embargo']=Embargo, ['laydate']=LayDate, ['publicationdate']=PublicationDate, ['year']=Year});
| |
| − | if is_set(error_message) then
| |
| − | table.insert( z.message_tail, { seterror( 'bad_date', {error_message}, true ) } ); -- add this error message
| |
| − | end
| |
| − |
| |
| − | -- At this point fields may be nil if they weren't specified in the template use. We can use that fact.
| |
| − |
| |
| − | -- COinS metadata (see <http://ocoins.info/>) for
| |
| − | -- automated parsing of citation information.
| |
| − | local OCinSoutput = COinS{
| |
| − | ['Periodical'] = Periodical,
| |
| − | ['Chapter'] = Chapter,
| |
| − | ['Title'] = Title,
| |
| − | ['PublicationPlace'] = PublicationPlace,
| |
| − | ['Date'] = first_set(COinS_date, Date), -- COinS_date has correctly formatted date if Date is valid; any reason to keep Date here? Should we be including invalid dates in metadata?
| |
| − | ['Series'] = Series,
| |
| − | ['Volume'] = Volume,
| |
| − | ['Issue'] = Issue,
| |
| − | ['Pages'] = get_coins_pages (first_set(Page, Pages, At)), -- pages stripped of external links
| |
| − | ['Edition'] = Edition,
| |
| − | ['PublisherName'] = PublisherName,
| |
| − | ['URL'] = first_set( URL, ChapterURL ),
| |
| − | ['Authors'] = a,
| |
| − | ['ID_list'] = ID_list,
| |
| − | ['RawPage'] = this_page.prefixedText,
| |
| − | };
| |
| − |
| |
| − | if is_set(Periodical) and not is_set(Chapter) and is_set(Title) then
| |
| − | Chapter = Title;
| |
| − | ChapterLink = TitleLink;
| |
| − | TransChapter = TransTitle;
| |
| − | Title = '';
| |
| − | TitleLink = '';
| |
| − | TransTitle = '';
| |
| − | 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.
| |
| − | if not is_set(Authors) then
| |
| − | local Maximum = tonumber( A['DisplayAuthors'] );
| |
| − |
| |
| − | -- Preserve old-style implicit et al.
| |
| − | if not is_set(Maximum) and #a == 9 then
| |
| − | Maximum = 8;
| |
| − | table.insert( z.message_tail, { seterror('implict_etal_author', {}, true ) } );
| |
| − | elseif not is_set(Maximum) then
| |
| − | Maximum = #a + 1;
| |
| − | end
| |
| − |
| |
| − | local control = {
| |
| − | sep = A["AuthorSeparator"] .. " ",
| |
| − | namesep = (first_set(A["AuthorNameSeparator"], A["NameSeparator"]) or "") .. " ",
| |
| − | format = A["AuthorFormat"],
| |
| − | maximum = Maximum,
| |
| − | lastauthoramp = LastAuthorAmp
| |
| − | };
| |
| − |
| |
| − | -- If the coauthor field is also used, prevent ampersand and et al. formatting.
| |
| − | if is_set(Coauthors) then
| |
| − | control.lastauthoramp = nil;
| |
| − | control.maximum = #a + 1;
| |
| − | end
| |
| − |
| |
| − | Authors = listpeople(control, a)
| |
| − | end
| |
| − |
| |
| − | if not is_set(Authors) and is_set(Coauthors) then -- coauthors aren't displayed if one of authors=, authorn=, or lastn= isn't specified
| |
| − | table.insert( z.message_tail, { seterror('coauthors_missing_author', {}, true) } ); -- emit error message
| |
| − | end
| |
| − |
| |
| − | local EditorCount
| |
| − | if not is_set(Editors) then
| |
| − | local Maximum = tonumber( A['DisplayEditors'] );
| |
| − | -- Preserve old-style implicit et al.
| |
| − | if not is_set(Maximum) and #e == 4 then
| |
| − | Maximum = 3;
| |
| − | table.insert( z.message_tail, { seterror('implict_etal_editor', {}, true) } );
| |
| − | elseif not is_set(Maximum) then
| |
| − | Maximum = #e + 1;
| |
| − | end
| |
| − |
| |
| − | local control = {
| |
| − | sep = A["EditorSeparator"] .. " ",
| |
| − | namesep = (first_set(A["EditorNameSeparator"], A["NameSeparator"]) or "") .. " ",
| |
| − | format = A['EditorFormat'],
| |
| − | maximum = Maximum,
| |
| − | lastauthoramp = LastAuthorAmp
| |
| − | };
| |
| − |
| |
| − | Editors, EditorCount = listpeople(control, e);
| |
| − | else
| |
| − | EditorCount = 1;
| |
| − | end
| |
| − |
| |
| − | local Cartography = "";
| |
| − | local Scale = "";
| |
| − | if config.CitationClass == "map" then
| |
| − | if not is_set( Authors ) and is_set( PublisherName ) then
| |
| − | Authors = PublisherName;
| |
| − | PublisherName = "";
| |
| − | end
| |
| − | Cartography = A['Cartography'];
| |
| − | if is_set( Cartography ) then
| |
| − | Cartography = sepc .. " " .. wrap( 'cartography', Cartography, use_lowercase );
| |
| − | end
| |
| − | Scale = A['Scale'];
| |
| − | if is_set( Scale ) then
| |
| − | Scale = sepc .. " " .. Scale;
| |
| − | end
| |
| − | end
| |
| − |
| |
| − | if not is_set(URL) and
| |
| − | not is_set(ChapterURL) and
| |
| − | not is_set(ArchiveURL) and
| |
| − | not is_set(ConferenceURL) and
| |
| − | not is_set(TranscriptURL) then
| |
| − |
| |
| − | -- Test if cite web or cite podcast |url= is missing or empty
| |
| − | if inArray(config.CitationClass, {"web","podcast"}) then
| |
| − | table.insert( z.message_tail, { seterror( 'cite_web_url', {}, true ) } );
| |
| − | end
| |
| − |
| |
| − | -- Test if accessdate is given without giving a URL
| |
| − | if is_set(AccessDate) then
| |
| − | table.insert( z.message_tail, { seterror( 'accessdate_missing_url', {}, true ) } );
| |
| − | AccessDate = '';
| |
| − | end
| |
| − |
| |
| − | -- Test if format is given without giving a URL
| |
| − | if is_set(Format) then
| |
| − | Format = Format .. seterror( 'format_missing_url' );
| |
| − | end
| |
| − | end
| |
| − |
| |
| − | -- Test if citation has no title
| |
| − | if not is_set(Chapter) and
| |
| − | not is_set(Title) and
| |
| − | not is_set(Periodical) and
| |
| − | not is_set(Conference) and
| |
| − | not is_set(TransTitle) and
| |
| − | not is_set(TransChapter) then
| |
| − | table.insert( z.message_tail, { seterror( 'citation_missing_title', {}, true ) } );
| |
| − | end
| |
| − |
| |
| − | Format = is_set(Format) and " (" .. Format .. ")" or "";
| |
| − |
| |
| − | local OriginalURL = URL
| |
| − | DeadURL = DeadURL:lower();
| |
| − | if is_set( ArchiveURL ) then
| |
| − | if ( DeadURL ~= "no" ) then
| |
| − | URL = ArchiveURL
| |
| − | URLorigin = A:ORIGIN('ArchiveURL')
| |
| − | end
| |
| − | end
| |
| − |
| |
| − | -- Format chapter / article title
| |
| − | if is_set(Chapter) and is_set(ChapterLink) then
| |
| − | Chapter = "[[" .. ChapterLink .. "|" .. Chapter .. "]]";
| |
| − | end
| |
| − | if is_set(Periodical) and is_set(Title) then
| |
| − | Chapter = wrap( 'italic-title', Chapter );
| |
| − | TransChapter = wrap( 'trans-italic-title', TransChapter );
| |
| − | else
| |
| − | Chapter = kern_quotes (Chapter); -- if necessary, separate chapter title's leading and trailing quote marks from Module provided quote marks
| |
| − | Chapter = wrap( 'quoted-title', Chapter );
| |
| − | TransChapter = wrap( 'trans-quoted-title', TransChapter );
| |
| − | end
| |
| − |
| |
| − | local TransError = ""
| |
| − | if is_set(TransChapter) then
| |
| − | if not is_set(Chapter) then
| |
| − | TransError = " " .. seterror( 'trans_missing_chapter' );
| |
| − | else
| |
| − | TransChapter = " " .. TransChapter;
| |
| − | end
| |
| − | end
| |
| − |
| |
| − | Chapter = Chapter .. TransChapter;
| |
| − |
| |
| − | if is_set(Chapter) then
| |
| − | if not is_set(ChapterLink) then
| |
| − | if is_set(ChapterURL) then
| |
| − | Chapter = externallink( ChapterURL, Chapter ) .. TransError;
| |
| − | if not is_set(URL) then
| |
| − | Chapter = Chapter .. Format;
| |
| − | Format = "";
| |
| − | end
| |
| − | elseif is_set(URL) then
| |
| − | Chapter = externallink( URL, Chapter ) .. TransError .. Format;
| |
| − | URL = "";
| |
| − | Format = "";
| |
| − | else
| |
| − | Chapter = Chapter .. TransError;
| |
| − | end
| |
| − | elseif is_set(ChapterURL) then
| |
| − | Chapter = Chapter .. " " .. externallink( ChapterURL, nil, ChapterURLorigin ) ..
| |
| − | TransError;
| |
| − | else
| |
| − | Chapter = Chapter .. TransError;
| |
| − | end
| |
| − | Chapter = Chapter .. sepc .. " " -- with end-space
| |
| − | elseif is_set(ChapterURL) then
| |
| − | Chapter = " " .. externallink( ChapterURL, nil, ChapterURLorigin ) .. sepc .. " ";
| |
| − | end
| |
| − |
| |
| − | -- Format main title.
| |
| − | if is_set(TitleLink) and is_set(Title) then
| |
| − | Title = "[[" .. TitleLink .. "|" .. Title .. "]]"
| |
| − | end
| |
| − |
| |
| − | if is_set(Periodical) then
| |
| − | Title = kern_quotes (Title); -- if necessary, separate title's leading and trailing quote marks from Module provided quote marks
| |
| − | Title = wrap( 'quoted-title', Title );
| |
| − | TransTitle = wrap( 'trans-quoted-title', TransTitle );
| |
| − | elseif inArray(config.CitationClass, {"web","news","pressrelease","conference","podcast"}) and
| |
| − | not is_set(Chapter) then
| |
| − | Title = kern_quotes (Title); -- if necessary, separate title's leading and trailing quote marks from Module provided quote marks
| |
| − | Title = wrap( 'quoted-title', Title );
| |
| − | TransTitle = wrap( 'trans-quoted-title', TransTitle );
| |
| − | else
| |
| − | Title = wrap( 'italic-title', Title );
| |
| − | TransTitle = wrap( 'trans-italic-title', TransTitle );
| |
| − | end
| |
| − |
| |
| − | TransError = "";
| |
| − | if is_set(TransTitle) then
| |
| − | if not is_set(Title) then
| |
| − | TransError = " " .. seterror( 'trans_missing_title' );
| |
| − | else
| |
| − | TransTitle = " " .. TransTitle;
| |
| − | end
| |
| − | end
| |
| − |
| |
| − | Title = Title .. TransTitle;
| |
| − |
| |
| − | if is_set(Title) then
| |
| − | if not is_set(TitleLink) and is_set(URL) then
| |
| − | Title = externallink( URL, Title ) .. TransError .. Format
| |
| − | URL = "";
| |
| − | Format = "";
| |
| − | else
| |
| − | Title = Title .. TransError;
| |
| − | end
| |
| − | end
| |
| − |
| |
| − | if is_set(Place) then
| |
| − | Place = " " .. wrap( 'written', Place, use_lowercase ) .. sepc .. " ";
| |
| − | end
| |
| − |
| |
| − | if is_set(Conference) then
| |
| − | if is_set(ConferenceURL) then
| |
| − | Conference = externallink( ConferenceURL, Conference );
| |
| − | end
| |
| − | Conference = sepc .. " " .. Conference
| |
| − | elseif is_set(ConferenceURL) then
| |
| − | Conference = sepc .. " " .. externallink( ConferenceURL, nil, ConferenceURLorigin );
| |
| − | end
| |
| − |
| |
| − | if not is_set(Position) then
| |
| − | local Minutes = A['Minutes'];
| |
| − | if is_set(Minutes) then
| |
| − | Position = " " .. Minutes .. " " .. cfg.messages['minutes'];
| |
| − | else
| |
| − | local Time = A['Time'];
| |
| − | if is_set(Time) then
| |
| − | local TimeCaption = A['TimeCaption']
| |
| − | if not is_set(TimeCaption) then
| |
| − | TimeCaption = cfg.messages['event'];
| |
| − | if sepc ~= '.' then
| |
| − | TimeCaption = TimeCaption:lower();
| |
| − | end
| |
| − | end
| |
| − | Position = " " .. TimeCaption .. " " .. Time;
| |
| − | end
| |
| − | end
| |
| − | else
| |
| − | Position = " " .. Position;
| |
| − | At = '';
| |
| − | end
| |
| − |
| |
| − | if not is_set(Page) then
| |
| − | if is_set(Pages) then
| |
| − | if is_set(Periodical) and
| |
| − | not inArray(config.CitationClass, {"encyclopaedia","web","book","news","podcast"}) then
| |
| − | Pages = ": " .. Pages;
| |
| − | elseif tonumber(Pages) ~= nil then
| |
| − | Pages = sepc .." " .. PPrefix .. Pages;
| |
| − | else
| |
| − | Pages = sepc .." " .. PPPrefix .. Pages;
| |
| − | end
| |
| − | end
| |
| − | else
| |
| − | if is_set(Periodical) and
| |
| − | not inArray(config.CitationClass, {"encyclopaedia","web","book","news","podcast"}) then
| |
| − | Page = ": " .. Page;
| |
| − | else
| |
| − | Page = sepc .." " .. PPrefix .. Page;
| |
| − | end
| |
| − | end
| |
| − |
| |
| − | At = is_set(At) and (sepc .. " " .. At) or "";
| |
| − | Position = is_set(Position) and (sepc .. " " .. Position) or "";
| |
| − | if config.CitationClass == 'map' then
| |
| − | local Section = A['Section'];
| |
| − | local Inset = A['Inset'];
| |
| − | if first_set( Pages, Page, At ) ~= nil or sepc ~= '.' then
| |
| − | if is_set( Section ) then
| |
| − | Section = ", " .. wrap( 'section', Section, true );
| |
| − | end
| |
| − | if is_set( Inset ) then
| |
| − | Inset = ", " .. wrap( 'inset', Inset, true );
| |
| − | end
| |
| − | else
| |
| − | if is_set( Section ) then
| |
| − | Section = sepc .. " " .. wrap( 'section', Section, use_lowercase );
| |
| − | if is_set( Inset ) then
| |
| − | Inset = ", " .. wrap( 'inset', Inset, true );
| |
| − | end
| |
| − | elseif is_set( Inset ) then
| |
| − | Inset = sepc .. " " .. wrap( 'inset', Inset, use_lowercase );
| |
| − | end
| |
| − | end
| |
| − | At = At .. Section .. Inset;
| |
| − | end
| |
| − |
| |
| − | --[[Look in the list of iso639-1 language codes to see if the value provided in the language parameter matches one of them. If a match is found,
| |
| − | use that value; if not, then use the value that was provided with the language parameter.
| |
| − |
| |
| − | Categories are assigned in a manner similar to the {{xx icon}} templates - categorizes only mainspace citations and only when the language code is not 'en' (English).
| |
| − | ]]
| |
| − | if is_set (Language) then
| |
| − | -- local name = mw.language.fetchLanguageName( Language:lower(), "en" ); -- experiment: this seems to return correct ISO 639-1 language names
| |
| − | local name = cfg.iso639_1[Language:lower()]; -- get the language name if Language parameter has a valid iso 639-1 code
| |
| − | if nil == name then
| |
| − | Language=" " .. wrap( 'language', Language ); -- no match, use parameter's value
| |
| − | else
| |
| − | if 0 == this_page.namespace and 'en' ~= Language:lower() then --found a match; is this page main / article space and English not the language?
| |
| − | Language=" " .. wrap( 'language', name .. '[[Category:Articles with ' .. name .. '-language external links]]' ); -- in main space and not English: categorize
| |
| − | else
| |
| − | Language=" " .. wrap( 'language', name ); --not in mainspace or language is English so don't categorize
| |
| − | end
| |
| − | end
| |
| − | else
| |
| − | Language=""; -- language not specified so make sure this is an empty string;
| |
| − | end
| |
| − |
| |
| − | Others = is_set(Others) and (sepc .. " " .. Others) or "";
| |
| − |
| |
| − | -- handle type parameter for those CS1 citations that have default values
| |
| − |
| |
| − | if inArray(config.CitationClass, {"AV media notes", "DVD notes", "podcast", "pressrelease", "techreport", "thesis"}) then
| |
| − | TitleType = set_titletype (config.CitationClass, TitleType);
| |
| − | if is_set(Degree) and "Thesis" == TitleType then -- special case for cite thesis
| |
| − | TitleType = Degree .. " thesis";
| |
| − | end
| |
| − | end
| |
| − |
| |
| − | if is_set(TitleType) then -- if type parameter is specified
| |
| − | TitleType = " (" .. TitleType .. ")"; -- display it in parentheses
| |
| − | end
| |
| − |
| |
| − | TitleNote = is_set(TitleNote) and (sepc .. " " .. TitleNote) or "";
| |
| − | Edition = is_set(Edition) and (" " .. wrap( 'edition', Edition )) or "";
| |
| − | Issue = is_set(Issue) and (" (" .. Issue .. ")") or "";
| |
| − | Series = is_set(Series) and (sepc .. " " .. Series) or "";
| |
| − | OrigYear = is_set(OrigYear) and (" [" .. OrigYear .. "]") or "";
| |
| − | Agency = is_set(Agency) and (sepc .. " " .. Agency) or "";
| |
| − |
| |
| − | if is_set(Volume) then
| |
| − | if ( mw.ustring.len(Volume) > 4 )
| |
| − | then Volume = sepc .." " .. Volume;
| |
| − | else Volume = " <b>" .. hyphentodash(Volume) .. "</b>";
| |
| − | end
| |
| − | end
| |
| − |
| |
| − | --[[ This code commented out while discussion continues until after week of 2014-03-23 live module update;
| |
| − | if is_set(Volume) then
| |
| − | if ( mw.ustring.len(Volume) > 4 )
| |
| − | then Volume = sepc .. " " .. Volume;
| |
| − | else
| |
| − | Volume = " <b>" .. hyphentodash(Volume) .. "</b>";
| |
| − | if is_set(Series) then Volume = sepc .. Volume;
| |
| − | end
| |
| − | end
| |
| − | end
| |
| − | ]]
| |
| − | ------------------------------------ totally unrelated data
| |
| − | --[[ Loosely mimic {{subscription required}} template; Via parameter identifies a delivery source that is not the publisher; these sources often, but not always, exist
| |
| − | behind a registration or paywall. So here, we've chosen to decouple via from subscription (via has never been part of the registration required template).
| |
| − |
| |
| − | Subscription implies paywall; Registration does not. If both are used in a citation, the subscription required link note is displayed. There are no error messages for this condition.
| |
| − | ]]
| |
| − | if is_set(Via) then
| |
| − | Via = " " .. wrap( 'via', Via );
| |
| − | end
| |
| − |
| |
| − | if is_set(SubscriptionRequired) then
| |
| − | SubscriptionRequired = sepc .. " " .. cfg.messages['subscription']; --here when 'via' parameter not used but 'subscription' is
| |
| − | elseif is_set(RegistrationRequired) then
| |
| − | SubscriptionRequired = sepc .. " " .. cfg.messages['registration']; --here when 'via' and 'subscription' parameters not used but 'registration' is
| |
| − | end
| |
| − |
| |
| − | if is_set(AccessDate) then
| |
| − | local retrv_text = " " .. cfg.messages['retrieved']
| |
| − | if (sepc ~= ".") then retrv_text = retrv_text:lower() end
| |
| − | AccessDate = '<span class="reference-accessdate">' .. sepc
| |
| − | .. substitute( retrv_text, {AccessDate} ) .. '</span>'
| |
| − | end
| |
| − |
| |
| − | if is_set(ID) then ID = sepc .." ".. ID; end
| |
| − | if "thesis" == config.CitationClass and is_set(Docket) then
| |
| − | ID = sepc .." Docket ".. Docket .. ID;
| |
| − | end
| |
| − |
| |
| − |
| |
| − | ID_list = buildidlist( ID_list, {DoiBroken = DoiBroken, ASINTLD = ASINTLD, IgnoreISBN = IgnoreISBN, Embargo=Embargo} );
| |
| − |
| |
| − | if is_set(URL) then
| |
| − | URL = " " .. externallink( URL, nil, URLorigin );
| |
| − | end
| |
| − |
| |
| − | if is_set(Quote) then
| |
| − | if Quote:sub(1,1) == '"' and Quote:sub(-1,-1) == '"' then
| |
| − | Quote = Quote:sub(2,-2);
| |
| − | end
| |
| − | Quote = sepc .." " .. wrap( 'quoted-text', Quote );
| |
| − | PostScript = ""; -- CS1 does not supply terminal punctuation when |quote= is set
| |
| − | end
| |
| − |
| |
| − | local Archived
| |
| − | if is_set(ArchiveURL) then
| |
| − | if not is_set(ArchiveDate) then
| |
| − | ArchiveDate = seterror('archive_missing_date');
| |
| − | end
| |
| − | if "no" == DeadURL then
| |
| − | local arch_text = cfg.messages['archived'];
| |
| − | if sepc ~= "." then arch_text = arch_text:lower() end
| |
| − | Archived = sepc .. " " .. substitute( cfg.messages['archived-not-dead'],
| |
| − | { externallink( ArchiveURL, arch_text ), ArchiveDate } );
| |
| − | if not is_set(OriginalURL) then
| |
| − | Archived = Archived .. " " .. seterror('archive_missing_url');
| |
| − | end
| |
| − | elseif is_set(OriginalURL) then
| |
| − | local arch_text = cfg.messages['archived-dead'];
| |
| − | if sepc ~= "." then arch_text = arch_text:lower() end
| |
| − | Archived = sepc .. " " .. substitute( arch_text,
| |
| − | { externallink( OriginalURL, cfg.messages['original'] ), ArchiveDate } );
| |
| − | else
| |
| − | local arch_text = cfg.messages['archived-missing'];
| |
| − | if sepc ~= "." then arch_text = arch_text:lower() end
| |
| − | Archived = sepc .. " " .. substitute( arch_text,
| |
| − | { seterror('archive_missing_url'), ArchiveDate } );
| |
| − | end
| |
| − | else
| |
| − | Archived = ""
| |
| − | end
| |
| − |
| |
| − | local Lay
| |
| − | if is_set(LayURL) then
| |
| − | if is_set(LayDate) then LayDate = " (" .. LayDate .. ")" end
| |
| − | if is_set(LaySource) then
| |
| − | LaySource = " – ''" .. safeforitalics(LaySource) .. "''";
| |
| − | else
| |
| − | LaySource = "";
| |
| − | end
| |
| − | if sepc == '.' then
| |
| − | Lay = sepc .. " " .. externallink( LayURL, cfg.messages['lay summary'] ) .. LaySource .. LayDate
| |
| − | else
| |
| − | Lay = sepc .. " " .. externallink( LayURL, cfg.messages['lay summary']:lower() ) .. LaySource .. LayDate
| |
| − | end
| |
| − | else
| |
| − | Lay = "";
| |
| − | end
| |
| − |
| |
| − | if is_set(Transcript) then
| |
| − | if is_set(TranscriptURL) then Transcript = externallink( TranscriptURL, Transcript ); end
| |
| − | elseif is_set(TranscriptURL) then
| |
| − | Transcript = externallink( TranscriptURL, nil, TranscriptURLorigin );
| |
| − | end
| |
| − |
| |
| − | local Publisher;
| |
| − | if is_set(Periodical) and
| |
| − | not inArray(config.CitationClass, {"encyclopaedia","web","pressrelease","podcast"}) then
| |
| − | if is_set(PublisherName) then
| |
| − | if is_set(PublicationPlace) then
| |
| − | Publisher = PublicationPlace .. ": " .. PublisherName;
| |
| − | else
| |
| − | Publisher = PublisherName;
| |
| − | end
| |
| − | elseif is_set(PublicationPlace) then
| |
| − | Publisher= PublicationPlace;
| |
| − | else
| |
| − | Publisher = "";
| |
| − | end
| |
| − | if is_set(PublicationDate) then
| |
| − | if is_set(Publisher) then
| |
| − | Publisher = Publisher .. ", " .. wrap( 'published', PublicationDate );
| |
| − | else
| |
| − | Publisher = PublicationDate;
| |
| − | end
| |
| − | end
| |
| − | if is_set(Publisher) then
| |
| − | Publisher = " (" .. Publisher .. ")";
| |
| − | end
| |
| − | else
| |
| − | if is_set(PublicationDate) then
| |
| − | PublicationDate = " (" .. wrap( 'published', PublicationDate ) .. ")";
| |
| − | end
| |
| − | if is_set(PublisherName) then
| |
| − | if is_set(PublicationPlace) then
| |
| − | Publisher = sepc .. " " .. PublicationPlace .. ": " .. PublisherName .. PublicationDate;
| |
| − | else
| |
| − | Publisher = sepc .. " " .. PublisherName .. PublicationDate;
| |
| − | end
| |
| − | elseif is_set(PublicationPlace) then
| |
| − | Publisher= sepc .. " " .. PublicationPlace .. PublicationDate;
| |
| − | else
| |
| − | Publisher = PublicationDate;
| |
| − | end
| |
| − | end
| |
| − |
| |
| − | -- Several of the above rely upon detecting this as nil, so do it last.
| |
| − | if is_set(Periodical) then
| |
| − | if is_set(Title) or is_set(TitleNote) then
| |
| − | Periodical = sepc .. " " .. wrap( 'italic-title', Periodical )
| |
| − | else
| |
| − | Periodical = wrap( 'italic-title', Periodical )
| |
| − | end
| |
| − | end
| |
| − |
| |
| − | --[[
| |
| − | Handle the oddity that is cite speech. This code overrides 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).
| |
| − | ]]
| |
| − | if "speech" == config.CitationClass then -- cite speech only
| |
| − | TitleNote = " (Speech)"; -- annotate the citation
| |
| − | if is_set (Periodical) then -- if Periodical, perhaps because of an included |website= or |journal= parameter
| |
| − | if is_set (Conference) then -- and if |event= is set
| |
| − | Conference = Conference .. sepc .. " "; -- then add appropriate punctuation to the end of the Conference variable before rendering
| |
| − | end
| |
| − | end
| |
| − | end
| |
| − |
| |
| − | -- 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
| |
| − | if inArray(config.CitationClass, {"journal","citation"}) and is_set(Periodical) then
| |
| − | if is_set(Others) then Others = Others .. sepc .. " " end
| |
| − | tcommon = safejoin( {Others, Title, TitleNote, Conference, Periodical, Format, TitleType, Scale, Series,
| |
| − | Language, Cartography, Edition, Publisher, Agency, Volume, Issue}, sepc );
| |
| − | else
| |
| − | tcommon = safejoin( {Title, TitleNote, Conference, Periodical, Format, TitleType, Scale, Series, Language,
| |
| − | Volume, Issue, Others, Cartography, Edition, Publisher, Agency}, sepc );
| |
| − | end
| |
| − |
| |
| − | if #ID_list > 0 then
| |
| − | ID_list = safejoin( { sepc .. " ", table.concat( ID_list, sepc .. " " ), ID }, sepc );
| |
| − | else
| |
| − | ID_list = ID;
| |
| − | end
| |
| − |
| |
| − | local idcommon = safejoin( { ID_list, URL, Archived, AccessDate, Via, SubscriptionRequired, Lay, Quote }, sepc );
| |
| − | local text;
| |
| − | local pgtext = Position .. Page .. Pages .. At;
| |
| − |
| |
| − | if is_set(Authors) then
| |
| − | if is_set(Coauthors) then
| |
| − | Authors = Authors .. A['AuthorSeparator'] .. " " .. Coauthors
| |
| − | end
| |
| − | if is_set(Date) then
| |
| − | Date = " ("..Date..")" .. OrigYear .. sepc .. " "
| |
| − | elseif string.sub(Authors,-1,-1) == sepc then
| |
| − | Authors = Authors .. " "
| |
| − | else
| |
| − | Authors = Authors .. sepc .. " "
| |
| − | end
| |
| − | if is_set(Editors) then
| |
| − | local in_text = " ";
| |
| − | local post_text = "";
| |
| − | if is_set(Chapter) then
| |
| − | in_text = in_text .. cfg.messages['in'] .. " "
| |
| − | else
| |
| − | if EditorCount <= 1 then
| |
| − | post_text = ", " .. cfg.messages['editor'];
| |
| − | else
| |
| − | post_text = ", " .. cfg.messages['editors'];
| |
| − | end
| |
| − | end
| |
| − | if (sepc ~= '.') then in_text = in_text:lower() end
| |
| − | Editors = in_text .. Editors .. post_text;
| |
| − | if (string.sub(Editors,-1,-1) == sepc)
| |
| − | then Editors = Editors .. " "
| |
| − | else Editors = Editors .. sepc .. " "
| |
| − | end
| |
| − | end
| |
| − | text = safejoin( {Authors, Date, Chapter, Place, Editors, tcommon }, sepc );
| |
| − | text = safejoin( {text, pgtext, idcommon}, sepc );
| |
| − | elseif is_set(Editors) then
| |
| − | if is_set(Date) then
| |
| − | if EditorCount <= 1 then
| |
| − | Editors = Editors .. ", " .. cfg.messages['editor'];
| |
| − | else
| |
| − | Editors = Editors .. ", " .. cfg.messages['editors'];
| |
| − | end
| |
| − | Date = " (" .. Date ..")" .. OrigYear .. sepc .. " "
| |
| − | else
| |
| − | if EditorCount <= 1 then
| |
| − | Editors = Editors .. " (" .. cfg.messages['editor'] .. ")" .. sepc .. " "
| |
| − | else
| |
| − | Editors = Editors .. " (" .. cfg.messages['editors'] .. ")" .. sepc .. " "
| |
| − | end
| |
| − | end
| |
| − | text = safejoin( {Editors, Date, Chapter, Place, tcommon}, sepc );
| |
| − | text = safejoin( {text, pgtext, idcommon}, sepc );
| |
| − | else
| |
| − | if is_set(Date) then
| |
| − | if ( string.sub(tcommon,-1,-1) ~= sepc )
| |
| − | then Date = sepc .." " .. Date .. OrigYear
| |
| − | else Date = " " .. Date .. OrigYear
| |
| − | end
| |
| − | end
| |
| − | if config.CitationClass=="journal" and is_set(Periodical) then
| |
| − | text = safejoin( {Chapter, Place, tcommon}, sepc );
| |
| − | text = safejoin( {text, pgtext, Date, idcommon}, sepc );
| |
| − | else
| |
| − | text = safejoin( {Chapter, Place, tcommon, Date}, sepc );
| |
| − | text = safejoin( {text, pgtext, idcommon}, sepc );
| |
| − | end
| |
| − | end
| |
| − |
| |
| − | if is_set(PostScript) and PostScript ~= sepc then
| |
| − | text = safejoin( {text, sepc}, sepc ); --Deals with italics, spaces, etc.
| |
| − | text = text:sub(1,-sepc:len()-1);
| |
| − | -- text = text:sub(1,-2); --Remove final separator (assumes that sepc is only one character)
| |
| − | end
| |
| − |
| |
| − | text = safejoin( {text, PostScript}, sepc );
| |
| − |
| |
| − | -- Now enclose the whole thing in a <span/> element
| |
| − | local options = {};
| |
| − |
| |
| − | if is_set(config.CitationClass) and config.CitationClass ~= "citation" then
| |
| − | options.class = "citation " .. config.CitationClass;
| |
| − | else
| |
| − | options.class = "citation";
| |
| − | end
| |
| − |
| |
| − | if is_set(Ref) and Ref:lower() ~= "none" then
| |
| − | local id = Ref
| |
| − | if ( "harv" == Ref ) then
| |
| − | local names = {} --table of last names & year
| |
| − | if #a > 0 then
| |
| − | for i,v in ipairs(a) do
| |
| − | names[i] = v.last
| |
| − | if i == 4 then break end
| |
| − | end
| |
| − | elseif #e > 0 then
| |
| − | for i,v in ipairs(e) do
| |
| − | names[i] = v.last
| |
| − | if i == 4 then break end
| |
| − | end
| |
| − | end
| |
| − | names[ #names + 1 ] = first_set(Year, anchor_year); -- Year first for legacy citations
| |
| − | id = anchorid(names)
| |
| − | end
| |
| − | options.id = id;
| |
| − | end
| |
| − |
| |
| − | if string.len(text:gsub("<span[^>/]*>.-</span>", ""):gsub("%b<>","")) <= 2 then
| |
| − | z.error_categories = {};
| |
| − | text = seterror('empty_citation');
| |
| − | z.message_tail = {};
| |
| − | end
| |
| − |
| |
| − | if is_set(options.id) then
| |
| − | text = '<span id="' .. mw.uri.anchorEncode(options.id) ..'" class="' .. mw.text.nowiki(options.class) .. '">' .. text .. "</span>";
| |
| − | else
| |
| − | text = '<span class="' .. mw.text.nowiki(options.class) .. '">' .. text .. "</span>";
| |
| − | end
| |
| − |
| |
| − | local empty_span = '<span style="display:none;"> </span>';
| |
| − |
| |
| − | -- Note: Using display: none on then COinS span breaks some clients.
| |
| − | local OCinS = '<span title="' .. OCinSoutput .. '" class="Z3988">' .. empty_span .. '</span>';
| |
| − | text = text .. OCinS;
| |
| − |
| |
| − | if #z.message_tail ~= 0 then
| |
| − | text = text .. " ";
| |
| − | for i,v in ipairs( z.message_tail ) do
| |
| − | if is_set(v[1]) then
| |
| − | if i == #z.message_tail then
| |
| − | text = text .. errorcomment( v[1], v[2] );
| |
| − | else
| |
| − | text = text .. errorcomment( v[1] .. "; ", v[2] );
| |
| − | end
| |
| − | end
| |
| − | end
| |
| − | end
| |
| − |
| |
| − | no_tracking_cats = no_tracking_cats:lower();
| |
| − | if inArray(no_tracking_cats, {"", "no", "false", "n"}) then
| |
| − | for _, v in ipairs( z.error_categories ) do
| |
| − | text = text .. '[[Category:' .. v ..']]';
| |
| − | end
| |
| − | end
| |
| − |
| |
| − | return text
| |
| − | end
| |
| − |
| |
| − | -- This is used by templates such as {{cite book}} to create the actual citation text.
| |
| − | function z.citation(frame)
| |
| − | local pframe = frame:getParent()
| |
| − |
| |
| − | if nil ~= string.find( frame:getTitle(), 'sandbox', 1, true ) then -- did the {{#invoke:}} use sandbox version?
| |
| − | cfg = mw.loadData( 'Module:Citation/CS1/Configuration/sandbox' ); -- load sandbox versions of Configuration and Whitelist and ...
| |
| − | whitelist = mw.loadData( 'Module:Citation/CS1/Whitelist/sandbox' );
| |
| − | dates = require('Module:Citation/CS1/Date_validation/sandbox').dates -- ... sandbox version of date validation code
| |
| − | else -- otherwise
| |
| − | cfg = mw.loadData( 'Module:Citation/CS1/Configuration' ); -- load live versions of Configuration and Whitelist and ...
| |
| − | whitelist = mw.loadData( 'Module:Citation/CS1/Whitelist' );
| |
| − | dates = require('Module:Citation/CS1/Date_validation').dates -- ... live version of date validation code
| |
| − | end
| |
| − |
| |
| − | local args = {};
| |
| − | local suggestions = {};
| |
| − | local error_text, error_state;
| |
| − |
| |
| − | local config = {};
| |
| − | for k, v in pairs( frame.args ) do
| |
| − | config[k] = v;
| |
| − | args[k] = v;
| |
| − | end
| |
| − |
| |
| − | for k, v in pairs( pframe.args ) do
| |
| − | if v ~= '' then
| |
| − | if not validate( k ) then
| |
| − | error_text = "";
| |
| − | if type( k ) ~= 'string' then
| |
| − | -- Exclude empty numbered parameters
| |
| − | if v:match("%S+") ~= nil then
| |
| − | error_text, error_state = seterror( 'text_ignored', {v}, true );
| |
| − | end
| |
| − | elseif validate( k:lower() ) then
| |
| − | error_text, error_state = seterror( 'parameter_ignored_suggest', {k, k:lower()}, true );
| |
| − | else
| |
| − | if #suggestions == 0 then
| |
| − | suggestions = mw.loadData( 'Module:Citation/CS1/Suggestions' );
| |
| − | end
| |
| − | if suggestions[ k:lower() ] ~= nil then
| |
| − | error_text, error_state = seterror( 'parameter_ignored_suggest', {k, suggestions[ k:lower() ]}, true );
| |
| − | else
| |
| − | error_text, error_state = seterror( 'parameter_ignored', {k}, true );
| |
| − | end
| |
| − | end
| |
| − | if error_text ~= '' then
| |
| − | table.insert( z.message_tail, {error_text, error_state} );
| |
| − | end
| |
| − | end
| |
| − | args[k] = v;
| |
| − | elseif args[k] ~= nil or (k == 'postscript') then
| |
| − | args[k] = v;
| |
| − | end
| |
| − | end
| |
| − |
| |
| − | return citation0( config, args)
| |
| − | end
| |
| − |
| |
| − | return z
| |