Difference between revisions of "Module:Bibleverse"

From blackwiki
Jump to navigation Jump to search
blackwiki>Jonesey95
(revert to last good version before my failed attempts.)
blackwiki>Johnuniq
(update from Module:Bibleverse/sandbox; tweaks + fix URLs for biblica.com and usccb.org and fix links to no-chapter books; display error message as well as add Category:Pages with Bible version errors)
Line 3: Line 3:
  
 
--codes from bvf that may be in use
 
--codes from bvf that may be in use
local version_num_cds = {
+
local version_num_codes = {
 
[9] = 'kjv', [31] = 'niv', [45] = 'amp', [49] = 'nasb', [64] = 'nivuk',
 
[9] = 'kjv', [31] = 'niv', [45] = 'amp', [49] = 'nasb', [64] = 'nivuk',
 
[51] = 'nlt', [65] = 'msg', [76] = 'nirv', [105] = 'he', [104] = 'jps',
 
[51] = 'nlt', [65] = 'msg', [76] = 'nirv', [105] = 'he', [104] = 'jps',
Line 50: Line 50:
 
['zechariah'] = {'zech', 'zec'},
 
['zechariah'] = {'zech', 'zec'},
 
['malachi'] = {'mal'},
 
['malachi'] = {'mal'},
['baruch'] = {'bar'},                                           --apocrypha/deuterocanon
+
['baruch'] = {'bar'},                                 --apocrypha/deuterocanon
 
['ecclesiasticus'] = {'ecclus', 'wisdomofsirach', 'sirach', 'sir'},
 
['ecclesiasticus'] = {'ecclus', 'wisdomofsirach', 'sirach', 'sir'},
 
['1_esdras'] = {'1esd'},
 
['1_esdras'] = {'1esd'},
Line 67: Line 67:
 
['bel_and_the_dragon'] = {'bel'},
 
['bel_and_the_dragon'] = {'bel'},
 
['epistle_of_jeremiah'] = {'letterofjeremiah'},
 
['epistle_of_jeremiah'] = {'letterofjeremiah'},
['matthew'] = {'matt', 'mt'},                                       --new testament
+
['matthew'] = {'matt', 'mt'},                         --new testament
 
['mark'] = {'mk'},
 
['mark'] = {'mk'},
 
['luke'] = {'lk'},
 
['luke'] = {'lk'},
Line 104: Line 104:
  
 
--these books only have one chapter, have to be handled differently
 
--these books only have one chapter, have to be handled differently
local no_chaps = {
+
local no_chapters = {
'obadiah', 'epistle_of_jeremiah', 'prayer_of_azariah',
+
['obadiah'] = true, ['epistle_of_jeremiah'] = true, ['prayer_of_azariah'] = true,
'susanna', 'bel_and_the_dragon', 'prayer_of_manasseh', 'philemon', '2_john',
+
['susanna'] = true, ['bel_and_the_dragon'] = true, ['prayer_of_manasseh'] = true,
'3_john', 'jude',
+
['philemon'] = true, ['2_john'] = true, ['3_john'] = true, ['jude'] = true,
 
}
 
}
  
Line 122: Line 122:
 
}
 
}
  
--pattern for the url of each site, use _book, v_range, etc. for variables
+
--pattern for the url of each site using _book etc. (underscore then letters [a-z]) for variables
 
local urlpat_tbl = {
 
local urlpat_tbl = {
oremus = 'https://bible.oremus.org/?passage=_book+v_range&version=_version',
+
oremus = 'https://bible.oremus.org/?passage=_book%20_vrange&version=_version',
wikisource = 's:Bible (_version)/_book#s_chap:s_vers',
+
wikisource = 's:Bible (_version)/_book#_schap:_svers',
mechon_mamre = 'https://www.mechon-mamre.org/_version_books_chap.htm#s_vers',
+
mechon_mamre = 'https://www.mechon-mamre.org/_version_book_schap.htm#_svers',
biblica = 'https://www.biblica.com/bible/?osis=_version:_book.v_range',
+
biblica = 'https://www.biblica.com/bible/?osis=_version:_book%20_vrange',
usccb = 'http://www.usccb.org/bible/_book.s_chap:s_vers',
+
usccb = 'https://bible.usccb.org/bible/_book/_schap?_svers',
biblegateway = 'https://www.biblegateway.com/passage/?search=_book%20v_range&version=_version',
+
biblegateway = 'https://www.biblegateway.com/passage/?search=_book%20_vrange&version=_version',
bible_hub = 'https://_version/_book/s_chap-s_vers.htm',
+
bible_hub = 'https://_version/_book/_schap-_svers.htm',
esv_bible = 'https://www.esv.org/_book+s_chap:s_vers',
+
esv_bible = 'https://www.esv.org/_book+_schap:_svers',
 
}
 
}
  
