----------------------------< F O R W A R D D E C L A R A T I O N S >--------------------------------------  
local has_accept_as_written, is_set, in_array, remove_wiki_link, strip_apostrophe_markup; -- functions in Module:Citation/CS1/Utilities  
local cfg; -- table of configuration tables that are defined in Module:Citation/CS1/Configuration  
----------------------------< M A K E _ C O I N S _ T I T L E >----------------------------------------------  
Makes a title for COinS from Title and / or ScriptTitle (or any other name-script pairs)  
Apostrophe markup (bold, italics) is stripped from each value so that the COinS metadata isn't corrupted with strings
of %27%27...  
local function make_coins_title (title, script)
title = has_accept_as_written (title);
if is_set (title) then
title = strip_apostrophe_markup (title); -- strip any apostrophe markup
else
title = ''; -- if not set, make sure title is an empty string
end
if is_set (script) then
script = script:gsub ('^%l%l%s*:%s*', ''); -- remove language prefix if present (script value may now be empty string)
script = strip_apostrophe_markup (script); -- strip any apostrophe markup
else
script = ''; -- if not set, make sure script is an empty string
end
if is_set (title) and is_set (script) then
script = ' ' .. script; -- add a space before we concatenate
end
return title .. script; -- return the concatenation
end  
----------------------------< E S C A P E _ L U A _ M A G I C _ C H A R S >----------------------------------  
Returns a string where all of Lua's magic characters have been escaped. This is important because functions like
string.gsub treat their pattern and replace strings as patterns, not literal strings.  
local function escape_lua_magic_chars (argument)
argument = argument:gsub("%%", "%%%%"); -- replace % with %%
argument = argument:gsub("([%^%$%(%)%.%[%]%*%+%-%?])", "%%%1"); -- replace all other Lua magic pattern characters
return argument;
end  
--page=, |pages=, or |at= parameters for use in COinS.  
local function get_coins_pages (pages)
local pattern;
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
pattern = escape_lua_magic_chars (pattern); -- pattern is not a literal string; escape Lua's magic pattern characters
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 (&ndash; etc.) with hyphens; do we need to replace numerical entities like &#32; and the like?
return pages;
end  
--[=[-------------------------< C O I N S _ R E P L A C E _ M A T H _ S T R I P M A R K E R >------------------  
There are three options for math markup rendering that depend on the editor's math preference settings. These
settings are at Special:Preferences#mw-prefsection-rendering and are
PNG images
TeX source
MathML with SVG or PNG fallback  
All three are heavy with HTML and CSS which doesn't belong in the metadata.  
Without this function, the metadata saved in the raw wikitext contained the rendering determined by the settings
of the last editor to save the page.  
This function gets the rendered form of an equation according to the editor's preference before the page is saved. It
then searches the rendering for the text equivalent of the rendered equation and replaces the rendering with that so
that the page is saved without extraneous HTML/CSS markup and with a reasonably readable text form of the equation.  
When a replacement is made, this function returns true and the value with replacement; otherwise false and the initial
value. To replace multipe equations it is necessary to call this function from within a loop.  
]=]  
local function coins_replace_math_stripmarker (value)
local stripmarker = cfg.stripmarkers['math'];
local rendering = value:match (stripmarker); -- is there a math stripmarker  
if not rendering then -- when value doesn't have a math stripmarker, abandon this test
return false, value;
end  
rendering = mw.text.unstripNoWiki (rendering); -- convert stripmarker into rendered value (or nil? ''? when math render error)  
if rendering:match ('alt="[^"]+"') then -- if PNG math option
rendering = rendering:match ('alt="([^"]+)"'); -- extract just the math text
elseif rendering:match ('$%s+.+%s+%$') then -- if TeX math option; $ is legit character that is escapes as \$
rendering = rendering:match ('$%s+(.+)%s+%$') -- extract just the math text
elseif rendering:match ('<annotation[^>]+>.+</annotation>') then -- if MathML math option
rendering = rendering:match ('<annotation[^>]+>(.+)</annotation>') -- extract just the math text
else
return false, value; -- had math stripmarker but not one of the three defined forms
end  
return true, value:gsub (stripmarker, rendering, 1);
end  
----------------------------< C O I N S _ C L E A N U P >----------------------------------------------------  
Cleanup parameter values for the metadata by removing or replacing invisible characters and certain HTML entities.  
2015-12-10: there is a bug in mw.text.unstripNoWiki . It replaces math stripmarkers with the appropriate content
when it shouldn't. See https://phabricator.wikimedia.org/T121085 and Wikipedia_talk:Lua#stripmarkers_and_mw.text.unstripNoWiki.28.29  
TODO: move the replacement patterns and replacement values into a table in /Configuration similar to the invisible
characters table?  
local function coins_cleanup (value)
local replaced = true; -- default state to get the do loop running  
while replaced do -- loop until all math stripmarkers replaced
replaced, value = coins_replace_math_stripmarker (value); -- replace math stripmarker with text representation of the equation
end  
value = value:gsub (cfg.stripmarkers['math'], "MATH RENDER ERROR"); -- one or more couldn't be replaced; insert vague error message  
value = mw.text.unstripNoWiki (value); -- replace nowiki stripmarkers with their content
value = value:gsub ('<span>&#39;(s?)</span>', "'%1"); -- replace or with simple apostrophe or apostrophe-s
value = value:gsub ('&nbsp;', ' '); -- replace &nbsp; entity with plain space
value = value:gsub ('\226\128\138', ' '); -- replace hair space with plain space
if not mw.ustring.find (value, cfg.indic_script) then -- don't remove zero-width joiner characters from indic script
value = value:gsub ('&zwj;', ''); -- remove &zwj; entities
value = mw.ustring.gsub (value, '[\226\128\141\226\128\139\194\173]', ''); -- remove zero-width joiner, zero-width space, soft hyphen
end
value = value:gsub ('[\009\010\013 ]+', ' '); -- replace horizontal tab, line feed, carriage return with plain space
return value;
end  
----------------------------< C O I N S >--------------------------------------------------------------------  
COinS metadata (see <http://ocoins.info/>) allows automated tools to parse the citation information.  
local function COinS(data, class)
if 'table' ~= type(data) or nil == next(data) then
return '';
end  
for k, v in pairs (data) do -- spin through all of the metadata parameter values
if 'ID_list' ~= k and 'Authors' ~= k then -- except the ID_list and Author tables (author nowiki stripmarker done when Author table processed)
data[k] = coins_cleanup (v);
end
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( remove_wiki_link( value ) ) } );
end
end
});