Timeline Chart.fh_lua--[[
@Title: Timeline Chart
@Author: Jane Taubman
@LastUpdated: 30 Nov 2020
@Version: 1.8
@Description:
Creates a web page containing a timeline to display the events of persons life.
Can either create a temporary page to see the currently selected person, or can select a group of people and create a whole set of pages for use on a web site.
Please note the exported pages may not display correctly until loaded on a web site, as local file restrictions on modern browsers prevent the data being loaded.
Uses SIMILE Widgets to display the time line.
V1.1 Fix issue with multiple Families with Parents Deaths
V1.2 Use 2.3.1 SIMILE Widgets
V1.3 Fix issue with year only births, better support for extended character sets.
V1.4 Use HTA files for the quick view to avoid IE and Chrome issues
V1.5 Add Sibling Births and Deaths in the Lifetime
V1.6 Adjust Timeline to Window Size and update page link to new code on Smilie (thanks Mike)
V1.7 Add Spouse Death to Timeline
V1.8 V7 Compatibility
]]
------------------------------------------------------------------- Main Code
require "iuplua"
iup.SetGlobal("CUSTOMQUITMESSAGE","YES")
function main()
settingsFile = fhGetPluginDataFileName()
iAction = iup.Alarm('Select Timeline Action','Please Select your option from the items below:',
'Quick View Current Record',
'Select Records and Create Pages for All')
if iAction == 2 then
outputDir = getOutPutDir()
if outputDir == nil then
return
end
ptrList = fhPromptUserForRecordSel('INDI')
if ptrList == nil then
return
end
end
if iAction == 1 then
ptrList = fhGetCurrentRecordSel('INDI')
if #ptrList >= 1 then
ptrIndi = ptrList[1]:Clone()
ptrList = {ptrIndi}
else
fhMessageBox('Please select a record before running this Plugin')
return
end
end
if iAction == 0 then
return
end
local ptrEvent = fhNewItemPtr()
local ptrFam = fhNewItemPtr()
local ptrChild = fhNewItemPtr()
local ptrDate = fhNewItemPtr()
---------------------------------------------------------- Loop all selected Records
for i,ptrIndi in pairs(ptrList) do
tblPlaces = {}
tblEvents = {}
strEndLabel = 'Death'
birthdate = fhCallBuiltInFunction('EstimatedBirthDate',ptrIndi, 'EARLIEST')
deathdate = fhCallBuiltInFunction('EstimatedDeathDate',ptrIndi, 'LATEST')
if deathdate:IsNull() then
deathdate = fhCallBuiltInFunction('Today')
strEndLabel = 'Today'
end
xmlBirth = buildDate(birthdate)
xmlDeath = buildDate(deathdate)
---------------------------------------------------------- Individual Events
ptrEvent:MoveToFirstChildItem(ptrIndi)
while ptrEvent:IsNotNull() do
if fhIsFact(ptrEvent) then
addFact(ptrEvent)
end
ptrEvent:MoveNext('ANY')
end
--------------------------------------------------------- Family Events
i = 1
ptrFam:MoveTo(ptrIndi,'~.FAMS['..i..']>')
while ptrFam:IsNotNull() do
--- Process Family Events
ptrEvent:MoveToFirstChildItem(ptrFam)
while ptrEvent:IsNotNull() do
if fhIsFact(ptrEvent) then
addFact(ptrEvent,'with '..fhGetItemText(ptrIndi,'~.~SPOU['..i..']>NAME') ..': '..fhGetDisplayText(ptrEvent))
end
ptrEvent:MoveNext('ANY')
end
--- Process Spouses Death
local ptrSpouse = fhGetItemPtr(ptrIndi,'~.~SPOU['..i..']>')
if ptrSpouse:IsNotNull() then
ptrEvent:MoveTo(ptrSpouse,'~.DEAT')
if fhIsFact(ptrEvent) then
addFact(ptrEvent,'Spouse:'..fhGetItemText(ptrSpouse,'~.NAME') ..': '..fhGetDisplayText(ptrEvent))
end
end
--- Process Child Births
c = 1
ptrChild:MoveTo(ptrFam,'~.CHIL['..c..']>')
while ptrChild:IsNotNull() do
ptrEvent:MoveTo(ptrChild,'~.BIRT')
if fhIsFact(ptrEvent) then
addFact(ptrEvent,'Child: '..fhGetItemText(ptrChild,'~.NAME') ..' '..fhGetDisplayText(ptrEvent))
end
-- Death of Child before Parent
ptrEvent:MoveTo(ptrChild,'~.DEAT')
if fhIsFact(ptrEvent) then
ptrDate:MoveTo(ptrEvent,'~.DATE')
ChildDeath = fhGetValueAsDate(ptrDate)
if deathdate:Compare(ChildDeath:GetDatePt1()) > 0 then
addFact(ptrEvent,'Child: '..fhGetItemText(ptrChild,'~.NAME') ..' '..fhGetDisplayText(ptrEvent))
end
end
c= c + 1
ptrChild:MoveTo(ptrFam,'~.CHIL['..c..']>')
end
i = i + 1
ptrFam:MoveTo(ptrIndi,'~.FAMS['..i..']>')
end
-- Get Parents Deaths
i = 1
ptrFam:MoveTo(ptrIndi,'~.FAMC['..i..']>')
while ptrFam:IsNotNull() do
ptrParent = fhGetItemPtr(ptrFam,'~.HUSB>')
ptrEvent:MoveTo(ptrParent,'~.DEAT')
if fhIsFact(ptrEvent) then
addFact(ptrEvent,'Father:'..fhGetItemText(ptrParent,'~.NAME') ..': '..fhGetDisplayText(ptrEvent))
end
ptrParent = fhGetItemPtr(ptrFam,'~.WIFE>')
ptrEvent:MoveTo(ptrParent,'~.DEAT')
if fhIsFact(ptrEvent) then
addFact(ptrEvent,'Mother:'..fhGetItemText(ptrParent,'~.NAME') ..': '..fhGetDisplayText(ptrEvent))
end
-- Sibling Births and Deaths in Lifetime
c = 1
ptrChild:MoveTo(ptrFam,'~.CHIL['..c..']>')
while ptrChild:IsNotNull() do
if not(ptrChild:IsSame(ptrIndi)) then
-- Birth of Younger Sibling
ptrEvent:MoveTo(ptrChild,'~.BIRT')
if fhIsFact(ptrEvent) then
ptrDate:MoveTo(ptrEvent,'~.DATE')
local sibBirthDate = fhGetValueAsDate(ptrDate)
if birthdate:Compare(sibBirthDate:GetDatePt1()) < 0 then
addFact(ptrEvent,'Sibling: '..fhGetItemText(ptrChild,'~.NAME') ..' '..fhGetDisplayText(ptrEvent))
end
end
-- Death of Sibling before Individual
ptrEvent:MoveTo(ptrChild,'~.DEAT')
if fhIsFact(ptrEvent) then
ptrDate:MoveTo(ptrEvent,'~.DATE')
ChildDeath = fhGetValueAsDate(ptrDate)
if deathdate:Compare(ChildDeath:GetDatePt1()) > 0 and birthdate:Compare(ChildDeath:GetDatePt1()) < 0 then
addFact(ptrEvent,'Sibling: '..fhGetItemText(ptrChild,'~.NAME') ..' '..fhGetDisplayText(ptrEvent))
end
end
end
c= c + 1
ptrChild:MoveTo(ptrFam,'~.CHIL['..c..']>')
end
-- End Siblings
i = i + 1
ptrFam:MoveTo(ptrIndi,'~.FAMC['..i..']>')
end
strEndLabel = 'Death'
birthdate = fhCallBuiltInFunction('EstimatedBirthDate',ptrIndi, 'EARLIEST')
deathdate = fhCallBuiltInFunction('EstimatedDeathDate',ptrIndi, 'LATEST')
if deathdate:IsNull() then
deathdate = fhCallBuiltInFunction('Today')
strEndLabel = 'Today'
end
xmlBirth = buildDate(birthdate,'min')
xmlDeath = buildDate(deathdate,'max')
------------------------------------ Build Time Line XML for Html
strEvents = ''
xmlData = '\n'
simpleSort2dDate(tblEvents,2)
for i,event in pairs(tblEvents) do
strEvents = strEvents..''..event[1]..' \n'
xmlLine = strEventTemplate
if not(event[2]:IsNull()) then
startDate = event[2]:GetDatePt1()
endDate = event[2]:GetDatePt2()
xmlStart = buildDate(startDate)
xmlLine = xmlLine:gsubplain('{start}',xmlStart)
if endDate:IsNull() then
xmlLine = xmlLine:gsubplain('end="{end}"',' ')
else
xmlEnd = buildDate(endDate)
xmlLine = xmlLine:gsubplain('{end}',xmlEnd)
end
xmlLine = xmlLine:gsubplain('{note}',StrHTML_Encode(event[4]))
xmlLine = xmlLine:gsubplain('{title}',StrHTML_Encode(event[1]))
xmlData = xmlData..xmlLine
end
end
xmlData = xmlData..''
----------------------------------- Output Web Page and XML file
if #tblEvents > 0 then
htmlPage = htmlMasterPage
htmlPage = htmlPage:gsubplain('{name}',fhGetDisplayText(ptrIndi))
htmlPage = htmlPage:gsubplain('{ref}',fhGetRecordId(ptrIndi))
htmlPage = htmlPage:gsubplain('{title}','The life of '..fhGetDisplayText(ptrIndi))
htmlPage = htmlPage:gsubplain('{startdate}',xmlBirth)
htmlPage = htmlPage:gsubplain('{birthyear}',birthdate:GetYear())
htmlPage = htmlPage:gsubplain('{enddate}',xmlDeath)
htmlPage = htmlPage:gsubplain('{endlabel}',strEndLabel)
if outputDir == nil then
-- Create Temp Filename
filename = os.getenv('TEMP')..'\\timeline.hta'
xmlfile = os.getenv('TEMP')..'\\timeline.xml'
xmlshort = 'timeline.xml'
else
filename = outputDir..'\\timeI'..fhGetRecordId(ptrIndi)..'.html'
xmlfile = outputDir..'\\timeI'..fhGetRecordId(ptrIndi)..'.xml'
xmlshort = 'timeI'..fhGetRecordId(ptrIndi)..'.xml'
end
htmlPage = htmlPage:gsubplain('{xmlfilename}',xmlshort)
savestringtofile(htmlPage,filename)
savestringtofile(xmlData,xmlfile)
if #ptrList == 1 then
bOK, iErrorCode, strErrorText = fhShellExecute(filename)
if not(bOK) then
fhMessageBox(strErrorText..' ('..iErrorCode..')')
end
end
else
fhMessageBox('No Events with Places found for '..fhGetDisplayText(ptrIndi)..' No Map Created')
end
end
if #ptrList > 1 then
bOK, iErrorCode, strErrorText = fhShellExecute(outputDir)
if not(bOK) then
fhMessageBox(strErrorText..' ('..iErrorCode..')')
end
end
end
------------------------------------------------------------- Functions
function string.gsubplain (str,sSearch,sReplace)
str = str:gsub(sSearch, function() return sReplace end)
return str
end
function dumptable(tbl)
-- Function dumptable
-- Debug Dump Table to Output Pane
-- params tbl
for i,d in pairs(tbl) do
print(i,d)
end
end
function StrHTML_Encode(strHTML)
-- Encode HTML symbols into &...; escape sequences --
-- "\n" newline is encoded as "
" tag, which must be unencoded from "<br>" to "
"
local tblEncodings = {
{"\n","
"}, {"%c"," "}, {"&","&"}, {"<","<"}, {">",">"}, {"\'","'"},{"\"","""},
{"€","€"}, {"™","™"},{"¢","¢"}, {"¤","¤"},{"¥","¥"}, {"<br>","
"},
{"["..string.char(127).."-"..string.char(255).."]",
function (char)
return ""..string.byte(char)..";"
end
},
}
if strHTML then
for i, tblEncode in ipairs(tblEncodings) do
strHTML = strHTML:gsub(tblEncode[1],tblEncode[2])
end
else
strHTML = ""
end
return strHTML
end -- function StrHTML_Encode
function savestringtofile(str,filename)
------------------------------------------------ Save String To File
local file = assert(io.open(filename,'w'))
file:write(str)
file:close()
end
function addFact(ptrEvent, strFact)
------------------------------------------------ Add Fact to List
local dtDate = fhNewDate()
local datePtr = fhNewItemPtr()
datePtr:MoveTo(ptrEvent,'~.DATE')
if datePtr:IsNotNull() then
dtDate = fhGetValueAsDate(datePtr)
else
dtDate:SetSimpleDate(fhCallBuiltInFunction('Today'))
end
strPlace = fhGetItemText(ptrEvent,'~.PLAC')
if strFact == nil then
strFact = (fhGetDisplayText(ptrEvent))
end
local strNote = (fhGetItemText(ptrEvent,'~.NOTE2'))
if strNote == nil then
strNote = ' '
end
table.insert(tblEvents,{strFact,dtDate:Clone(),strPlace,strNote})
end
function simpleSort2dDate(tbl,index)
-------------------------------------------------------------------- Sort Event Table by Date
table.sort(tbl,function(a, b) return a[index]:Compare(b[index]) == -1 end)
end
function getOutPutDir()
-------------------------------------------------------------------- Select Directory
local dir = fhGetContextInfo('CI_PROJECT_PUBLIC_FOLDER')
filedlg = iup.filedlg{dialogtype = "DIR", title = "Please select destination directory", DIRECTORY=dir}
-- Shows file dialog in the center of the screen
filedlg:popup (iup.ANYWHERE, iup.ANYWHERE)
-- Gets file dialog status
status = filedlg.status
if status == "-1" then
iup.Message("Timeline Chart","Cancelled")
return nil
end
return filedlg.value
end
function buildDate(datept,type)
----------------------------------------------------------- Build Date
if type == nil then
type = 'min'
end
local strDate = ''
tblMonthName = {'Jan','Feb','Mar','Apr','May','June','Jul','Aug','Sep','Oct','Nov','Dec'}
local iMonth = datept:GetMonth()
local iDay = datept:GetDay()
local iYear = datept:GetYear()
if iMonth == 0 then
if type == 'min' then
iMonth = 1
else
iMonth = 12
end
end
if iDay == 0 then
if type == 'min' then
iDay = 1
else
iDay = 28
end
end
if iMonth > 0 and iDay > 0 then
strDate = strDate..tblMonthName[iMonth]
if iDay > 0 then
strDate = strDate..' '..iDay
end
strDate = strDate..' '..iYear
else
strDate = iYear
end
return strDate
end
------------------------------------------------ Templates
htmlMasterPage = [[
{title}
The Life of {name}
]]
strEventTemplate = '{note} \n'
------------------------------------------------------------- MonthNames
tblMonthName = {'Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'}
--------------------------------------------------- Call Main Function
main()
--[[
@Title: Timeline Chart
@Author: Jane Taubman
@LastUpdated: 30 Nov 2020
@Version: 1.8
@Description:
Creates a web page containing a timeline to display the events of persons life.
Can either create a temporary page to see the currently selected person, or can select a group of people and create a whole set of pages for use on a web site.
Please note the exported pages may not display correctly until loaded on a web site, as local file restrictions on modern browsers prevent the data being loaded.
Uses SIMILE Widgets to display the time line.
V1.1 Fix issue with multiple Families with Parents Deaths
V1.2 Use 2.3.1 SIMILE Widgets
V1.3 Fix issue with year only births, better support for extended character sets.
V1.4 Use HTA files for the quick view to avoid IE and Chrome issues
V1.5 Add Sibling Births and Deaths in the Lifetime
V1.6 Adjust Timeline to Window Size and update page link to new code on Smilie (thanks Mike)
V1.7 Add Spouse Death to Timeline
V1.8 V7 Compatibility
]]
------------------------------------------------------------------- Main Code
require "iuplua"
iup.SetGlobal("CUSTOMQUITMESSAGE","YES")
function main()
settingsFile = fhGetPluginDataFileName()
iAction = iup.Alarm('Select Timeline Action','Please Select your option from the items below:',
'Quick View Current Record',
'Select Records and Create Pages for All')
if iAction == 2 then
outputDir = getOutPutDir()
if outputDir == nil then
return
end
ptrList = fhPromptUserForRecordSel('INDI')
if ptrList == nil then
return
end
end
if iAction == 1 then
ptrList = fhGetCurrentRecordSel('INDI')
if #ptrList >= 1 then
ptrIndi = ptrList[1]:Clone()
ptrList = {ptrIndi}
else
fhMessageBox('Please select a record before running this Plugin')
return
end
end
if iAction == 0 then
return
end
local ptrEvent = fhNewItemPtr()
local ptrFam = fhNewItemPtr()
local ptrChild = fhNewItemPtr()
local ptrDate = fhNewItemPtr()
---------------------------------------------------------- Loop all selected Records
for i,ptrIndi in pairs(ptrList) do
tblPlaces = {}
tblEvents = {}
strEndLabel = 'Death'
birthdate = fhCallBuiltInFunction('EstimatedBirthDate',ptrIndi, 'EARLIEST')
deathdate = fhCallBuiltInFunction('EstimatedDeathDate',ptrIndi, 'LATEST')
if deathdate:IsNull() then
deathdate = fhCallBuiltInFunction('Today')
strEndLabel = 'Today'
end
xmlBirth = buildDate(birthdate)
xmlDeath = buildDate(deathdate)
---------------------------------------------------------- Individual Events
ptrEvent:MoveToFirstChildItem(ptrIndi)
while ptrEvent:IsNotNull() do
if fhIsFact(ptrEvent) then
addFact(ptrEvent)
end
ptrEvent:MoveNext('ANY')
end
--------------------------------------------------------- Family Events
i = 1
ptrFam:MoveTo(ptrIndi,'~.FAMS['..i..']>')
while ptrFam:IsNotNull() do
--- Process Family Events
ptrEvent:MoveToFirstChildItem(ptrFam)
while ptrEvent:IsNotNull() do
if fhIsFact(ptrEvent) then
addFact(ptrEvent,'with '..fhGetItemText(ptrIndi,'~.~SPOU['..i..']>NAME') ..': '..fhGetDisplayText(ptrEvent))
end
ptrEvent:MoveNext('ANY')
end
--- Process Spouses Death
local ptrSpouse = fhGetItemPtr(ptrIndi,'~.~SPOU['..i..']>')
if ptrSpouse:IsNotNull() then
ptrEvent:MoveTo(ptrSpouse,'~.DEAT')
if fhIsFact(ptrEvent) then
addFact(ptrEvent,'Spouse:'..fhGetItemText(ptrSpouse,'~.NAME') ..': '..fhGetDisplayText(ptrEvent))
end
end
--- Process Child Births
c = 1
ptrChild:MoveTo(ptrFam,'~.CHIL['..c..']>')
while ptrChild:IsNotNull() do
ptrEvent:MoveTo(ptrChild,'~.BIRT')
if fhIsFact(ptrEvent) then
addFact(ptrEvent,'Child: '..fhGetItemText(ptrChild,'~.NAME') ..' '..fhGetDisplayText(ptrEvent))
end
-- Death of Child before Parent
ptrEvent:MoveTo(ptrChild,'~.DEAT')
if fhIsFact(ptrEvent) then
ptrDate:MoveTo(ptrEvent,'~.DATE')
ChildDeath = fhGetValueAsDate(ptrDate)
if deathdate:Compare(ChildDeath:GetDatePt1()) > 0 then
addFact(ptrEvent,'Child: '..fhGetItemText(ptrChild,'~.NAME') ..' '..fhGetDisplayText(ptrEvent))
end
end
c= c + 1
ptrChild:MoveTo(ptrFam,'~.CHIL['..c..']>')
end
i = i + 1
ptrFam:MoveTo(ptrIndi,'~.FAMS['..i..']>')
end
-- Get Parents Deaths
i = 1
ptrFam:MoveTo(ptrIndi,'~.FAMC['..i..']>')
while ptrFam:IsNotNull() do
ptrParent = fhGetItemPtr(ptrFam,'~.HUSB>')
ptrEvent:MoveTo(ptrParent,'~.DEAT')
if fhIsFact(ptrEvent) then
addFact(ptrEvent,'Father:'..fhGetItemText(ptrParent,'~.NAME') ..': '..fhGetDisplayText(ptrEvent))
end
ptrParent = fhGetItemPtr(ptrFam,'~.WIFE>')
ptrEvent:MoveTo(ptrParent,'~.DEAT')
if fhIsFact(ptrEvent) then
addFact(ptrEvent,'Mother:'..fhGetItemText(ptrParent,'~.NAME') ..': '..fhGetDisplayText(ptrEvent))
end
-- Sibling Births and Deaths in Lifetime
c = 1
ptrChild:MoveTo(ptrFam,'~.CHIL['..c..']>')
while ptrChild:IsNotNull() do
if not(ptrChild:IsSame(ptrIndi)) then
-- Birth of Younger Sibling
ptrEvent:MoveTo(ptrChild,'~.BIRT')
if fhIsFact(ptrEvent) then
ptrDate:MoveTo(ptrEvent,'~.DATE')
local sibBirthDate = fhGetValueAsDate(ptrDate)
if birthdate:Compare(sibBirthDate:GetDatePt1()) < 0 then
addFact(ptrEvent,'Sibling: '..fhGetItemText(ptrChild,'~.NAME') ..' '..fhGetDisplayText(ptrEvent))
end
end
-- Death of Sibling before Individual
ptrEvent:MoveTo(ptrChild,'~.DEAT')
if fhIsFact(ptrEvent) then
ptrDate:MoveTo(ptrEvent,'~.DATE')
ChildDeath = fhGetValueAsDate(ptrDate)
if deathdate:Compare(ChildDeath:GetDatePt1()) > 0 and birthdate:Compare(ChildDeath:GetDatePt1()) < 0 then
addFact(ptrEvent,'Sibling: '..fhGetItemText(ptrChild,'~.NAME') ..' '..fhGetDisplayText(ptrEvent))
end
end
end
c= c + 1
ptrChild:MoveTo(ptrFam,'~.CHIL['..c..']>')
end
-- End Siblings
i = i + 1
ptrFam:MoveTo(ptrIndi,'~.FAMC['..i..']>')
end
strEndLabel = 'Death'
birthdate = fhCallBuiltInFunction('EstimatedBirthDate',ptrIndi, 'EARLIEST')
deathdate = fhCallBuiltInFunction('EstimatedDeathDate',ptrIndi, 'LATEST')
if deathdate:IsNull() then
deathdate = fhCallBuiltInFunction('Today')
strEndLabel = 'Today'
end
xmlBirth = buildDate(birthdate,'min')
xmlDeath = buildDate(deathdate,'max')
------------------------------------ Build Time Line XML for Html
strEvents = ''
xmlData = '\n'
simpleSort2dDate(tblEvents,2)
for i,event in pairs(tblEvents) do
strEvents = strEvents..''..event[1]..' \n'
xmlLine = strEventTemplate
if not(event[2]:IsNull()) then
startDate = event[2]:GetDatePt1()
endDate = event[2]:GetDatePt2()
xmlStart = buildDate(startDate)
xmlLine = xmlLine:gsubplain('{start}',xmlStart)
if endDate:IsNull() then
xmlLine = xmlLine:gsubplain('end="{end}"',' ')
else
xmlEnd = buildDate(endDate)
xmlLine = xmlLine:gsubplain('{end}',xmlEnd)
end
xmlLine = xmlLine:gsubplain('{note}',StrHTML_Encode(event[4]))
xmlLine = xmlLine:gsubplain('{title}',StrHTML_Encode(event[1]))
xmlData = xmlData..xmlLine
end
end
xmlData = xmlData..''
----------------------------------- Output Web Page and XML file
if #tblEvents > 0 then
htmlPage = htmlMasterPage
htmlPage = htmlPage:gsubplain('{name}',fhGetDisplayText(ptrIndi))
htmlPage = htmlPage:gsubplain('{ref}',fhGetRecordId(ptrIndi))
htmlPage = htmlPage:gsubplain('{title}','The life of '..fhGetDisplayText(ptrIndi))
htmlPage = htmlPage:gsubplain('{startdate}',xmlBirth)
htmlPage = htmlPage:gsubplain('{birthyear}',birthdate:GetYear())
htmlPage = htmlPage:gsubplain('{enddate}',xmlDeath)
htmlPage = htmlPage:gsubplain('{endlabel}',strEndLabel)
if outputDir == nil then
-- Create Temp Filename
filename = os.getenv('TEMP')..'\\timeline.hta'
xmlfile = os.getenv('TEMP')..'\\timeline.xml'
xmlshort = 'timeline.xml'
else
filename = outputDir..'\\timeI'..fhGetRecordId(ptrIndi)..'.html'
xmlfile = outputDir..'\\timeI'..fhGetRecordId(ptrIndi)..'.xml'
xmlshort = 'timeI'..fhGetRecordId(ptrIndi)..'.xml'
end
htmlPage = htmlPage:gsubplain('{xmlfilename}',xmlshort)
savestringtofile(htmlPage,filename)
savestringtofile(xmlData,xmlfile)
if #ptrList == 1 then
bOK, iErrorCode, strErrorText = fhShellExecute(filename)
if not(bOK) then
fhMessageBox(strErrorText..' ('..iErrorCode..')')
end
end
else
fhMessageBox('No Events with Places found for '..fhGetDisplayText(ptrIndi)..' No Map Created')
end
end
if #ptrList > 1 then
bOK, iErrorCode, strErrorText = fhShellExecute(outputDir)
if not(bOK) then
fhMessageBox(strErrorText..' ('..iErrorCode..')')
end
end
end
------------------------------------------------------------- Functions
function string.gsubplain (str,sSearch,sReplace)
str = str:gsub(sSearch, function() return sReplace end)
return str
end
function dumptable(tbl)
-- Function dumptable
-- Debug Dump Table to Output Pane
-- params tbl
for i,d in pairs(tbl) do
print(i,d)
end
end
function StrHTML_Encode(strHTML)
-- Encode HTML symbols into &...; escape sequences --
-- "\n" newline is encoded as "
" tag, which must be unencoded from "<br>" to "
"
local tblEncodings = {
{"\n","
"}, {"%c"," "}, {"&","&"}, {"<","<"}, {">",">"}, {"\'","'"},{"\"","""},
{"€","€"}, {"™","™"},{"¢","¢"}, {"¤","¤"},{"¥","¥"}, {"<br>","
"},
{"["..string.char(127).."-"..string.char(255).."]",
function (char)
return ""..string.byte(char)..";"
end
},
}
if strHTML then
for i, tblEncode in ipairs(tblEncodings) do
strHTML = strHTML:gsub(tblEncode[1],tblEncode[2])
end
else
strHTML = ""
end
return strHTML
end -- function StrHTML_Encode
function savestringtofile(str,filename)
------------------------------------------------ Save String To File
local file = assert(io.open(filename,'w'))
file:write(str)
file:close()
end
function addFact(ptrEvent, strFact)
------------------------------------------------ Add Fact to List
local dtDate = fhNewDate()
local datePtr = fhNewItemPtr()
datePtr:MoveTo(ptrEvent,'~.DATE')
if datePtr:IsNotNull() then
dtDate = fhGetValueAsDate(datePtr)
else
dtDate:SetSimpleDate(fhCallBuiltInFunction('Today'))
end
strPlace = fhGetItemText(ptrEvent,'~.PLAC')
if strFact == nil then
strFact = (fhGetDisplayText(ptrEvent))
end
local strNote = (fhGetItemText(ptrEvent,'~.NOTE2'))
if strNote == nil then
strNote = ' '
end
table.insert(tblEvents,{strFact,dtDate:Clone(),strPlace,strNote})
end
function simpleSort2dDate(tbl,index)
-------------------------------------------------------------------- Sort Event Table by Date
table.sort(tbl,function(a, b) return a[index]:Compare(b[index]) == -1 end)
end
function getOutPutDir()
-------------------------------------------------------------------- Select Directory
local dir = fhGetContextInfo('CI_PROJECT_PUBLIC_FOLDER')
filedlg = iup.filedlg{dialogtype = "DIR", title = "Please select destination directory", DIRECTORY=dir}
-- Shows file dialog in the center of the screen
filedlg:popup (iup.ANYWHERE, iup.ANYWHERE)
-- Gets file dialog status
status = filedlg.status
if status == "-1" then
iup.Message("Timeline Chart","Cancelled")
return nil
end
return filedlg.value
end
function buildDate(datept,type)
----------------------------------------------------------- Build Date
if type == nil then
type = 'min'
end
local strDate = ''
tblMonthName = {'Jan','Feb','Mar','Apr','May','June','Jul','Aug','Sep','Oct','Nov','Dec'}
local iMonth = datept:GetMonth()
local iDay = datept:GetDay()
local iYear = datept:GetYear()
if iMonth == 0 then
if type == 'min' then
iMonth = 1
else
iMonth = 12
end
end
if iDay == 0 then
if type == 'min' then
iDay = 1
else
iDay = 28
end
end
if iMonth > 0 and iDay > 0 then
strDate = strDate..tblMonthName[iMonth]
if iDay > 0 then
strDate = strDate..' '..iDay
end
strDate = strDate..' '..iYear
else
strDate = iYear
end
return strDate
end
------------------------------------------------ Templates
htmlMasterPage = [[
{title}
The Life of {name}
]]
strEventTemplate = '{note} \n'
------------------------------------------------------------- MonthNames
tblMonthName = {'Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'}
--------------------------------------------------- Call Main Function
main()Source:Timeline-Chart.fh_lua