Comparing sensitive data, confidential files or internal emails?

Most legal and privacy policies prohibit uploading sensitive data online. Diffchecker Desktop ensures your confidential information never leaves your computer. Work offline and compare documents securely.

Untitled diff

Created Diff never expires
24 removals
602 lines
18 additions
594 lines
function Initialize()
function Initialize()
-- SET UPDATE DIVIDER
-- SET UPDATE DIVIDER
SKIN:Bang('!SetOption', SELF:GetName(), 'UpdateDivider', -1)
SKIN:Bang('!SetOption', SELF:GetName(), 'UpdateDivider', -1)
-- This script never needs to update on a schedule. It should only
-- This script never needs to update on a schedule. It should only
-- update when it gets a "Refresh" command from WebParser.
-- update when it gets a "Refresh" command from WebParser.
-- CREATE MAIN DATABASE
-- CREATE MAIN DATABASE
Feeds = {}
Feeds = {}
-- CREATE TYPE MATCHING PATTERNS AND FORMATTING FUNCTIONS
-- CREATE TYPE MATCHING PATTERNS AND FORMATTING FUNCTIONS
DefineTypes()
DefineTypes()
-- GET MEASURE NAMES
-- GET MEASURE NAMES
local AllMeasureNames = SELF:GetOption('MeasureName', '')
local AllMeasureNames = SELF:GetOption('MeasureName', '')
for MeasureName in AllMeasureNames:gmatch('[^%|]+') do
for MeasureName in AllMeasureNames:gmatch('[^%|]+') do
table.insert(Feeds, {
table.insert(Feeds, {
Measure = SKIN:GetMeasure(MeasureName),
Measure = SKIN:GetMeasure(MeasureName),
MeasureName = MeasureName,
MeasureName = MeasureName,
Raw = nil,
Raw = nil,
Type = nil,
Type = nil,
Title = nil,
Title = nil,
Link = nil,
Link = nil,
Error = nil
Error = nil
})
})
end
end
-- MODULES
-- MODULES
EventFile_Initialize()
EventFile_Initialize()
HistoryFile_Initialize()
HistoryFile_Initialize()
-- SET STARTING FEED
-- SET STARTING FEED
f = f or 1
f = f or 1
-- SET USER INPUT
-- SET USER INPUT
UserInput = false
UserInput = false
-- Used to detect when an item has been marked as read.
-- Used to detect when an item has been marked as read.
end
end
function Update()
function Update()
Input()
Input()
return Output()
return Output()
end
end
-----------------------------------------------------------------------
-----------------------------------------------------------------------
-- INPUT
-- INPUT
function compareTime(a,b)
return a.Date < b.Date
end
function Input(a)
function Input(a)
local f = a or f
local f = a or f
local Raw = Feeds[f].Measure:GetStringValue()
local Raw = Feeds[f].Measure:GetStringValue()
if Raw == '' then
if Raw == '' then
Feeds[f].Error = {
Feeds[f].Error = {
Description = 'Waiting for data from WebParser.',
Description = 'Waiting for data from WebParser.',
Title = 'Loading...',
Title = 'Loading...',
Link = 'http://enigma.kaelri.com/support'
Link = 'http://enigma.kaelri.com/support'
}
}
return false
return false
elseif (Raw ~= Feeds[f].Raw) or UserInput then
elseif (Raw ~= Feeds[f].Raw) or UserInput then
Feeds[f].Raw = Raw
Feeds[f].Raw = Raw
-- DETERMINE FEED FORMAT AND CONTENTS
-- DETERMINE FEED FORMAT AND CONTENTS
local t = IdentifyType(Raw)
local t = IdentifyType(Raw)
if not t then
if not t then
Feeds[f].Error = {
Feeds[f].Error = {
Description = 'Could not identify a valid feed format.',
Description = 'Could not identify a valid feed format.',
Title = 'Invalid Feed Format',
Title = 'Invalid Feed Format',
Link = 'http://enigma.kaelri.com/support'
Link = 'http://enigma.kaelri.com/support'
}
}
return false
return false
else
else
Feeds[f].Type = t
Feeds[f].Type = t
end
end
-- MAKE SYNTAX PRETTIER
-- MAKE SYNTAX PRETTIER
local Type = Types[t]
local Type = Types[t]
-- GET NEW DATA
-- GET NEW DATA
Feeds[f].Title = Raw:match('<title.->(.-)</title>') or 'Untitled'
Feeds[f].Title = Raw:match('<title.->(.-)</title>') or 'Untitled'
Feeds[f].Link = Raw:match(Type.MatchLink) or nil
Feeds[f].Link = Raw:match(Type.MatchLink) or nil
local Items = {}
local Items = {}
for RawItem in Raw:gmatch(Type.MatchItem) do
for RawItem in Raw:gmatch(Type.MatchItem) do
local Item = {}
local Item = {}
-- MATCH RAW DATA
-- MATCH RAW DATA
Item.Unread = 1
Item.Unread = 1
if (t == 'GoogleCalendar') then
if (t == 'GoogleCalendar') then
Item.Title = RawItem:match('SUMMARY:(.-)\n') or nil
Item.Title = RawItem:match('SUMMARY:(.-)\n') or nil
if (string.len(Item.Title) == 1) then
if (string.len(Item.Title) == 1) then
Item.Title = RawItem:match('DESCRIPTION:(.-)\n') or nil
Item.Title = RawItem:match('DESCRIPTION:(.-)\n') or nil
end
end
Item.Title = Item.Title:gsub('\\', '')
Item.Title = Item.Title:gsub('\\', '')
Item.Date = RawItem:match(Type.MatchRecurringDate) or RawItem:match(Type.MatchItemDate)
Item.Date = RawItem:match(Type.MatchRecurringDate) or RawItem:match(Type.MatchItemDate)
else
else
Item.Title = RawItem:match('<title.->(.-)</title>') or nil
Item.Title = RawItem:match('<title.->(.-)</title>') or nil
Item.Date = RawItem:match(Type.MatchItemDate) or nil
Item.Date = RawItem:match(Type.MatchItemDate) or nil
end
end
Item.Link = RawItem:match(Type.MatchItemLink) or nil
Item.Link = RawItem:match(Type.MatchItemLink) or nil
Item.Desc = RawItem:match(Type.MatchItemDesc) or nil
Item.Desc = RawItem:match(Type.MatchItemDesc) or nil
Item.ID = RawItem:match(Type.MatchItemID) or Item.Link or Item.Title or Item.Desc or Item.Date
Item.ID = RawItem:match(Type.MatchItemID) or Item.Link or Item.Title or Item.Desc or Item.Date
-- ADDITIONAL PARSING
-- ADDITIONAL PARSING
if (not Item.Title) or (Item.Title == '') then
if (not Item.Title) or (Item.Title == '') then
Item.Title = 'Untitled'
Item.Title = 'Untitled'
end
end
if Item.Desc then
if Item.Desc then
Item.Desc = Item.Desc:gsub('<.->', '')
Item.Desc = Item.Desc:gsub('<.->', '')
Item.Desc = Item.Desc:gsub('%s%s+', ' ')
Item.Desc = Item.Desc:gsub('%s%s+', ' ')
end
end
Item.Date, Item.AllDay, Item.RealDate = IdentifyDate(Item.Date, t)
Item.Date, Item.AllDay, Item.RealDate = IdentifyDate(Item.Date, t)
table.insert(Items, Item)
table.insert(Items, Item)
end
end
table.sort(Items, compareTime)
-- IDENTIFY DUPLICATES
-- IDENTIFY DUPLICATES
for i, OldItem in ipairs(Feeds[f]) do
for i, OldItem in ipairs(Feeds[f]) do
for j, NewItem in ipairs(Items) do
for j, NewItem in ipairs(Items) do
if NewItem.ID == OldItem.ID then
if NewItem.ID == OldItem.ID then
Feeds[f][i].Match = j
Feeds[f][i].Match = j
Items[j].Unread = OldItem.Unread
Items[j].Unread = OldItem.Unread
if NewItem.RealDate == 0 then
if NewItem.RealDate == 0 then
Items[j].Date = OldItem.Date
Items[j].Date = OldItem.Date
Items[j].AllDay = OldItem.AllDay
Items[j].AllDay = OldItem.AllDay
end
end
end
end
end
end
end
end
-- CLEAR DUPLICATES OR ALL HISTORY
-- CLEAR DUPLICATES OR ALL HISTORY
local KeepOldItems = SELF:GetNumberOption('KeepOldItems', 0)
local KeepOldItems = SELF:GetNumberOption('KeepOldItems', 0)
if (KeepOldItems == 1) and Type.MergeItems then
if (KeepOldItems == 1) and Type.MergeItems then
for i = #Feeds[f], 1, -1 do
for i = #Feeds[f], 1, -1 do
if Feeds[f][i].Match then
if Feeds[f][i].Match then
table.remove(Feeds[f], i)
table.remove(Feeds[f], i)
end
end
end
end
else
else
for i = 1, #Feeds[f] do
for i = 1, #Feeds[f] do
table.remove(Feeds[f])
table.remove(Feeds[f])
end
end
end
end
-- ADD NEW ITEMS
-- ADD NEW ITEMS
for i = #Items, 1, -1 do
for i = #Items, 1, -1 do
if Items[i] then
if Items[i] then
table.insert(Feeds[f], 1, Items[i])
if (Items[i].Date > os.time()) then
end
table.insert(Feeds[f], 1, Items[i])
end
end
end
end
-- CHECK NUMBER OF ITEMS
-- CHECK NUMBER OF ITEMS
local MaxItems = SELF:GetNumberOption('MaxItems', nil)
local MaxItems = SELF:GetNumberOption('MaxItems', nil)
local MaxItems = (MaxItems > 0) and MaxItems or nil
local MaxItems = (MaxItems > 0) and MaxItems or nil
if #Feeds[f] == 0 then
if #Feeds[f] == 0 then
Feeds[f].Error = {
Feeds[f].Error = {
Description = 'No items found.',
Description = 'No items found.',
Title = Feeds[f]['Title'],
Title = Feeds[f]['Title'],
Link = Feeds[f]['Link']
Link = Feeds[f]['Link']
}
}
return false
return false
elseif MaxItems and (#Feeds[f] > MaxItems) then
elseif MaxItems and (#Feeds[f] > MaxItems) then
for i = #Feeds[f], (MaxItems + 1), -1 do
for i = #Feeds[f], (MaxItems + 1), -1 do
table.remove(Feeds[f])
table.remove(Feeds[f])
end
end
end
end
-- MODULES
-- MODULES
EventFile_Update(f)
EventFile_Update(f)
HistoryFile_Update(f)
HistoryFile_Update(f)
-- CLEAR ERRORS FROM PREVIOUS UPDATE
-- CLEAR ERRORS FROM PREVIOUS UPDATE
Feeds[f].Error = nil
Feeds[f].Error = nil
-- RESET USER INPUT
-- RESET USER INPUT
UserInput = false
UserInput = false
end
end
return true
return true
end
end
-----------------------------------------------------------------------
-----------------------------------------------------------------------
-- OUTPUT
-- OUTPUT
function Output()
function Output()
local Queue = {}
local Queue = {}
-- MAKE SYNTAX PRETTIER
-- MAKE SYNTAX PRETTIER
local Feed = Feeds[f]
local Feed = Feeds[f]
local Type = Types[Feed.Type]
local Type = Types[Feed.Type]
local Error = Feed.Error
local Error = Feed.Error
-- BUILD QUEUE
-- BUILD QUEUE
Queue['CurrentFeed'] = f
Queue['CurrentFeed'] = f
Queue['NumberOfItems'] = #Feed
Queue['NumberOfItems'] = #Feed
-- CHECK FOR INPUT ERRORS
-- CHECK FOR INPUT ERRORS
local MinItems = SELF:GetNumberOption('MinItems', 0)
local MinItems = SELF:GetNumberOption('MinItems', 0)
local Timestamp = SELF:GetOption('Timestamp', '%I.%M %p on %d %B %Y')
local Timestamp = SELF:GetOption('Timestamp', '%I.%M %p on %d %B %Y')
if Error then
if Error then
-- ERROR; QUEUE MESSAGES
-- ERROR; QUEUE MESSAGES
Queue['FeedTitle'] = Error.Title
Queue['FeedTitle'] = Error.Title
Queue['FeedLink'] = Error.Link
Queue['FeedLink'] = Error.Link
Queue['Item1Title'] = Error.Description
Queue['Item1Title'] = Error.Description
Queue['Item1Link'] = Error.Link
Queue['Item1Link'] = Error.Link
Queue['Item1Desc'] = ''
Queue['Item1Desc'] = ''
Queue['Item1Date'] = ''
Queue['Item1Date'] = ''
Queue['Item1Unread'] = 0
Queue['Item1Unread'] = 0
for i = 2, MinItems do
for i = 2, MinItems do
Queue['Item'..i..'Title'] = ''
Queue['Item'..i..'Title'] = ''
Queue['Item'..i..'Link'] = ''
Queue['Item'..i..'Link'] = ''
Queue['Item'..i..'Desc'] = ''
Queue['Item'..i..'Desc'] = ''
Queue['Item'..i..'Date'] = ''
Queue['Item'..i..'Date'] = ''
Queue['Item'..i..'Unread'] = 0
Queue['Item'..i..'Unread'] = 0
end
end
else
else
-- NO ERROR; QUEUE FEED
-- NO ERROR; QUEUE FEED
Queue['FeedTitle'] = Feed.Title
Queue['FeedTitle'] = Feed.Title
Queue['FeedLink'] = Feed.Link or ''
Queue['FeedLink'] = Feed.Link or ''
for i = 1, math.max(#Feed, MinItems) do
for i = 1, math.max(#Feed, MinItems) do
local Item = Feed[i] or {}
local Item = Feed[i] or {}
Queue['Item'..i..'Title'] = Item.Title or ''
Queue['Item'..i..'Title'] = Item.Title or ''
Queue['Item'..i..'Link'] = Item.Link or Feed.Link or ''
Queue['Item'..i..'Link'] = Item.Link or Feed.Link or ''
Queue['Item'..i..'Desc'] = Item.Desc or ''
Queue['Item'..i..'Desc'] = Item.Desc or ''
Queue['Item'..i..'Unread'] = Item.Unread or ''
Queue['Item'..i..'Unread'] = Item.Unread or ''
Queue['Item'..i..'Date'] = Item.Date and os.date(Timestamp, Item.Date) or ''
Queue['Item'..i..'Date'] = Item.Date and os.date(Timestamp, Item.Date) or ''
end
end
end
end
-- SET VARIABLES
-- SET VARIABLES
local VariablePrefix = SELF:GetOption('VariablePrefix', '')
local VariablePrefix = SELF:GetOption('VariablePrefix', '')
for k, v in pairs(Queue) do
for k, v in pairs(Queue) do
SKIN:Bang('!SetVariable', VariablePrefix..k, v)
SKIN:Bang('!SetVariable', VariablePrefix..k, v)
end
end
-- FINISH ACTION
-- FINISH ACTION
local FinishAction = SELF:GetOption('FinishAction', '')
local FinishAction = SELF:GetOption('FinishAction', '')
if FinishAction ~= '' then
if FinishAction ~= '' then
SKIN:Bang(FinishAction)
SKIN:Bang(FinishAction)
end
end
return Error and Error.Description or 'Finished #'..f..' ('..Feed.MeasureName..'). Name: '..Feed.Title..'. Type: '..Feed.Type..'. Items: '..#Feed..'.'
return Error and Error.Description or 'Finished #'..f..' ('..Feed.MeasureName..'). Name: '..Feed.Title..'. Type: '..Feed.Type..'. Items: '..#Feed..'.'
end
end
-----------------------------------------------------------------------
-----------------------------------------------------------------------
-- EXTERNAL COMMANDS
-- EXTERNAL COMMANDS
function Refresh(a)
function Refresh(a)
a = a and tonumber(a) or f
a = a and tonumber(a) or f
if a == f then
if a == f then
SKIN:Bang('!UpdateMeasure', SELF:GetName())
SKIN:Bang('!UpdateMeasure', SELF:GetName())
else
else
Input(a)
Input(a)
end
end
end
end
function Show(a)
function Show(a)
f = tonumber(a)
f = tonumber(a)
SKIN:Bang('!UpdateMeasure', SELF:GetName())
SKIN:Bang('!UpdateMeasure', SELF:GetName())
end
end
function ShowNext()
function ShowNext()
f = (f % #Feeds) + 1
f = (f % #Feeds) + 1
SKIN:Bang('!UpdateMeasure', SELF:GetName())
SKIN:Bang('!UpdateMeasure', SELF:GetName())
end
end
function ShowPrevious()
function ShowPrevious()
f = (f == 1) and #Feeds or (f - 1)
f = (f == 1) and #Feeds or (f - 1)
SKIN:Bang('!UpdateMeasure', SELF:GetName())
SKIN:Bang('!UpdateMeasure', SELF:GetName())
end
end
function MarkRead(a, b)
function MarkRead(a, b)
b = b and tonumber(b) or f
b = b and tonumber(b) or f
Feeds[b][a].Unread = 0
Feeds[b][a].Unread = 0
UserInput = true
UserInput = true
SKIN:Bang('!UpdateMeasure', SELF:GetName())
SKIN:Bang('!UpdateMeasure', SELF:GetName())
end
end
function MarkUnread(a, b)
function MarkUnread(a, b)
b = b and tonumber(b) or f
b = b and tonumber(b) or f
Feeds[b][a].Unread = 1
Feeds[b][a].Unread = 1
UserInput = true
UserInput = true
SKIN:Bang('!UpdateMeasure', SELF:GetName())
SKIN:Bang('!UpdateMeasure', SELF:GetName())
end
end
function ToggleUnread(a, b)
function ToggleUnread(a, b)
b = b and tonumber(b) or f
b = b and tonumber(b) or f
Feeds[b][a].Unread = 1 - Feeds[b][a].Unread
Feeds[b][a].Unread = 1 - Feeds[b][a].Unread
UserInput = true
UserInput = true
SKIN:Bang('!UpdateMeasure', SELF:GetName())
SKIN:Bang('!UpdateMeasure', SELF:GetName())
end
end
-----------------------------------------------------------------------
-----------------------------------------------------------------------
-- TYPES
-- TYPES
function DefineTypes()
function DefineTypes()
Types = {
Types = {
RSS = {
RSS = {
MatchLink = '<link.->(.-)</link>',
MatchLink = '<link.->(.-)</link>',
MatchItem = '<item.-</item>',
MatchItem = '<item.-</item>',
MatchItemID = '<guid.->(.-)</guid>',
MatchItemID = '<guid.->(.-)</guid>',
MatchItemLink = '<link.->(.-)</link>',
MatchItemLink = '<link.->(.-)</link>',
MatchItemDesc = '<description.->(.-)</description>',
MatchItemDesc = '<description.->(.-)</description>',
MatchItemDate = '<pubDate.->(.-)</pubDate>',
MatchItemDate = '<pubDate.->(.-)</pubDate>',
MergeItems = true,
MergeItems = true,
ParseDate = function(s)
ParseDate = function(s)
local Date = {}
local Date = {}
local MatchTime = '%a%a%a, (%d%d) (%a%a%a) (%d%d%d%d) (%d%d)%:(%d%d)%:(%d%d) (.-)$'
local MatchTime = '%a%a%a, (%d%d) (%a%a%a) (%d%d%d%d) (%d%d)%:(%d%d)%:(%d%d) (.-)$'
local MatchDate = '%a%a%a, (%d%d) (%a%a%a) (%d%d%d%d)$'
local MatchDate = '%a%a%a, (%d%d) (%a%a%a) (%d%d%d%d)$'
if s:match(MatchTime) then
if s:match(MatchTime) then
Date.day, Date.month, Date.year, Date.hour, Date.min, Date.sec, Date.Offset = s:match(MatchTime)
Date.day, Date.month, Date.year, Date.hour, Date.min, Date.sec, Date.Offset = s:match(MatchTime)
elseif s:match(MatchDate) then
elseif s:match(MatchDate) then
Date.day, Date.month, Date.year = s:match(MatchDate)
Date.day, Date.month, Date.year = s:match(MatchDate)
end
end
return (Date.year and Date.month and Date.day) and Date or nil
return (Date.year and Date.month and Date.day) and Date or nil
end
end
},
},
Atom = {
Atom = {
MatchLink = '<link.-href=["\'](.-)["\']',
MatchLink = '<link.-href=["\'](.-)["\']',
MatchItem = '<entry.-</entry>',
MatchItem = '<entry.-</entry>',
MatchItemID = '<id.->(.-)</id>',
MatchItemID = '<id.->(.-)</id>',
MatchItemLink = '<link.-href=["\'](.-)["\']',
MatchItemLink = '<link.-href=["\'](.-)["\']',
MatchItemDesc = '<summary.->(.-)</summary>',
MatchItemDesc = '<summary.->(.-)</summary>',
MatchItemDate = '<updated.->(.-)</updated>',
MatchItemDate = '<updated.->(.-)</updated>',
MergeItems = true,
MergeItems = true,
ParseDate = function(s)
ParseDate = function(s)
local Date = {}
local Date = {}
local MatchTime = '(%d%d%d%d)%-(%d%d)%-(%d%d)T(%d%d)%:(%d%d)%:(%d%d)(.-)$'
local MatchTime = '(%d%d%d%d)%-(%d%d)%-(%d%d)T(%d%d)%:(%d%d)%:(%d%d)(.-)$'
local MatchDate = '(%d%d%d%d)%-(%d%d)%-(%d%d)$'
local MatchDate = '(%d%d%d%d)%-(%d%d)%-(%d%d)$'
if s:match(MatchTime) then
if s:match(MatchTime) then
Date.year, Date.month, Date.day, Date.hour, Date.min, Date.sec, Date.Offset = s:match(MatchTime)
Date.year, Date.month, Date.day, Date.hour, Date.min, Date.sec, Date.Offset = s:match(MatchTime)
elseif s:match(MatchDate) then
elseif s:match(MatchDate) then
Date.year, Date.month, Date.day = s:match(MatchDate)
Date.year, Date.month, Date.day = s:match(MatchDate)
end
end
return Date
return Date
end
end
},
},
GoogleCalendar = {
GoogleCalendar = {
MatchLink = 'UID:(%S+)',
MatchLink = 'UID:(%S+)',
MatchItem = 'BEGIN:VEVENT.-END:VEVENT',
MatchItem = 'BEGIN:VEVENT.-END:VEVENT',
MatchItemID = 'UID:(%S+)',
MatchItemID = 'UID:(%S+)',
MatchItemLink = 'UID:(%S+)',
MatchItemLink = 'UID:(%S+)',
MatchItemDesc = 'DESCRIPTION:(%S+)',
MatchItemDesc = 'DESCRIPTION:(%S+)',
MatchItemDate = 'DTSTART(%S+)',
MatchItemDate = 'DTSTART(%S+)',
MatchRecurringDate = 'RRULE:FREQ=MONTHLY;(%S+)',
MatchRecurringDate = 'RRULE:FREQ=MONTHLY;(%S+)',
MergeItems = false,
MergeItems = false,
ParseDate = function(s)
ParseDate = function(s)
local Date = {}
local Date = {}
local MatchTime = '(%d%d%d%d)(%d%d)(%d%d)T(%d%d)(%d%d)(%d%d)'
local MatchTime = '(%d%d%d%d)(%d%d)(%d%d)T(%d%d)(%d%d)(%d%d)'
local MatchDate = '(%d%d%d%d)(%d%d)(%d%d)'
local MatchDate = '(%d%d%d%d)(%d%d)(%d%d)'
if s:match(MatchTime) then
if s:match(MatchTime) then
Date.year, Date.month, Date.day, Date.hour, Date.min, Date.sec = s:match(MatchTime)
Date.year, Date.month, Date.day, Date.hour, Date.min, Date.sec = s:match(MatchTime)
else
else
Date.year, Date.month, Date.day = s:match(MatchDate)
Date.year, Date.month, Date.day = s:match(MatchDate)
end
end
return Date
return Date
end,
end,
ParseRecurringDate = function(s)
ParseRecurringDate = function(s)
local Date = {}
local Date = {}
if s:match('BYMONTHDAY=(%S+)') then
local MatchUntil = 'UNTIL=(%d%d%d%d)(%d%d)(%d%d)'
if s:match(MatchUntil) then
Date.year, Date.month, Date.day = s:match('(%d%d%d%d)(%d%d)(%d%d)')
elseif s:match('BYMONTHDAY=(%S+)') then
Date.year = os.date('%Y')
Date.year = os.date('%Y')
Date.month = os.date('%m')
Date.month = os.date('%m')
Date.day = s:match('BYMONTHDAY=(%S+)')
Date.day = s:match('BYMONTHDAY=(%S+)')
end
end
return Date
return Date
end
end
},
},
RememberTheMilk = {
RememberTheMilk = {
MatchLink = '<link.-rel=.-alternate.-href=["\'](.-)["\']',
MatchLink = '<link.-rel=.-alternate.-href=["\'](.-)["\']',
MatchItem = '<entry.-</entry>',
MatchItem = '<entry.-</entry>',
MatchItemID = '<id.->(.-)</id>',
MatchItemID = '<id.->(.-)</id>',
MatchItemLink = '<link.-href=["\'](.-)["\']',
MatchItemLink = '<link.-href=["\'](.-)["\']',
MatchItemDesc = '<summary.->(.-)</summary>',
MatchItemDesc = '<summary.->(.-)</summary>',
MatchItemDate = '<span class=["\']rtm_due_value["\']>(.-)</span>',
MatchItemDate = '<span class=["\']rtm_due_value["\']>(.-)</span>',
MergeItems = false,
MergeItems = false,
ParseDate = function(s)
ParseDate = function(s)
local Date = {}
local Date = {}
local MatchTime = '%a%a%a (%d+) (%a%a%a) (%d+) at (%d+)%:(%d+)(%a%a)' -- e.g. 'Wed 7 Nov 12 at 3:17PM'
local MatchTime = '%a%a%a (%d+) (%a%a%a) (%d+) at (%d+)%:(%d+)(%a%a)' -- e.g. 'Wed 7 Nov 12 at 3:17PM'
local MatchDate = '%a%a%a (%d+) (%a%a%a) (%d+)' -- e.g. 'Tue 25 Dec 12'
local MatchDate = '%a%a%a (%d+) (%a%a%a) (%d+)' -- e.g. 'Tue 25 Dec 12'
if s:match(MatchTime) then
if s:match(MatchTime) then
Date.day, Date.month, Date.year, Date.hour, Date.min, Date.Meridiem = s:match(MatchTime)
Date.day, Date.month, Date.year, Date.hour, Date.min, Date.Meridiem = s:match(MatchTime)
elseif s:match(MatchDate) then
elseif s:match(MatchDate) then
Date.day, Date.month, Date.year = s:match(MatchDate)
Date.day, Date.month, Date.year = s:match(MatchDate)
end
end
return Date
return Date
end
end
}
}
}
}
end
end
-------------------------
-------------------------
function IdentifyType(s)
function IdentifyType(s)
-- COLLAPSE CONTAINER TAGS
-- COLLAPSE CONTAINER TAGS
for _, v in ipairs{ 'item', 'entry' } do
for _, v in ipairs{ 'item', 'entry' } do
s = s:gsub('<'..v..'.->.+</'..v..'>', '<'..v..'></'..v..'>') -- e.g. '<entry.->.+</entry>' --> '<entry></entry>'
s = s:gsub('<'..v..'.->.+</'..v..'>', '<'..v..'></'..v..'>') -- e.g. '<entry.->.+</entry>' --> '<entry></entry>'
end
end
--DEFINE RSS MARKER TESTS
--DEFINE RSS MARKER TESTS
--Each of these test functions will be run in turn, until one of them gets a solid match on the format type.
--Each of these test functions will be run in turn, until one of them gets a solid match on the format type.
local TestRSS = {
local TestRSS = {
function(a)
function(a)
-- If the feed contains these tags outside of <item> or <entry>, RSS is confirmed.
-- If the feed contains these tags outside of <item> or <entry>, RSS is confirmed.
for _, v in ipairs{ '<rss', '<channel', '<lastBuildDate', '<pubDate', '<ttl', '<description' } do
for _, v in ipairs{ '<rss', '<channel', '<lastBuildDate', '<pubDate', '<ttl', '<description' } do
if a:match(v) then
if a:match(v) then
return 'RSS'
return 'RSS'
end
end
end
end
return false
return false
end,
end,
function(a)
function(a)
-- Alternatively, if the feed contains these tags outside of <item> or <entry>, Atom is confirmed.
-- Alternatively, if the feed contains these tags outside of <item> or <entry>, Atom is confirmed.
for _, v in ipairs{ '<feed', '<subtitle' } do
for _, v in ipairs{ '<feed', '<subtitle' } do
if a:match(v) then
if a:match(v) then
return 'Atom'
return 'Atom'
end
end
end
end
return false
return false
end,
end,
function(a)
function(a)
-- Alternatively, if the feed contains these tags ICAL is confirmed.
-- Alternatively, if the feed contains these tags ICAL is confirmed.
for _, v in ipairs{ 'DTSTART', 'DTEND' } do
for _, v in ipairs{ 'DTSTART', 'DTEND' } do
if a:match(v) then
if a:match(v) then
return 'Ical'
return 'Ical'
end
end
end
end
return false
return false
end,
end,
function(a)
function(a)
-- If no markers are present, we search for <item> or <entry> tags to confirm the type.
-- If no markers are present, we search for <item> or <entry> tags to confirm the type.
local HaveItems = a:match('<item')
local HaveItems = a:match('<item')
local HaveEntries = a:match('<entry')
local HaveEntries = a:match('<entry')
local HaveIcal = a:match('DTSTART')
local HaveIcal = a:match('DTSTART')
if HaveItems and not HaveEntries then
if HaveItems and not HaveEntries then
return 'RSS'
return 'RSS'
elseif HaveEntries and not HaveItems then
elseif HaveEntries and not HaveItems then
return 'Atom'
return 'Atom'
elseif HaveIcal then
elseif HaveIcal then
return 'Ical'
return 'Ical'
else
else
-- If both kinds of tags are present, and no markers are given, then I give up
-- If both kinds of tags are present, and no markers are given, then I give up
-- because your feed is ridiculous. And if neither tag is present, then no type
-- because your feed is ridiculous. And if neither tag is present, then no type
-- can be confirmed (and there would be no usable data anyway).
-- can be confirmed (and there would be no usable data anyway).
return false
return false
end
end
end
end
}
}
-- RUN RSS MARKER TESTS
-- RUN RSS MARKER TESTS
local Class = false
local Class = false
for _, v in ipairs(TestRSS) do
for _, v in ipairs(TestRSS) do
Class = v(s)
Class = v(s)
if Class then break end
if Class then break end
end
end
-- DETECT SUBTYPE AND RETURN
-- DETECT SUBTYPE AND RETURN
if Class == 'RSS' then
if Class == 'RSS' then
return 'RSS'
return 'RSS'
elseif Class == 'Atom' then
elseif Class == 'Atom' then
if s:match('xmlns:gCal') then
if s:match('xmlns:gCal') then
return 'GoogleCalendar'
return 'GoogleCalendar'
elseif s:match('<subtitle>rememberthemilk.com</subtitle>') then
elseif s:match('<subtitle>rememberthemilk.com</subtitle>') then
return 'RememberTheMilk'
return 'RememberTheMilk'
else
else
return 'Atom'
return 'Atom'
end
end
elseif Class == 'Ical' then
elseif Class == 'Ical' then
return 'GoogleCalendar'
return 'GoogleCalendar'
else
else
return false
return false
end
end
end
end
-------------------------
-------------------------
function IdentifyDate(s, t)
function IdentifyDate(s, t)
--file = io.open("tester.txt", "a")
--file:write(s)
--file:close()
local Date = nil
local Date = nil
-- PARSE STRING BY TYPE
-- PARSE STRING BY TYPE
if (s:match('BYMONTHDAY')) then
if (s:match('BYMONTHDAY')) then
Date = s and Types[t].ParseRecurringDate(s) or {}
Date = s and Types[t].ParseRecurringDate(s) or {}
else
else
Date = s and Types[t].ParseDate(s) or {}
Date = s and Types[t].ParseDate(s) or {}
end
end
Date.year = tonumber(Date.year) or nil
Date.year = tonumber(Date.year) or nil
Date.month = tonumber(Date.month) or MonthAcronyms[Date.month] or nil
Date.month = tonumber(Date.month) or MonthAcronyms[Date.month] or nil
Date.day = tonumber(Date.day) or nil
Date.day = tonumber(Date.day) or nil
Date.hour = tonumber(Date.hour) or nil
Date.hour = tonumber(Date.hour) or nil
Date.min = tonumber(Date.min) or nil
Date.min = tonumber(Date.min) or nil
Date.sec = tonumber(Date.sec) or 0
Date.sec = tonumber(Date.sec) or 0
-- FIND ENOUGH ELEMENTS, OR DEFAULT TO RETRIEVAL DATE
-- FIND ENOUGH ELEMENTS, OR DEFAULT TO RETRIEVAL DATE
local RealDate, AllDay
local RealDate, AllDay
if (Date.year and Date.month and Date.day) then
if (Date.year and Date.month and Date.day) then
RealDate = 1
RealDate = 1
-- DETECT ALL-DAY EVENT
-- DETECT ALL-DAY EVENT
if (Date.hour and Date.min) then
if (Date.hour and Date.min) then
AllDay = 0
AllDay = 0
else
else
AllDay = 1
AllDay = 1
Date.hour = 0
Date.hour = 0
Date.min = 0
Date.min = 0
end
end
-- GET CURRENT LOCAL TIME, UTC OFFSET
-- GET CURRENT LOCAL TIME, UTC OFFSET
-- These values are referenced in several procedures below.
-- These values are referenced in several procedures below.
local UTC = os.date('!*t')
local UTC = os.date('!*t')
local LocalTime = os.date('*t')
local LocalTime = os.date('*t')
local DaylightSavings = LocalTime.isdst and 3600 or 0
local DaylightSavings = LocalTime.isdst and 3600 or 0
local LocalOffset = os.time(LocalTime) - os.time(UTC) + DaylightSavings
local LocalOffset = os.time(LocalTime) - os.time(UTC) + DaylightSavings
-- CHANGE 12-HOUR to 24-HOUR
-- CHANGE 12-HOUR to 24-HOUR
if Date.Meridiem then
if Date.Meridiem then
if (Date.Meridiem == 'AM') and (Date.hour == 12) then
if (Date.Meridiem == 'AM') and (Date.hour == 12) then
Date.hour = 0
Date.hour = 0
elseif (Date.Meridiem == 'PM') and (Date.hour < 12) then
elseif (Date.Meridiem == 'PM') and (Date.hour < 12) then
Date.hour = Date.hour + 12
Date.hour = Date.hour + 12
end
end
end
end
-- FIND CLOSEST MATCH FOR TWO-DIGIT YEAR
-- FIND CLOSEST MATCH FOR TWO-DIGIT YEAR
if Date.year < 100 then
if Date.year < 100 then
local CurrentYear = LocalTime.year
local CurrentYear = LocalTime.year
local CurrentCentury = math.floor(CurrentYear / 100) * 100
local CurrentCentury = math.floor(CurrentYear / 100) * 100
local IfThisCentury = CurrentCentury + Date.year
local IfThisCentury = CurrentCentury + Date.year
local IfNextCentury = CurrentCentury + Date.year + 100
local IfNextCentury = CurrentCentury + Date.year + 100
if math.abs(CurrentYear - IfThisCentury) < math.abs(CurrentYear - IfNextCentury) then
if math.abs(CurrentYear - IfThisCentury) < math.abs(CurrentYear - IfNextCentury) then
Date.year = IfThisCentury
Date.year = IfThisCentury
else
else
Date.year = IfNextCentury
Date.year = IfNextCentury
end
end
end
end
-- GET INPUT OFFSET FROM UTC (OR DEFAULT TO LOCAL)
-- GET INPUT OFFSET FROM UTC (OR DEFAULT TO LOCAL)
if (Date.Offset) and (Date.Offset ~= '') then
if (Date.Offset) and (Date.Offset ~= '') then
if Date.Offset:match('%a') then
if Date.Offset:match('%a') then
Date.Offset = TimeZones[Date.Offset] and (TimeZones[Date.Offset] * 3600) or 0
Date.Offset = TimeZones[Date.Offset] and (TimeZones[Date.Offset] * 3600) or 0
elseif Date.Offset:match('%d') then
elseif Date.Offset:match('%d') then
local Direction, Hours, Minutes = Date.Offset:match('^([^%d]-)(%d+)[^%d]-(%d%d)')
local Direction, Hours, Minutes = Date.Offset:match('^([^%d]-)(%d+)[^%d]-(%d%d)')
Direction = Direction:match('%-') and -1 or 1
Direction = Direction:match('%-') and -1 or 1
Hours = tonumber(Hours) * 3600
Hours = tonumber(Hours) * 3600
Minutes = tonumber(Minutes) and (tonumber(Minutes) * 60) or 0
Minutes = tonumber(Minutes) and (tonumber(Minutes) * 60) or 0
Date.Offset = (Hours + Minutes) * Direction
Date.Offset = (Hours + Minutes) * Direction
end
end
else
else
Date.Offset = LocalOffset
Date.Offset = LocalOffset
end
end
-- RETURN CONVERTED DATE
-- RETURN CONVERTED DATE
Date = os.time(Date) + LocalOffset - Date.Offset
Date = os.time(Date) + LocalOffset - Date.Offset
else
else
-- NO USABLE DATE FOUND; USE RETRIEVAL DATE INSTEAD
-- NO USABLE DATE FOUND; USE RETRIEVAL DATE INSTEAD
RealDate = 0
RealDate = 0
AllDay = 0
A
Date = os.time()
end
return Date, AllDay, RealDate
end
-----------------------------------------------------------------------
-- EVENT FILE MODULE
function EventFile_Initialize()
local EventFiles = {}
local AllEventFiles = SELF:GetOption('EventFile', '')
for EventFile in AllEventFiles:gmatch('[^%|]+') do
table.insert(EventFiles, EventFile)
end
for i, v in ipairs(Feeds) do
local EventFile = EventFiles[i] or