Line 178: Line 178:
 
}
 
}
  
local function trimArg(arg)
+
local function trimArg(text)
if arg == '' or arg == ' ' or arg == nil then
+
if type(text) == 'string' then
return nil
+
text = text:match('(%S.-)%s*$') --trimmed text or nil if empty
else
 
return mw.text.trim(arg)
 
 
end
 
end
 +
return text
 
end
 
end
  
 
local function valueExists(tbl, value)
 
local function valueExists(tbl, value)
for k,v in pairs(tbl) do
+
for _, v in pairs(tbl) do
 
if value == v then
 
if value == v then
 
return true
 
return true
Line 206: Line 205:
 
['from'] = true, ['by'] = true, ['of'] = true, ['up'] = true,
 
['from'] = true, ['by'] = true, ['of'] = true, ['up'] = true,
 
}
 
}
local res = ''
+
local words = mw.text.split(mw.text.trim(arg or ''), '_')
local s = mw.text.trim(arg or '')
 
local words = mw.text.split( s, '_')
 
 
for i, s in ipairs(words) do
 
for i, s in ipairs(words) do
 
s = string.lower(s)
 
s = string.lower(s)
Line 224: Line 221:
  
 
function p.main(frame)
 
function p.main(frame)
local args = frame:getParent().args
+
local targs = frame:getParent().args
 
+
local args = {}
 +
for _, param in ipairs({1, 2, 3, 4, 5, 'nobook'}) do
 +
args[param] = trimArg(targs[param])
 +
end
 
local default_version = 'nrsv'
 
local default_version = 'nrsv'
 
local input_book = ''
 
local input_book = ''
Line 232: Line 232:
 
local version_num
 
local version_num
 
local text = ''
 
local text = ''
local this_page = mw.title.getCurrentTitle()
+
local mainspace = mw.title.getCurrentTitle():inNamespaces(0)
local mainspace = this_page:inNamespaces( 0 )
+
if args[1] == nil or args[2] == nil or tonumber(args[1]) ~= nil then
if trimArg(args[1]) == nil or trimArg(args[2]) == nil or tonumber(trimArg(args[1])) ~= nil then
 
 
-- first argument is a numeric prefix and second is book name
 
-- first argument is a numeric prefix and second is book name
input_book = trimArg((args[1] or '') .. ' ' .. (trimArg(args[2]) or '')) or ''
+
input_book = trimArg((args[1] or '') .. ' ' .. (args[2] or '')) or ''
ref = trimArg(args[3]) or ''
+
ref = args[3] or ''
version = mw.ustring.lower(trimArg(args[4]) or default_version)
+
version = mw.ustring.lower(args[4] or default_version)
text = trimArg(args[5]) or trimArg((input_book .. ' ' .. ref))
+
text = args[5] or trimArg((input_book .. ' ' .. ref))
 
else
 
else
 
-- first argument is the whole book name
 
-- first argument is the whole book name
input_book = trimArg(args[1]) or ''
+
input_book = args[1] or ''
ref = trimArg(args[2]) or ''
+
ref = args[2] or ''
version = mw.ustring.lower(trimArg(args[3]) or default_version)
+
version = mw.ustring.lower(args[3] or default_version)
text = trimArg(args[4]) or (input_book .. ' ' .. ref)
+
text = args[4] or (input_book .. ' ' .. ref)
 
end
 
end
 
+
if args.nobook == 'yes' then
if trimArg(args.nobook) == 'yes' then
 
 
text = ref
 
