Greetings, folks!
This may be of little (if any) use to others, given the age of HyperCard, but it is a script for converting a DIFfersifier generated export of a HyperCard calendar into a “.ics” file that can be imported into iCal or BusyCal. Dave Riggle and John Chaffee’s incredible work on BusyCal inspired me to do what I’ve been putting off for over a decade. Wisdom gained from MacScripter was plundered without shame.
Here it is:
(*
This script assumes DIFfersifier has been used to generate the comma-delimited text on a per-week basis and is replacing returns with vertical tabs (ASCII character 11). Open the resulting file in BBEdit (though Text Wrangler may be up to it!) and run this script. When the script is done, save it as an ".ics" file. Double-clicking the file will open iCal/BusyCal and import it. Put it in a new calendar for review.
NOTE: -- multi-event dates in HyperCard adding \",\" is an error due to changing the TID and not resetting it afterward. Reboot may be necessary; it may just require quitting the AppleScript editor.
*)
global theIndex, theTIDs, trimCharacters, theDates, prevWeeksMonth, theMonth, theDayNumber, theLastDay, startDate, thisWeeksMonth, theExportWeek, errorCount, theYear
set theTIDs to AppleScript's text item delimiters
set theLastDay to {{"0131", "0201"}, {"0228", "0301"}, {"0229", "0301"}, {"0331", "0401"}, {"0430", "0501"}, {"0531", "0601"}, {"0630", "0701"}, {"0731", "0801"}, {"0831", "0901"}, {"0930", "1001"}, {"1031", "1101"}, {"1130", "1201"}, {"1231", "0101"}} -- this compensates for end-of-month issues
set trimCharacters to {" ", tab, return, ASCII character 0, ASCII character 10, ASCII character 11}
set theCalendar to ""
tell application "BBEdit"
-- before anything else, replace multiple returns to eliminate errant events in any given day, as they won't be stripped by trim()
replace ((ASCII character 11) & (ASCII character 11) & "+") using (ASCII character 11) searching in text 1 of text document 1 options {search mode:grep, starting at top:true}
-- establish the field headings index and determine the version of HyperCard that was used
set theIndex to line 1 of text 1 of text document 1 as text -- this establishes the numerical index of days and dates
-- if the calendar is from version 1 of HyperCard's calendar
if theIndex is "wee,mon,tue,wed,thu,fri,Month,WeekNumber,monday,tuesday,wednesday,thursday,friday,weekend" then
set theIndex to "HC1"
-- if the calendar is from version 2 of HyperCard's calendar
else if theIndex is "Mon,Tue,Wed,Thu,Fri,Sat,Sun,Month,WeekNumber,weekday1,weekday2,weekday3,weekday4,weekday5,weekday6,weekday7,weekSecs" then
set theIndex to "HC2"
-- also establish the initial month number for the card, i.e. week, as HyperCard 2 will often title a week with the coming month
set theList to characters 2 thru -2 of line 2 of text 1 of text document 1 as text -- consolidate the data
set AppleScript's text item delimiters to "\",\""
set theMonth to word 2 of (item 8 of the text items of theList)
set AppleScript's text item delimiters to theTIDs -- restore the default
set prevWeeksMonth to ((offset of theMonth's text 1 thru 3 in " JanFebMarAprMayJunJulAugSepOctNovDec") div 3 + 1)
set errorCount to 0
else
say "the list headings do not match, please adjust the index"
return
end if
######## Here We Set the Range and Process the Data ########
-- this repeat goes through each line of the comma-delimited text file generated by DIFfersifier, which is by week
repeat with weekNumber from 2 to ((the number of lines of text 1 of text document 1) - 1) -- the last line is a return
my eachWeek(weekNumber)
set theCalendar to theCalendar & theExportWeek
end repeat
############################################
-- prepare the data for importing into iCal/BusyCal
set theCalendar to "BEGIN:VCALENDAR" & return & theCalendar & "END:VCALENDAR" -- theCalendar ends with a return
make new text window with properties {contents:theCalendar}
-- strip out multiple spaces
replace " +" using " " searching in text 1 of text document 1 options {search mode:grep, starting at top:true}
-- strip out any empty entries
replace "BEGIN:VJOURNAL" & return & "DTSTART;VALUE=DATE:(\\d+)" & return & "DTEND;VALUE=DATE:(\\d+)" & return & "SUMMARY:" & return & "END:VJOURNAL" & return using "" searching in text 1 of text document 1 options {search mode:grep, case sensitive:true, starting at top:true}
activate
select insertion point before character 1 of line 1 of text window 1 -- scroll to the top of the window
if theIndex is "HC2" then say "there were " & errorCount & " date errors" -- where the month name was not for the first day
end tell
---- The Handlers ----
-- process each week, one week at a time
on eachWeek(weekNumber)
-- strip out the opening and closing quotes as the text item delimiters substitution won't do it
tell application "BBEdit" to set theList to characters 2 thru -2 of line weekNumber of text 1 of text document 1 as text
-- create two corresponding lists with matching day/date references
set AppleScript's text item delimiters to "\",\""
#### the following is for early HyperCard calendars ####
if theIndex is "HC1" then
-- "text items of" to make it a list, the first six are: wee,mon,tue,wed,thu,fri -- this is the event text for the week
set theEvents to items 2 thru 3 of the text items of theList & item 1 of the text items of theList
-- these are the date number fields, and is appending the year and month text for the week
set theDates to items 9 thru -1 of the text items of theList & item 7 of the text items of theList
-- split out the weekend, and append the year and month
set theDates to items 1 thru 5 of theDates & word 1 of item 6 of theDates & word 2 of item 6 of theDates & item 7 of theDates
set daysInWeek to 6
#### the following is for HyperCard 2 (or so ;-) ) calendars ####
else if theIndex is "HC2" then
-- "text items of" to make it a list, the first seven are: Mon,Tue,Wed,Thu,Fri,Sat,Sun -- this is the event text for the week
set theEvents to items 1 thru 7 of the text items of theList
-- these are the date number fields, and is appending the year and month text for the week
set theDates to items 1 thru 8 of (items 10 thru -2 of the text items of theList & item 8 of the text items of theList)
set daysInWeek to 7
end if
set AppleScript's text item delimiters to theTIDs -- restore the default
set theExportWeek to "" as text
-- ensure that the day numbers are two digits
repeat with i from 1 to 7
if the number of characters of item i of theDates = 1 then set item i of theDates to "0" & item i of theDates
end repeat
repeat with d from 1 to daysInWeek -- the number of items of theEvents
set e to ""
set startDate to ""
set endDate to ""
set theEvent to item d of theEvents
if the number of characters of theEvent > 0 then
set theEvent to trim(trimCharacters, theEvent)
-- if there are multiple events in a day
if theEvent contains (ASCII character 11) then
set AppleScript's text item delimiters to ASCII character 11
set splitDay to text items of theEvent -- not text of, text items of
set AppleScript's text item delimiters to theTIDs -- restore the default
repeat with e from 1 to the number of items of splitDay
set theEvent to item e of splitDay
if the number of characters of theEvent > 0 then
set theEvent to trim(trimCharacters, theEvent)
set item e of splitDay to theEvent
-- create the export data
set startDate to my processStartDate(d, startDate)
set endDate to my processEndDate(endDate)
set theEventExport to ("BEGIN:VJOURNAL" & return & "DTSTART;VALUE=DATE:" & startDate & return & "DTEND;VALUE=DATE:" & endDate & return & "SUMMARY:" & theEvent & return & "END:VJOURNAL" & return)
set item e of splitDay to theEventExport
end if
end repeat
set item d of theEvents to splitDay
else -- if there is only one event in the day
-- create the export data
set startDate to my processStartDate(d, startDate)
set endDate to my processEndDate(endDate)
set theDayExport to ("BEGIN:VJOURNAL" & return & "DTSTART;VALUE=DATE:" & startDate & return & "DTEND;VALUE=DATE:" & endDate & return & "SUMMARY:" & theEvent & return & "END:VJOURNAL" & return)
set item d of theEvents to theDayExport
end if
end if
if item d of theEvents is not "" then set theExportWeek to (theExportWeek & item d of theEvents)
end repeat
if theIndex is "HC2" then set prevWeeksMonth to thisWeeksMonth
return theExportWeek
end eachWeek
on processStartDate(d, startDate)
-- generate the day number
set theDayNumber to item d of theDates
-- generate the month number
set theMonth to ((offset of (word 2 of item 8 of theDates)'s text 1 thru 3 in " JanFebMarAprMayJunJulAugSepOctNovDec") div 3 + 1)
if theIndex is "HC2" then -- given that the week's month may not correspond to the month of the first day of the week
set thisWeeksMonth to theMonth as number
if ((prevWeeksMonth < theMonth) and (theDayNumber > 0)) then
set errorCount to errorCount + 1
set theMonth to (theMonth - 1)
end if
end if
-- ensure that the month numbers are two digits
if the number of characters of (theMonth as text) = 1 then set theMonth to "0" & theMonth as text
-- generate the year
set theYear to word 1 of item 8 of theDates
-- generate the full date
-- if the day number is less than the opening-of-the-week day number, increase the month number
if theDayNumber < item 1 of theDates then
if theMonth = 12 then -- if it is December then make it January and make it a new year
set theMonth to "01"
set theYear to theYear + 1
else -- just increasee the month number
set theMonth to theMonth + 1
if the number of characters of (theMonth as text) = 1 then set theMonth to "0" & theMonth as text
end if
end if
set startDate to (theYear as text) & theMonth & theDayNumber
return startDate
end processStartDate
on processEndDate(endDate)
-- this calculates end-of-month-and-year dates for the end-of-event date, i.e., the next day
set beginDate to (theMonth as text) & (theDayNumber as text) as number
if (beginDate = item 1 of item 13 of theLastDay as number) then -- if it is December 31st
set endDate to (theYear + 1 as text) & (item 2 of item 13 of theLastDay)
else
repeat with i from 1 to 12 -- I have two enties for February ;-)
if (beginDate = item 1 of item i of theLastDay as number) then
set endDate to theYear & (item 2 of item i of theLastDay)
exit repeat
else
set endDate to ((startDate + 1) as number)
end if
end repeat
end if
return endDate
end processEndDate
-- trim the text
on trim(trimCharacters, theEvent)
-- remove extraneous spaces, returns, etc.
set isText to false
repeat with c from 1 to the number of characters in theEvent
if character c of theEvent is not in trimCharacters then -- if it is not just empty space
set isText to true
exit repeat
end if
end repeat
if isText is true then
repeat until first character of theEvent is not in trimCharacters
set theEvent to text 2 thru -1 of theEvent
end repeat
repeat until last character of theEvent is not in trimCharacters
set theEvent to text 1 thru -2 of theEvent
end repeat
else
set theEvent to ""
end if
return theEvent
end trim
Model: 2.33GHz MBPC2D
Browser: Google Chrome 6.0.472.62
Operating System: Mac OS X (10.6)