text = ref
 
end
 
end
 +
text = text:gsub('-', '–')  --change hyphens to en dashes (3:2-5 → 3:2–5)
  
if tonumber(version) ~= nil then
+
if tonumber(version) then
version = tonumber(version)
+
version = version_num_codes[tonumber(version)] or version
 
version_num = true
 
version_num = true
 
end
 
end
 
version = version_num_cds[version] or version
 
  
 
local errors = {}
 
local errors = {}
Line 267: Line 264:
 
end
 
end
  
local book = mw.ustring.gsub(input_book, '%p', '')
+
local book = input_book:gsub('%p', ''):gsub(' ', '_')
book = mw.ustring.gsub(book, ' ', '_')
 
 
book = mw.ustring.lower(book)
 
book = mw.ustring.lower(book)
  
 
local book_found = false
 
local book_found = false
for full_books, alias_tbls in pairs(book_aliases) do
+
local standard = book:gsub('_', '')
if mw.ustring.gsub(book, '_', '') == mw.ustring.gsub(full_books, '_', '') then  --is the book already standardized?
+
for full_book, aliases in pairs(book_aliases) do
book_found = true
+
if standard == full_book:gsub('_', '') or valueExists(aliases, standard) then
elseif valueExists(book_aliases[full_books], mw.ustring.gsub(book, '_', '')) then --check to see if it exists as a value in each table
+
book = full_book
book = full_books
 
 
book_found = true
 
book_found = true
 +
break
 
end
 
end
 
end
 
end
Line 284: Line 280:
  
 
if valueExists(apoc, book) and version == 'nrsv' and site == 'oremus' then  --oremus's nrsv doesn't have the apocrypha
 
if valueExists(apoc, book) and version == 'nrsv' and site == 'oremus' then  --oremus's nrsv doesn't have the apocrypha
version = 'nrsvae'
+
version = 'nrsvae' --the anglicized version does
end                            --the anglicized version does
+
end
  
 
local urlpat = urlpat_tbl[site]
 
local urlpat = urlpat_tbl[site]
Line 292: Line 288:
 
local s_ref = mw.text.split(split_ref[1], '%p')    --any punctuation can be used to separate chapter from verse
 
local s_ref = mw.text.split(split_ref[1], '%p')    --any punctuation can be used to separate chapter from verse
 
local e_ref = split_ref[2] or split_ref[1]
 
local e_ref = split_ref[2] or split_ref[1]
local e_ref = mw.text.split(e_ref, '%p')
+
e_ref = mw.text.split(e_ref, '%p')
for i, v in ipairs(s_ref) do s_ref[i] = mw.ustring.gsub(v, '%D', '') end     --remove any non-numeric character (such as f)
+
for i, v in ipairs(s_ref) do s_ref[i] = v:gsub('%D', '') end --remove any non-numeric character (such as f)
for i, v in ipairs(e_ref) do e_ref[i] = mw.ustring.gsub(v, '%D', '') end
+
for i, v in ipairs(e_ref) do e_ref[i] = v:gsub('%D', '') end
  
 
local e_chap, e_vers, s_chap, s_vers
 
local e_chap, e_vers, s_chap, s_vers
if valueExists(no_chaps, book) then
+
local chapter_only = not s_ref[2]
 +
if no_chapters[book] then
 +
chapter_only = false
 
s_chap = 1
 
s_chap = 1
s_vers = s_ref[1] or s_ref[2] or 1
+
s_vers = s_ref[2] or s_ref[1] or 1   --verse 3 can be specified as "3" or "1:3"
 
e_chap = 1
 
e_chap = 1
 
e_vers = e_ref[2] or e_ref[1] or 1
 
e_vers = e_ref[2] or e_ref[1] or 1
Line 317: Line 315:
 
end
 
end
  
book = titlecase(book)                             --put book in title case, only because it looks better at oremus where they display the input
+
book = titlecase(book) --title case looks better at oremus where they display the input
  
if site == 'mechon_mamre' then                     --special case handling of aspects of the components of the url
+
if site == 'mechon_mamre' then   --special case handling for components of the url
s_chap = string.format( '%x', s_chap/10 ) .. string.sub(string.format('%02d', s_chap), -1)
+
if not tonumber(s_chap) then  --non-numeric characters were omitted above so this may not be what was entered
elseif (site == 'oremus') or (site == 'esv_bible') then
+
error('Bibleverse error: start chapter should be a number', 0)
book = mw.ustring.gsub(book, '_', '+')
+
end
 +
s_chap = string.format('%x', s_chap/10) .. (s_chap % 10)
 +
elseif site == 'biblica' or site == 'oremus' then
 +
book = book:gsub('_', '%%20')
 +
elseif site == 'esv_bible' then
 +
book = book:gsub('_', '+')
 
elseif site == 'bible_hub' then
 
elseif site == 'bible_hub' then
 
book = string.lower(book)
 
book = string.lower(book)
 
elseif site == 'usccb' then
 
elseif site == 'usccb' then
book = mw.ustring.gsub(book, '_', '')
+
book = book:gsub('_', '')
 
end
 
end
 
 
if type(site_version_tbl[site]) == 'table' then
 
if type(site_version_tbl[site]) == 'table' then
 
version = site_version_tbl[site][version]
 
version = site_version_tbl[site][version]
Line 334: Line 336:
  
 
local v_range
 
local v_range
if s_ref[2] == nil then                             --case of chapter range only
+
if chapter_only then
v_range = s_chap ..'–' .. e_chap
+
if e_chap == s_chap then
 +
v_range = s_chap
 +
else
 +
v_range = s_chap .. '–' .. e_chap
 +
end
 
else
 
else
v_range = s_chap ..':' .. s_vers ..'–' .. e_chap ..':' .. e_vers   --case of chapter and verse range
+
if e_chap == s_chap and e_vers == s_vers then
 +
v_range = s_chap ..':' .. s_vers
 +
elseif e_chap == s_chap then
 +
v_range = s_chap .. ':' .. s_vers .. '–' .. e_vers
 +
else
 +
v_range = s_chap .. ':' .. s_vers .. '–' .. e_chap .. ':' .. e_vers
 +
end
 
end
 
end
  
local url = mw.ustring.gsub(urlpat, '_book', book) --get the components into the urls
+
local url = urlpat:gsub('_%l+', { --get the components into the url
url = mw.ustring.gsub(url, 's_chap', s_chap)
+
_book    = book,
url = mw.ustring.gsub(url, 's_vers', s_vers)
+
_schap  = s_chap,
url = mw.ustring.gsub(url, 'e_chap', e_chap)
+
_svers  = s_vers,
url = mw.ustring.gsub(url, 'e_vers', e_vers)
+
_echap  = e_chap,
url = mw.ustring.gsub(url, 'v_range', v_range)
+
_evers  = e_vers,
url = mw.ustring.gsub(url, '_version', version)
+
_vrange  = v_range,
 +
_version = version,
 +
})
  
 
local fulllink
 
local fulllink
if site == 'wikisource' then                       --interwikilink
+
if site == 'wikisource' then --interwikilink
 
fulllink = '[[' .. url .. '|' .. text .. ']]'
 
fulllink = '[[' .. url .. '|' .. text .. ']]'
 
else
 
else
Line 355: Line 369:
 
end
 
end
  
if not book_found and mainspace == true then
+
if mainspace then
table.insert(errors, '[[Category:Pages with Bible book errors]]')
+
if not book_found then
end
+
table.insert(errors, '<span style="color:red">Template:Bibleverse with invalid book</span>[[Category:Pages with Bible book errors]]')
if version_num and mainspace == true then
+
end
table.insert(errors, '[[Category:Pages with numeric Bible version references]]')
+
if version_num then
 +
table.insert(errors, '[[Category:Pages with numeric Bible version references]]')
 +
end
 
end
 
end
 
+
return fulllink .. table.concat(errors)
return fulllink .. (table.concat(errors) or '')
 
 
end
 
end
  
 
return p
 
return p

Revision as of 01:36, 6 September 2020

This module implements {{Bibleverse}}. Please see the template page for documentation.



-- This module implements Template:Bibleverse.
local p = {}

--codes from bvf that may be in use
local version_num_codes = {
	[9] = 'kjv', [31] = 'niv', [45] = 'amp', [49] = 'nasb', [64] = 'nivuk',
	[51] = 'nlt', [65] = 'msg', [76] = 'nirv', [105] = 'he', [104] = 'jps',
	[120] = 'opb', [121] = 'bb', [122] = 'bbc', [123] = 'bbl', [131] = 'nrsv',
}

--possible book inputs, based on Chicago Manual
local book_aliases = {
	['genesis'] = {'gen', 'gn'},                          --old testament/tanakh
	['exodus'] = {'exod', 'ex'},
	['leviticus'] = {'lev', 'lv'},
	['numbers'] = {'num', 'nm'},
	['deuteronomy'] = {'deut', 'dt'},
	['joshua'] = {'josh' , 'jo'},
	['judges'] = {'judg', 'jgs'},
	['ruth'] = {'ru'},
	['1_samuel'] = {'1sam', '1sm'},
	['2_samuel'] = {'2sam', '2sm'},
	['1_kings'] = {'1kgs'},
	['2_kings'] = {'2kgs'},
	['1_chronicles'] = {'1chron', '1chr'},
	['2_chronicles'] = {'2chron', '2chr'},
	['ezra'] = {'ezr'},
	['nehemiah'] = {'neh'},
	['esther'] = {'est'},
	['job'] = {'jb'},
	['psalms'] = {'ps', 'pss', 'psalm'},
	['proverbs'] = {'prov', 'prv'},
	['ecclesiastes'] = {'eccles', 'eccl', 'qoheleth'},
	['song_of_solomon'] = {'songofsol', 'songofsongs', 'song', 'sg', 'canticles', 'canticleofcanticles'},
	['isaiah'] = {'isa', 'is'},
	['jeremiah'] = {'jer'},
	['lamentations'] = {'lam'},
	['ezekiel'] = {'ezek', 'ez'},
	['daniel'] = {'dan', 'dn'},
	['hosea'] = {'hos'},
	['joel'] = {'jl'},
	['amos'] = {'am'},
	['obadiah'] = {'obad', 'ob'},
	['jonah'] = {'jon'},
	['micah'] = {'mic', 'mi'},
	['nahum'] = {'nah', 'na'},
	['habakkuk'] = {'hab', 'hb'},
	['zephaniah'] = {'zeph', 'zep'},
	['haggai'] = {'hag', 'hg'},
	['zechariah'] = {'zech', 'zec'},
	['malachi'] = {'mal'},
	['baruch'] = {'bar'},                                 --apocrypha/deuterocanon
	['ecclesiasticus'] = {'ecclus', 'wisdomofsirach', 'sirach', 'sir'},
	['1_esdras'] = {'1esd'},
	['2_esdras'] = {'2esd'},
	['judith'] = {'jth', 'jdt'},
	['1_maccabees'] = {'1macc', '1mc'},
	['2_maccabees'] = {'2macc', '2mc'},
	['3_maccabees'] = {'3macc', '3mc'},
	['4_maccabees'] = {'4macc', '4mc'},
	['prayer_of_manasseh'] = {'profman', 'prayerofmanasses'},
	['susanna'] = {'sus'},
	['tobit'] = {'tob', 'tb'},
	['wisdom_of_solomon'] = {'ws', 'wisdom', 'wisofsol'},
	['prayer_of_azariah'] = {'prayerofazariahandhymnofthethreechildren'},
	['rest_of_esther'] = {'additionstoesther'},
	['bel_and_the_dragon'] = {'bel'},
	['epistle_of_jeremiah'] = {'letterofjeremiah'},
	['matthew'] = {'matt', 'mt'},                         --new testament
	['mark'] = {'mk'},
	['luke'] = {'lk'},
	['john'] = {'jn'},
	['acts'] = {'actsoftheapostles'},
	['romans'] = {'rom'},
	['1_corinthians'] = {'1cor'},
	['2_corinthians'] = {'2cor'},
	['galatians'] = {'gal'},
	['ephesians'] = {'eph'},
	['philippians'] = {'phil'},
	['colossians'] = {'col'},
	['1_thessalonians'] = {'1thess', '1thes'},
	['2_thessalonians'] = {'2thess', '2thes'},
	['1_timothy'] = {'1tim', '1tm'},
	['2_timothy'] = {'2tim', '2tm'},
	['titus'] = {'ti'},
	['philemon'] = {'philem', 'phlm'},
	['hebrews'] = {'heb'},
	['james'] = {'jas'},
	['1_peter'] = {'1pet', '1pt'},
	['2_peter'] = {'2pet', '2pt'},
	['1_john'] = {'1jn'},
	['2_john'] = {'2jn'},
	['3_john'] = {'3jn'},
	['jude'] = {'jude'},
	['revelation'] = {'rev', 'apocalypse', 'apoc', 'rv'},
}

local apoc = {
	'1_esdras', '2_esdras', 'judith', '1_maccabees', '2_maccabees',
	'3_maccabees', '4_maccabees', 'prayer_of_manasseh', 'susanna', 'tobit',
	'wisdom_of_solomon', 'prayer_of_azariah', 'rest_of_esther', 'bel_and_the_dragon',
	'epistle_of_jeremiah',
}

--these books only have one chapter, have to be handled differently
local no_chapters = {
	['obadiah'] = true, ['epistle_of_jeremiah'] = true, ['prayer_of_azariah'] = true,
	['susanna'] = true, ['bel_and_the_dragon'] = true, ['prayer_of_manasseh'] = true,
	['philemon'] = true, ['2_john'] = true, ['3_john'] = true, ['jude'] = true,
}

--sets the website to be used for each version, biblegateway is the fallback so don't define any for it
local site_tbl = {
	nrsv = 'oremus', nrsvae = 'oremus',
	kjv = 'wikisource', asv = 'wikisource',
	he = 'mechon_mamre', jps = 'mechon_mamre',
	niv = 'biblica', nivuk = 'biblica', amp = 'biblica', ab = 'biblica',
	nasb = 'biblica', msg = 'biblica', nlt = 'biblica', nirv = 'biblica',
	nab = 'usccb',
	bb = 'bible_hub', bbc = 'bible_hub', bbl = 'bible_hub', opb = 'bible_hub',
	esv = 'esv_bible',
}

--pattern for the url of each site using _book etc. (underscore then letters [a-z]) for variables
local urlpat_tbl = {
	oremus = 'https://bible.oremus.org/?passage=_book%20_vrange&version=_version',
	wikisource = 's:Bible (_version)/_book#_schap:_svers',
	mechon_mamre = 'https://www.mechon-mamre.org/_version_book_schap.htm#_svers',
	biblica = 'https://www.biblica.com/bible/?osis=_version:_book%20_vrange',
	usccb = 'https://bible.usccb.org/bible/_book/_schap?_svers',
	biblegateway = 'https://www.biblegateway.com/passage/?search=_book%20_vrange&version=_version',
	bible_hub = 'https://_version/_book/_schap-_svers.htm',
	esv_bible = 'https://www.esv.org/_book+_schap:_svers',
}

--changes book name to use in url for each site, only if necessary
local site_book_tbl = {
	oremus = {
		['epistle_of_jeremiah'] = 'letter of jeremiah',
	},
	usccb = {
		['song_of_solomon'] = 'song_of_songs',
		['wisdom_of_solomon'] = 'wisdom',
		['ecclesiasticus'] = 'sirach',
	},
	mechon_mamre = {
		['genesis'] = '01', ['exodus'] = '02', ['leviticus'] = '03',
		['numbers'] = '04', ['deuteronomy'] = '05', ['joshua'] = '06',
		['judges'] = '07', ['1_samuel'] = '08a', ['2_samuel'] = '08b',
		['1_kings'] = '09a', ['2_kings'] = '09b', ['isaiah'] = '10',
		['jeremiah'] = '11', ['ezekiel'] = '12', ['hosea'] = '13',
		['joel'] = '14', ['amos'] = '15', ['obadiah'] = '16', ['jonah'] = '17',
		['micah'] = '18', ['nahum'] = '19', ['habakkuk'] = '20',
		['zephaniah'] = '21', ['haggai'] = '22', ['zechariah'] = '23',
		['malachi'] = '24', ['1_chronicles'] = '25a', ['2_chronicles'] = '25b',
		['psalms'] = '26', ['job'] = '27', ['proverbs'] = '28', ['ruth'] = '29',
		['song_of_solomon'] = '30', ['ecclesiastes'] = '31', ['lamentations'] = '32',
		['esther'] = '33', ['daniel'] = '34', ['ezra'] = '35a', ['nehemiah'] = '35b',
	}
}

--changes to the version name to be used in urls, only if necessary
local site_version_tbl = {
	mechon_mamre = {
		he = 'p/pt/pt',
		jps = 'e/et/et',
	},
	wikisource = {
		kjv = 'King James', ['9'] = 'King James',
		asv = 'American Standard',
	},
	bible_hub = {
		bb = 'biblebrowser.com',
		bbc = 'biblecommenter.com',
		bbl = 'scripturetext.com',
		opb = 'bible.cc',
	}
}

local function trimArg(text)
	if type(text) == 'string' then
		text = text:match('(%S.-)%s*$')  --trimmed text or nil if empty
	end
	return text
end

local function valueExists(tbl, value)
	for _, v in pairs(tbl) do
		if value == v then
			return true
		end
	end
	return false
end

local function titlecase(arg)
	-- http://grammar.yourdictionary.com/capitalization/rules-for-capitalization-in-titles.html
	-- recommended by The U.S. Government Printing Office Style Manual:
	-- 'Capitalize all words in titles of publications and documents,
	-- except a, an, the, at, by, for, in, of, on, to, up, and, as, but, or, and nor.'
	local alwayslower = {
		['a'] = true, ['an'] = true, ['the'] = true,
		['and'] = true, ['but'] = true, ['or'] = true, ['for'] = true,
		['nor'] = true, ['on'] = true, ['in'] = true, ['at'] = true, ['to'] = true,
		['from'] = true, ['by'] = true, ['of'] = true, ['up'] = true,
	}
	local words = mw.text.split(mw.text.trim(arg or ''), '_')
	for i, s in ipairs(words) do
		s = string.lower(s)
		if i > 1 then
			if not alwayslower[s] then
				s = mw.getContentLanguage():ucfirst(s)
			end
		else
			s = mw.getContentLanguage():ucfirst(s)
		end
		words[i] = s
	end
	return table.concat(words, '_')
end

function p.main(frame)
	local targs = frame:getParent().args
	local args = {}
	for _, param in ipairs({1, 2, 3, 4, 5, 'nobook'}) do
		args[param] = trimArg(targs[param])
	end
	local default_version = 'nrsv'
	local input_book = ''
	local ref = ''
	local version = ''
	local version_num
	local text = ''
	local mainspace = mw.title.getCurrentTitle():inNamespaces(0)
	if args[1] == nil or args[2] == nil or tonumber(args[1]) ~= nil then
		-- first argument is a numeric prefix and second is book name
		input_book = trimArg((args[1] or '') .. ' ' .. (args[2] or '')) or ''
		ref = args[3] or ''
		version = mw.ustring.lower(args[4] or default_version)
		text = args[5] or trimArg((input_book .. ' ' .. ref))
	else
		-- first argument is the whole book name
		input_book = args[1] or ''
		ref = args[2] or ''
		version = mw.ustring.lower(args[3] or default_version)
		text = args[4] or (input_book .. ' ' .. ref)
	end
	if args.nobook == 'yes' then
		text = ref
	end
	text = text:gsub('-', '–')  --change hyphens to en dashes (3:2-5 → 3:2–5)

	if tonumber(version) then
		version = version_num_codes[tonumber(version)] or version
		version_num = true
	end

	local errors = {}
	if version == 'jpr' or version == 'jp' then  --chabad doesn't work so reroute
		version = 'he'
		if mainspace then
			table.insert(errors, '[[Category:Pages with Bible version errors]]')
		end
	end

	local book = input_book:gsub('%p', ''):gsub(' ', '_')
	book = mw.ustring.lower(book)

	local book_found = false
	local standard = book:gsub('_', '')
	for full_book, aliases in pairs(book_aliases) do
		if standard == full_book:gsub('_', '') or valueExists(aliases, standard) then
			book = full_book
			book_found = true
			break
		end
	end

	local site = site_tbl[version] or 'biblegateway'    --fall back on biblegateway for unrecognized version because it has so many versions

	if valueExists(apoc, book) and version == 'nrsv' and site == 'oremus' then  --oremus's nrsv doesn't have the apocrypha
		version = 'nrsvae'  --the anglicized version does
	end

	local urlpat = urlpat_tbl[site]

	local split_ref = mw.text.split(ref, '[-–—]')       --split the ref into the part before and after the dash/hyphen
	local s_ref = mw.text.split(split_ref[1], '%p')     --any punctuation can be used to separate chapter from verse
	local e_ref = split_ref[2] or split_ref[1]
	e_ref = mw.text.split(e_ref, '%p')
	for i, v in ipairs(s_ref) do s_ref[i] = v:gsub('%D', '') end  --remove any non-numeric character (such as f)
	for i, v in ipairs(e_ref) do e_ref[i] = v:gsub('%D', '') end

	local e_chap, e_vers, s_chap, s_vers
	local chapter_only = not s_ref[2]
	if no_chapters[book] then
		chapter_only = false
		s_chap = 1
		s_vers = s_ref[2] or s_ref[1] or 1   --verse 3 can be specified as "3" or "1:3"
		e_chap = 1
		e_vers = e_ref[2] or e_ref[1] or 1
	else
		s_chap = s_ref[1] or 1
		s_vers = s_ref[2] or 1
		if e_ref[2] or not s_ref[2] then     --chapter-chapter or chapter(:verse)?-chapter:verse
			e_chap = e_ref[1] or s_chap
		else                                 --chapter:verse-verse
			e_chap = s_chap
		end
		e_vers = e_ref[2] or e_ref[1] or s_vers
	end

	if type(site_book_tbl[site]) == 'table' then
		book = site_book_tbl[site][book] or book
	end

	book = titlecase(book)  --title case looks better at oremus where they display the input

	if site == 'mechon_mamre' then    --special case handling for components of the url
		if not tonumber(s_chap) then  --non-numeric characters were omitted above so this may not be what was entered
			error('Bibleverse error: start chapter should be a number', 0)
		end
		s_chap = string.format('%x', s_chap/10) .. (s_chap % 10)
	elseif site == 'biblica' or site == 'oremus' then
		book = book:gsub('_', '%%20')
	elseif site == 'esv_bible' then
		book = book:gsub('_', '+')
	elseif site == 'bible_hub' then
		book = string.lower(book)
	elseif site == 'usccb' then
		book = book:gsub('_', '')
	end
	if type(site_version_tbl[site]) == 'table' then
		version = site_version_tbl[site][version]
	end

	local v_range
	if chapter_only then
		if e_chap == s_chap then
			v_range = s_chap
		else
			v_range = s_chap .. '–' .. e_chap
		end
	else
		if e_chap == s_chap and e_vers == s_vers then
			v_range = s_chap ..':' .. s_vers
		elseif e_chap == s_chap then
			v_range = s_chap .. ':' .. s_vers .. '–' .. e_vers
		else
			v_range = s_chap .. ':' .. s_vers .. '–' .. e_chap .. ':' .. e_vers
		end
	end

	local url = urlpat:gsub('_%l+', {  --get the components into the url
					_book    = book,
					_schap   = s_chap,
					_svers   = s_vers,
					_echap   = e_chap,
					_evers   = e_vers,
					_vrange  = v_range,
					_version = version,
				})

	local fulllink
	if site == 'wikisource' then  --interwikilink
		fulllink = '[[' .. url .. '|' .. text .. ']]'
	else
		fulllink = '[' .. url .. ' ' .. text .. ']'
	end

	if mainspace then
		if not book_found then
			table.insert(errors, '<span style="color:red">Template:Bibleverse with invalid book</span>[[Category:Pages with Bible book errors]]')
		end
		if version_num then
			table.insert(errors, '[[Category:Pages with numeric Bible version references]]')
		end
	end
	return fulllink .. table.concat(errors)
end

return p