-- CALENDAR -- -- -- © October 1998 Macromedia Inc. -- and June 2000 - August 2005 OpenSpark Interactive Ltd -- -- ---------------------------------------------------------------------- -- HISTORY -- TO DO: * Allow user to disable certain weekdays in all months -- 051130 JN: * Fixed bug where mGetDisabledDates() took more time -- than necessary when startDate was after endDate -- 051103 JN: * Added startDate, endDate and plExclusions to limit the -- dates which can be clicked on and navigated to. -- + Calendar_SetLimits() -- + Calendar_GetLimits() -- + Calendar_ExcludeDates() -- + Calendar_GetExcludedDates() -- + Calendar_FirstAvailableDate() -- + Calendar_GetDateStatus() -- * mTreatLinkClick() adapted so that a Shift click on -- navigation arrows jumps to the first available date, -- regardless of choice of browseMode -- * GetRelativeDate() now maps to the end of the -- expected month rather than spilling over to the next -- month when the jump is by month or year, and the -- target month has less days than the source date month. -- 050820 JN: * Fully revised to use date objects only -- * Variables and handlers renamed to follow OpenSpark -- house style -- * Extended to work with both the Julian and Gregorian -- calendars (except for the month of September 1752) -- * A number of non-behavior features removed -- * Generic handlers rewritten to allow use as a Movie -- Script -- * Special color for today's date -- * Callback activated when a date is clicked. -- 000603 JN: Updated to accept date objects -- 981029 JN: Improvements to the navigation by year and month -- 981013 JN: Written for the D7 Behaviors Palette by James Newton -- ---------------------------------------------------------------------- -- HYPERTEXT -- This behavior dynamically creates the text and hyperlinks for a -- Text member. I override the standard hyperlinkStyles, so as to -- avoid the link underline. The mCheckLinkRollover() handler -- indicates that a word is active by modifying its color and altering -- the cursor. The mTreatLinkClick() handler uses the hyperlinkState -- property (#normal, #active, #visited) to store information on which -- link was last clicked. This information is used by -- mCheckLinkRollover() to determine which color a link word should -- revert to. -- ---------------------------------------------------------------------- -- DATE CALCULATION -- This behavior features a number of date manipulation handlers: -- -- GetRelativeDate -- GetFirstDayOfMonth -- GetWeekDay -- GetDayNames -- GetMonthNames -- GetDaysInMonth -- -- If this member is used as a Movie Script, then these handlers can -- be called directly. See the separate functions in the DATE -- HANDLERS section for details. -- ---------------------------------------------------------------------- -- LANGUAGES -- It is very simple to add new languages to the calendar. Simply add -- a new case statement to both the GetDayNames and the GetMonthNames -- functions. Follow the models for French and German. You will -- also have to add the name of the language (both in English and in -- the language itself) to the lists returned by the -- mGetSupportedLanguages() function. ---------------------------------------------------------------------- -- PROPERTY DECLARATIONS -- property spriteNum -- author-defined parameters property autoDate -- set this to FALSE if you want to control -- the calendar entirely from Lingo property period -- "this month", "next month", name of month property language -- symbol language name property showFullMonth -- TRUE if month name is to appear in full property showFullDay -- TRUE if weekday names are to appear in full property titleSize -- integer fontSize for titles (12 - 36) property dateSize -- integer fontSize for dates (9 - 36) property browseMode -- #no, #month, #year -- date limits property startDate -- earliest selectable or navigable date | 0 property endDate -- latest selectable or navigable date | 0 property plExclusions -- list of dates which will not be active -- [: , ...] -- callback property callback -- VOID or callback handler symbol property pObject -- object containing callback method property pParameter -- any Lingo value to be used with callback -- colors property pStandardColor -- color of non-interactive text property pRolloverColor -- color of hyperlinks on rollover property pSelectedColor -- color of month or year, depending on which -- is active property pTodayColor -- color for hiliting today's date property pDisabledColor -- color for date after pEndDate or before -- pStartDate property pOriginalColor -- color of current hyperlink before rollover -- internal properties property pSprite -- sprite(me.spriteNum) property pMember -- pSprite.member (text member) property pDateObject -- date() for day currently displayed -- links property pCheckRollover -- TRUE if calendar has interactive links property pLinkList -- pMember.hyperlinks, for example: -- [[1, 2], [5, 10], [12, 15], [18, 19]] property pLink -- hyperlink text for currently active link -- "last", "month", "year" "next" property pLinkRef -- for currently active -- hyperlink property pCalendarStep -- #month or #year: for stepping through dates property pQueue -- stores a date object immediately after a -- click on a date hyperlink, for callback on -- the next exitFrame() -- EVENT HANDLERS -- on beginSprite(me) me.mInitialize() end beginSprite on mouseWithin(me) ---------------------------------------------------- -- ACTION: Shows rollover colors and cursor -------------------------------------------------------------------- if pCheckRollover then me.mCheckLinkRollover() end if end mouseWithin on mouseLeave(me) ---------------------------------------------------- -- ACTION: Ensures rollover hilite disappears, even if the mouse is -- removed quickly -------------------------------------------------------------------- if pCheckRollover then me.mCheckLinkRollover() end if end mouseLeave on hyperlinkClicked(me, aLinkString, aRange) ------------------------- -- ACTION: Allows the user to navigate or to choose dates -------------------------------------------------------------------- me.mTreatLinkClick(aLinkString, aRange) end hyperlinkClicked on exitFrame(me) ----------------------------------------------------- -- ACTION: Used as a workaround for the fact that calling 'play ...' -- from within a hyperlinkClicked() handler crashes -- Director. -------------------------------------------------------------------- if not pQueue then exit end if vQueue = pQueue pQueue = VOID me.mCallback(vQueue) end exitFrame -- CUSTOM HANDLERS -- on mInitialize(me) --------------------------------------------------- -- SOURCE: Sent by beginSprite -- ACTION: Prepares the text member for the calendar -------------------------------------------------------------------- pSprite = sprite(me.spriteNum) pMember = pSprite.member -- HARD-CODED member manipulations pMember.alignment = #left pMember.editable = FALSE pMember.firstIndent = 0 pMember.leftIndent = 0 pMember.rightIndent = 0 pMember.useHypertextStyles = FALSE pMember.wordWrap = TRUE -- Convert author-defined parameters browseMode = symbol(browseMode) if startDate then startDate = date(startDate) -- no earlier than date(0, 1, 1) end if if endDate then -- may be 0 endDate = date(endDate) -- -ve endDate becomes date(0, 1, 1) end if if callback = "" then callback = VOID else callback = symbol(callback) end if me.mSetLinkColors() plExclusions = [:] plExclusions.sort() -- Set up calendar or diary pDateObject = the systemDate -- Period settings may be overwritten by start and end settings. case period of "this month": -- nothing "next month": pDateObject = GetRelativeDate(pDateObject, #month, 1) otherwise vMonths = GetMonthNames(0) vMonth = vMonths.getPos(period) pDateObject.month = vMonth pDateObject.day = 1 end case -- Ensure the date is within the accepted limits. If the endDate -- is before the startDate, the date will not be moved, but all -- interactions will be disabled. if startDate > pDateObject then if startDate <= endDate then pDateObject = startDate end if else if endDate < pDateObject then if endDate >= startDate then pDateObject = endDate end if end if if autoDate then me.mCreateCalendar() end if end mInitialize -- HYPERTEXT HANDLERS -- on mSetLinkColors(me) ------------------------------------------------ -- SOURCE: Sent by mInitialize() -- NOTE: Modify these values to customize the text colors -------------------------------------------------------------------- pStandardColor = rgb("#000000") pRolloverColor = rgb("#1B4E81") pSelectedColor = rgb("#003366") pTodayColor = rgb("#CC0000") pDisabledColor = rgb("#999999") pOriginalColor = pStandardColor end mSetLinkColors on mCheckLinkRollover(me) -------------------------------------------- -- SOURCE: Sent by mouseWithin() -- ACTION: Determines mouseEnter and mouseLeave for hyperlinks and -- sets the cursor and rollover colors accordingly --------------------------------------------------------------------- vMouseLoc = the mouseLoc if pSprite.pointInHyperLink(vMouseLoc) then vChar = pSprite.pointToChar(vMouseLoc) vLink = pMember.char[vChar].hyperLink if pLink = vLink then exit end if -- If we get here, the mouse is over a new hyperlink pLink = vLink vRange = pMember.char[vChar].hyperLinkRange if objectP(pLinkRef) then -- The mouse was previously over a different link. Reset color. if pLinkRef.hyperlinkState = #normal then pLinkRef.color = pOriginalColor -- pStandardColor else pLinkRef.color = pSelectedColor end if end if pLinkRef = pMember.char[vRange[1]..vRange[2]].ref pOriginalColor = pLinkRef.color pLinkRef.color = pRolloverColor pSprite.cursor = 280 else if objectP(pLinkRef) then -- If we get here, the mouse has just left a hyperlink. Reset -- color of link if pLinkRef.hyperlinkState = #normal then pLinkRef.color = pOriginalColor -- pStandardColor else pLinkRef.color = pSelectedColor end if -- Clean up pLinkRef = void pLink = EMPTY pSprite.cursor = 0 end if end mCheckLinkRollover on mTreatLinkClick(me, aLinkString, aRange) -------------------------- -- SOURCE: Sent by hyperlinkClicked() -- INPUT: will be "last", "month", "year" or "next" -- will be a list such as [1, 2] indicating the -- start and end characters for the clicked link. -- ACTION: Modifies pCalendarStep or creates a calendar for a new -- month -------------------------------------------------------------------- case aLinkString of "last", "next": if (aLinkString = "next") then vDirection = 1 else vDirection = -1 end if case pCalendarStep of #month: pDateObject = GetRelativeDate(pDateObject,#month,vDirection) #year: pDateObject = GetRelativeDate(pDateObject,#year, vDirection) end case if the shiftDown then -- Jump to the nearest month with available dates case vDirection of -1: vDaysInMonth = GetDaysInMonth(pDateObject) pDateObject.day = vDaysInMonth 1: pDateObject.day = 1 end case vTemp = me.Calendar_FirstAvailableDate(pDateObject,vDirection) if ilk(vTemp, #date) then pDateObject = vTemp else beep exit end if end if "month", "year": vLinkRef = pMember.char[aRange[1]..aRange[2]].ref pCalendarStep = symbol(aLinkString) if vLinkRef.hyperlinkState = #normal then -- Hilite selected step vLinkRef.hyperlinkState = #visited vLinkRef.color = pSelectedColor -- Unhilite previously selected step if aLinkString = "month" then vRange = pLinkList[3] -- year else vRange = pLinkList[2] -- month end if vLinkRef = pMember.char[vRange[1]..vRange[2]].ref vLinkRef.hyperlinkState = #normal vLinkRef.color = pStandardColor end if otherwise: -- "callback X" vDay = value(word 2 of aLinkString) vDate = pDateObject.duplicate() -- original can't be altered vDate.day = vDay return me.mQueueForCallback(vDate) -- mCallback(vDate) end case me.mCreateCalendar() end mTreatLinkClick -- DATE DISPLAY HANDLERS -- on mCreateCalendar(me) ----------------------------------------------- -- SOURCE: Sent by mInitialize(), mTreatLinkClick(), -- _ToggleFullNames(), _ToggleBrowse(), _SetLanguage(), -- _SetDisplay() -- ACTION: Updates the Calendar Text member display -------------------------------------------------------------------- pMember.text = me.mGetCalendarText(pDateObject) me.mFormatText() -- Treat links pCheckRollover = TRUE if browseMode = #no then if not symbolP(callback) then pCheckRollover = FALSE end if end if me.mInitializeLinks() end mCreateCalendar on mGetCalendarText(me, aDateObject) ---------------------------------- -- SOURCE: Sent by mCreateCalendar -- INPUT: will be a Lingo date object -- OUTPUT: Returns the tabulated text necessary for displaying the -- monthly calendar --------------------------------------------------------------------- vMonth = aDateObject.month vYear = aDateObject.year vDayOne = GetFirstDayOfMonth(aDateObject) -- 1 - 7 vDayCount = GetDaysInMonth(aDateObject) -- 28 - 31 vMonthNames = GetMonthNames() vDayNames = GetDayNames() vWeekCount = (vDayCount + vDayOne - 2) / 7 vText = vMonthNames[vMonth]&&vYear if browseMode <> #no then vText = "<< "&TAB&vText&TAB&" >>"&RETURN&RETURN else vText = TAB&vText&RETURN&RETURN end if repeat with day = 1 to 7 vText = vText&TAB&vDayNames[day] end repeat repeat with vLine = 0 to vWeekCount put RETURN after vText vFirstDayInWeek = (vLine * 7) + 2 - vDayOne vLastDayInWeek = (vLine * 7) + 8 - vDayOne repeat with i = vFirstDayInWeek to vLastDayInWeek if i > vDayCount then exit repeat end if if i < 1 then vDate = "" else vDate = i end if vText = vText&TAB&vDate end repeat end repeat return vText end mGetCalendarText on mFormatText(me) --------------------------------------------------- -- SOURCE: Sent by mCreateCalendar() and recursively if the text -- member is too narrow for the text -- ACTION: Optimizes the position of the tabulations in the Text -- member -------------------------------------------------------------------- -- Place month and year in center, and next arrows to extreme right vTabList = [ \ [#type: #center, #position: pMember.width / 2], \ [#type: #right, #position: pMember.width - 1]] vHeaderRef = pMember.paragraph[1].ref vHeaderRef.tabs = vTabList vHeaderRef.fontSize = titleSize -- Create 7 columns for the days vTabList = me.mGetDateTabs(pMember) vCalendarRef = pMember.paragraph[2..pMember.paragraph.count].ref vCalendarRef.tabs = vTabList vCalendarRef.fontSize = dateSize -- Check that the weekdays have not wrapped to the next line -- (Only works if the #wordWrap of the text member is TRUE). sundayPos = pMember.paragraph[1..2].char.count + 2 saturdayPos = sundayPos + pMember.paragraph[3].char.count sundayLoc = charPosToLoc(pMember, sundayPos) saturdayLoc = charPosToLoc(pMember, saturdayPos) if sundayLoc[2] <> saturdayLoc[2] then -- Not enough room for the weekday names: widen the field and use -- recursion pMember.rect = pMember.rect + [0, 0, saturdayLoc[1], 0] return me.mFormatText() end if -- Enable all dates by default pMember.color = pStandardColor end mFormatText on mGetDateTabs(me) -------------------------------------------------- -- SOURCE: Sent by FormatText -- OUTPUT: Returns a linear list of describing 7 tabs, with the -- format: -- [[#type: #center, #position: 14], ...] -------------------------------------------------------------------- vTabList = [] vTabWidth = pMember.width / 7 vTabAdjust = vTabWidth / 2 repeat with i = 1 to 7 vPosition = (vTabWidth * i) - vTabAdjust vTab = [#type: #center, #position: vPosition] vTabList.append(vTab) end repeat return vTabList end mGetDateTabs on mInitializeLinks(me) ---------------------------------------------- -- SOURCE: Sent by mCreateCalendar() after the text of the member -- has been set and shown as disabled. -- ACTION: Creates hyperlinks for the Text member and sets text -- color appropriately. -- NOTE: If start- and endDates have been set, or if plExclusions -- is not empty then, certain dates may be disabled and -- monthly or yearly navigation may be disabled in one or -- both directions. If entire months or years are excluded, -- a shift click on the navigation arrows will jump to the -- nearest month with active dates. -------------------------------------------------------------------- vSetLinks = symbolP(callback) -- Disabled dates if browseMode = #no then -- There are no << or >> arrows to take into account vStart = 9 else vStart = 11 end if vEnd = the number of words of pMember.text vDisabled = me.mGetDisabledDates() vDays = vDisabled.days vDayCount = vEnd - vStart repeat with i = 1 to vDayCount vRef = pMember.word[vStart + i].ref if vDays.getPos(i) then vRef.color = pDisabledColor else if vSetLinks then -- Active date. Setting the hyperlink of up to 31 individual -- dates is a lengthy business. Unfortunately, if hyperlinks -- are created collectively, you do not automatically get -- rollover detection for individual dates. One solution might -- be to abandon hyperlinks all together and write rollover -- and callback code manually. vRef.hyperlink = "callback "&i end if end repeat -- Navigation if browseMode <> #no then -- Header links if vDisabled[#last] then pMember.word[1].color = pDisabledColor else pMember.word[1].hyperlink = "last" end if if vDisabled[#next] then pMember.word[4].color = pDisabledColor put pMember.word[4].char.count put pMember.word[4].color put pMember.word[4].char[2].color else pMember.word[4].hyperlink = "next" end if if browseMode = #year then -- Allow user to choose between navigation by month and by year pMember.word[2].hyperlink = "month" pMember.word[3].hyperlink = "year" end if end if pLinkList = pMember.hyperlinks if browseMode <> #no then -- Add dummy links where required so that a click on month or year -- works as expected, even if navigation is limited. if vDisabled[#last] then pLinkList.addAt(1, 0) end if if vDisabled[#next] then pLinkList.addAt(4, 0) end if end if -- Activate the stepPeriod if voidP(pCalendarStep) then pCalendarStep = #month end if if browseMode = #year then case pCalendarStep of #month: vRange = pLinkList[2] #year: vRange = pLinkList[3] end case -- Show month or year as "visited", depending on stepPeriod vLinkRef = pMember.char[vRange[1]..vRange[2]].ref vLinkRef.hyperlinkState = #visited vLinkRef.color = pSelectedColor end if -- Show today's date vToday = the systemDate if vToday.year = pDateObject.year then if vToday.month = pDateObject.month then vDay = vToday.day if not vDays.getPos(vDay) then pMember.word[vStart + vDay].color = pTodayColor end if end if end if end mInitializeLinks on mGetDisabledDates(me) ---------------------------------------------- -- SOURCE: Called by mInitializeLinks() -- ACTION: Checks whether to disable any of the days in the current -- month, or one or both navigation buttons -- OUTPUT: Returns a property list with the format: -- [#days: {, -- #last: }{, -- #next: > arrows>}] -------------------------------------------------------------------- vOutput = [:] vDays = [] vDays.sort() vOutput[#days] = vDays -- Determine which dates are active vFirstDay = pDateObject.duplicate() vFirstDay.day = 1 if not startDate then -- There is no start limit else -- Check for start limit vDisabled = startDate - vFirstDay if vDisabled > -1 then -- Don't show last month or year vOutput[#last] = TRUE -- If the startDate is after the end of this month, don't -- disable more days than there are in the month if vDisabled > 31 then vDisabled = 31 end if if vDisabled > 0 then -- List days disabled at the beginning of the month repeat while vDisabled vDays.add(vDisabled) vDisabled = vDisabled - 1 end repeat end if else if vDisabled > -335 then if pCalendarStep = #year then -- Disable navigation to the previous year vOutput[#last] = TRUE end if end if end if -- Determine the last day of the current month vMonth = vFirstDay.month if vMonth = 12 then vLastDay = vFirstDay + 30 else vLastDay = vFirstDay.duplicate() vLastDay.month = vMonth + 1 -- 1st of following month vLastDay = vLastDay - 1 end if vDay = vLastDay.day -- number of days in month if not endDate then -- Do not check for an end limit else vDisabled = vLastDay - endDate if vDisabled > -1 then -- Don't show next month or year vOutput[#next] = TRUE -- If startDate is after endDate, then all dates will already -- have been disabled. Don't disable them again vMax = vDay - vDays.getLast() if vMax < 0 then vDisabled = 0 else if vDisabled > vMax then vDisabled = vMax end if repeat while vDisabled vDays.add(vDay) vDay = vDay - 1 vDisabled = vDisabled - 1 end repeat else if vDisabled > -335 then -- from month end x to beginning x+1 if pCalendarStep = #year then -- Disable navigation to the following year vOutput[#next] = TRUE end if end if end if -- Check for excluded days if not startDate then i = vFirstDay.day else i = max(startDate, vFirstDay).day end if repeat with i = i to vDay -- last day not off limits vFirstDay.day = i vStatus = me.Calendar_GetDateStatus(vFirstDay) if vStatus <> #available then vDays.add(i) end if end repeat return vOutput end mGetDisabledDates on mQueueForCallback(me, aDateObject) -------------------------------- -- SOURCE: Sent by mTreatLinkClick() -- ACTION: Stores the date for callback on the next exitFrame() to -- avoid a crash when 'play ...' is called from a -- #hyperlinkClicked event. -------------------------------------------------------------------- pQueue = aDateObject end mQueueForCallback on mCallback(me, aDateObject) ---------------------------------------- -- SOURCE: Sent by mTreatLinkClick() if the user clicks on a date -- ACTION: Calls the registered callback handler in the registered -- object. -------------------------------------------------------------------- case ilk(pObject) of #script, #instance: -- WORKAROUND: Using call() on a list to trigger 'play ...' will -- make the call stack abort at this point. Send the event -- directly to the object instead. --call(callback, [pObject], aDateObject, pParameter) if HasHandler(pObject, callback) then call(callback, pObject, aDateObject, pParameter) --==========================================================-- nothing end if -- #list, #propList: call(callback, pObject, aDateObject, pParameter) otherwise: -- Call a movie handler (use a dummy first parameter) sendSprite(0, callback, aDateObject, pParameter) end case end mCallback on mGetSupportedLanguages(me, inEnglish) ----------------------------- -- SOURCE: Sent by getPropertyDescriptionList, _SetLanguage -- INPUT: only has any effect if it is TRUE -- OUTPUT: Returns a list of symbols of supported language names -------------------------------------------------------------------- if (inEnglish = TRUE) then return [#English,#French, #German] else return [#English, #Deutsch, #Franais] end if end mGetSupportedLanguages on mUnexcludeDates(me, aStartDate, anEndDate) ------------------------ -- SOURCE: Sent by Calendar_ExcludeDates() -- INPUT: and will both be dates, with -- aStartDate earlier than anEndDate. The order and -- meaning of the parameters is reversed with respect to -- Calendar_ExcludeDates() -- ACTION: Modifies the entries in plExclusions so that all dates -- between aStartDate and anEndDate are included. -------------------------------------------------------------------- i = plExclusions.count repeat while i vStart = plExclusions.getPropAt(i) if anEndDate > vStart then -- This period will need to be trimmed or dropped exit repeat end if i = i - 1 end repeat if i then -- There is at least one exclusion period which starts before -- anEndDate. We need to move the beginning of the current period -- to anEndDate, delete the period altogether, or split it in two. vEnd = plExclusions[i] if vEnd < anEndDate then if vStart > aStartDate then -- The entire current period needs to be dropped plExclusions.deleteAt(i) i = i - 1 else -- The current period needs to be dropped from aStartDate to -- the end. There will be no trimmed end. end if else -- We need to split the current period in two. Add an entry for -- the end period. The start of the period will be dealt with -- in the final section. plExclusions.addProp(anEndDate, vEnd) end if -- Now we delete all periods which start after aStartDate. repeat while i vStart = plExclusions.getPropAt(i) if aStartDate < vStart then -- This period starts after aStartDate and ends before -- anEndDate plExclusions.deleteAt(i) else exit repeat end if i = i - 1 end repeat if i then -- The current period starts before aStartDate, but may need to -- be trimmed at the end vEnd = plExclusions[i] if vEnd > aStartDate then plExclusions[i] = aStartDate end if end if end if end mUnexcludeDates on mExcludeDates(me, aStartDate, anEndDate) -------------------------- -- SOURCE: Sent by Calendar_ExcludeDates() -- INPUT: and will both be dates, with -- aStartDate no later than anEndDate. -- ACTION: Modifies the entries in plExclusions so that all dates -- between aStartDate and anEndDate are excluded, and that -- there are no overlapping or contiguous entries. -------------------------------------------------------------------- vCount = plExclusions.count() if not vCount then plExclusions.addProp(aStartDate, anEndDate) exit end if -- Find which current exclusion contains aStartDate, and -- shift aStartDate back if such an exclusion is found. vIndex = plExclusions.findPos(aStartDate) if not vIndex then vIndex = plExclusions.findPosNear(aStartDate) vIndex = vIndex - 1 end if if vIndex then -- aStartDate may lie in the previous exclusion period vEnd = plExclusions[vIndex] if vEnd + 2 > aStartDate then -- merge contiguous periods aStartDate = plExclusions.getPropAt(vIndex) end if end if -- Find which current exclusion contains anEndDate, and -- shift anEndDate forward if such an exclusion is found. vIndex = plExclusions.findPos(anEndDate) if not vIndex then vIndex = plExclusions.findPosNear(anEndDate) vIndex = vIndex - 1 end if if vIndex then -- anEndDate may lie in the previous exclusion period vEnd = plExclusions[vIndex] if anEndDate < vEnd then anEndDate = vEnd else if vIndex < vCount then -- Check whether the next period starts the day after the -- new period ends. If so, merge the two periods. vIndex = vIndex + 1 if plExclusions.getPropAt(vIndex) = anEndDate + 1 then anEndDate = plExclusions[vIndex] else vIndex = vIndex - 1 end if end if end if -- Starting at the index of the (adjusted) anEndDate, delete -- entries which start after the (adjusted) aStartDate. repeat while vIndex vStart = plExclusions.getPropAt(vIndex) if vStart < aStartDate then -- We have reached a discontiguous earlier period exit repeat end if plExclusions.deleteAt(vIndex) vIndex = vIndex - 1 end repeat -- Add a new item with the (adjusted) aStart- and anEndDates plExclusions.addProp(aStartDate, anEndDate) end mExcludeDates -- DATE HANDLERS -- on GetRelativeDate(aDateObject, aPeriod, aStep) ---------------------- -- SOURCE: Sent by mInitialize(), mTreatLinkClick() -- INPUT: should be a date object -- can take one of the following values: -- #day, #week, #month, #year. Other values are ignored. -- can be either a positive or negative integer -- ACTION: Calculates the date which is aStep periods from the date -- defined by aDateObject. -- OUTPUT: Returns a date object -- SAMPLE: apolloLanding = date(1969, 7, 19) -- put GetRelativeDate(apolloLanding, #day, 11123) -- -- date(2000, 1, 1) ------------------------------------------------------------------- if ilk(aDateObject) <> #date then return #invalidDate end if if ilk (aStep) <> #integer then return aDateObject end if if aPeriod = #week then aStep = aStep * 7 aPeriod = #day end if vDateObject = aDateObject.duplicate() case aPeriod of #day: vDateObject = vDateObject + aStep #month: vMonth = vDateObject.month + aStep vYear = vDateObject.year repeat while vMonth < 1 vMonth = vMonth + 12 vYear = vYear - 1 end repeat repeat while vMonth > 12 vMonth = vMonth - 12 vYear = vYear + 1 end repeat vDateObject.month = vMonth vDateObject.year = vYear -- Go to the last day of the appropriate month, rather than -- spilling over into the next month repeat while vDateObject.month > vMonth vDateObject = vDateObject - 1 end repeat #year: vMonth = vDateObject.month vDateObject.year = vDateObject.year + aStep if vMonth = 2 and vDateObject.month = 3 then -- We've jumped from February 29 to a non leap year vDateObject.month = 2 vDateObject.day = 28 end if end case return vDateObject end GetRelativeDate on GetFirstDayOfMonth(aDateObject) ----------------------------------- -- SOURCE: Sent by mGetCalendarText() -- INPUT: should be a date object -- OUTPUT: Returns an integer corresponding to the weekday of the -- first of the month, or an error symbol -------------------------------------------------------------------- if ilk(aDateObject) <> #date then return #invalidDate end if vDayOne = aDateObject.duplicate() vDayOne.day = 1 vDay = GetWeekDay(vDayOne, #integer) return vDay end GetFirstDayOfMonth on GetWeekDay(aDateObject, asInteger) -------------------------------- -- SOURCE: Sent by GetFirstDayOfMonth -- INPUT: only has any effect if it is TRUE or #integer -- OUTPUT: Returns the name of the day of the week defined by -- aDateObject, or a number from 1 (Monday) to 7 (Sunday), -- or an error symbol -- SAMPLE: put GetWeekDay (date(1998, 12, 25)) -- -- "Friday" -- -- put GetWeekDay (date(1998, 12, 25), #integer) -- -- 5 -- NOTE: In English-speaking countries, the Gregorian calendar -- was adopted on Thursday 14 September 1752. We shall use -- that date as our basis of calculation. For dates prior -- to that date, the Julian calendar is used. The dates -- 3 - 13 September 1752 never existed. As a result, the -- calendar for September 1752 will be accurate only for -- 1 - 2 September. -------------------------------------------------------------------- if ilk(aDateObject) <> #date then return #invalidDate end if vStartDate = date(17520914) vElapsedDays = aDateObject - vStartDate if vElapsedDays < 0 then if vElapsedDays < -11 then -- Use the Julian calendar vStartDate = date(0) -- 1 January 0 vElapsedDays = aDateObject - vStartDate else return #dateUndefined end if end if vElapsedDays = vElapsedDays + 4 -- to make Sunday = 1 vIndex = (vElapsedDays mod 7) + 1 case asInteger of TRUE, #integer: return vIndex otherwise vDayNames = GetDayNames() vDayName = vDayNames[vIndex] return vDayName end case end GetWeekDay on GetDayNames(aLanguage, asAbbreviation)----------------------------- -- SOURCE: Sent by mGetCalendarText(), GetWeekDay() -- INPUT: may be a language symbol. If not, the default -- language (English) is used -- only has an effect if it is TRUE. -- OUTPUT: Returns a linear list of week day names -- NOTE: Customize this handler to use a localized date -------------------------------------------------------------------- if integerP(aLanguage) then if voidP(asAbbreviation) then asAbbreviation = (aLanguage <> 0) end if end if if not symbolP(aLanguage) then aLanguage = language end if if not integerP(asAbbreviation) then asAbbreviation = not showFullDay end if case aLanguage of #Deutsch, #German: if asAbbreviation then return ["So", "Mo", "Di", "Mi", "Do", "Fr", "Sa"] else return ["Sonntag", "Montag", "Dienstag", "Mittwoch", "Donnerstag", "Freitag", "Samstag"] end if #Franais, #French: if asAbbreviation then return ["Dim", "Lun", "Mar", "Mer", "Jeu", "Ven", "Sam"] else return ["Dimanche", "Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi", "Samedi"] end if -- Add other languages here otherwise if asAbbreviation = TRUE then return ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"] else return ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday","Saturday"] end if end case end GetDayNames on GetMonthNames(aLanguage, asAbbreviation) -------------------------- -- SOURCE: Sent by mGetCalendarText() and -- getPropertyDescriptionList() -- INPUT: may be a language symbol. If not, the default -- language (English) is used -- only has an effect if it is TRUE. -- OUTPUT: Returns a linear list of month names -- NOTE: Customize this handler to use a localized date -------------------------------------------------------------------- if integerP(aLanguage) then if voidP(asAbbreviation) then asAbbreviation = (aLanguage <> 0) end if end if if not symbolP(aLanguage) then aLanguage = language end if if not integerP(asAbbreviation) then asAbbreviation = not showFullMonth end if -- Customize this handler to use a localized date case aLanguage of #Deutsch, #German: if asAbbreviation then return ["Jan", "Feb", "MŠr", "Apr", "Mai", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov", "Dez"] else return ["Januar", "Februar", "MŠrz", "April", "Mai", "Juni", "July", "August", "September", "Oktober", "November", "Dezember"] end if #Franais, #French: if asAbbreviation then return ["Jan", "FŽv", "Mars", "Avr", "Mai", "Juin", "Juil", "Aožt", "Sep", "Oct", "Nov", "DŽc"] else return ["Janvier", "FŽvrier", "Mars", "Avril", "Mai", "Juin", "Juillet", "Aožt", "Septembre", "Octobre", "Novembre", "DŽcembre"] end if -- Add other languages here otherwise if asAbbreviation then return ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"] else return ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"] end if end case end GetMonthNames on GetDaysInMonth(aMonth, aYear) ------------------------------------- -- SOURCE: Sent by mGetCalendarText, GetRelativeDate, _SetDisplay -- INPUT: should be 1 - 12 -- should be an integer in the range 0 - 9999 -- Alternatively, aMonth can be a date object -- OUTPUT: Returns the number of days in a given month -------------------------------------------------------------------- case ilk(aMonth) of #integer: if aMonth < 1 or aMonth > 12 then return #invalidMonth end if if integerP(aYear) then if aYear < 0 or aYear > 9999 then return #invalidYear end if end if #date: aYear = aMonth.year aMonth = aMonth.month end case vDayList = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] if aYear > 1752 then -- Gregorian calendar in English-speaking world if not (aYear mod 4) then -- Leap year, except for centuries not divisible by 400 if aYear mod 100 then -- Leap year vDayList[2] = 29 else if not (aYear mod 400) then -- Leap century vDayList[2] = 29 end if end if end if vDayCount = vDayList[aMonth] return vDayCount end GetDaysInMonth -- PUBLIC METHODS (responses to #sendSprite, #sendAllSprites, #call) -- on Calendar_SetDisplay(me, aDateObject) ------------------------------ -- INPUT: should be a date object -- ACTION: Displays the month indicated by aDateObject. -- OUTPUT: Returns 0 (for no error) if the language was correctly -- set, or an error symbol. --------------------------------------------------------------------- if ilk(aDateObject) <> #date then return #invalidDate end if pDateObject = aDateObject.duplicate() me.mCreateCalendar() return 0 -- no error end Calendar_SetDisplay on Calendar_SetLanguage(me, aLanguage) ------------------------------- -- INPUT: should be a symbol, but strings are also -- supported -- ACTION: If aLanguage is recognized, changes the language in which -- the calendar displays -- OUTPUT: Returns 0 (for no error) if the language was correctly -- set, or an error symbol. -------------------------------------------------------------------- case ilk(aLanguage) of #symbol: -- continue #string: aLanguage = symbol(aLanguage) otherwise: -- Error check return #invalidSymbol end case vLanguages = me.mGetSupportedLanguages() -- localized if not vLanguages.getPos(aLanguage) then vLanguages = me.mGetSupportedLanguages(TRUE) -- in English if not vLanguages.getPos(aLanguage) then return #unsupportedLanguage end if end if -- End of error check language = aLanguage me.mCreateCalendar() return 0 -- no error end Calendar_SetLanguage on Calendar_SetBrowseMode(me, aBrowseMode) ---------------------------- -- INPUT: should be a symbol: -- ACTION: Modifies the hyperlink navigation mode by setting -- browseMode -- OUTPUT: Return 0 (for no error) or an error symbol -------------------------------------------------------------------- case browseMode of #year, #month, #no: browseMode = aBrowseMode otherwise return #invalidMode end case me.mCreateCalendar() return 0 -- no error end Calendar_SetBrowseMode on Calendar_ToggleFullNames(me, aBoolean, aPeriod) ------------------- -- INPUT: should be set to TRUE if you wish to see names -- in full (eg: Monday, August). Set it to FALSE to see -- short versions (Mon, Aug). If you leave it void, the -- full-name mode will toggle. Toggles or sets the way month -- and weekday names are returned. -- may be #day, #month or #both/VOID -- ACTION: Determines whether day or month names (or both) are -- displayed in long or short form. If both aBoolean and -- aPeriod are void, the full-name mode for months will -- toggle and the weekday mode will be assigned the same -- value. -- OUTPUT: Returns 0 (for no error) or an error symbol -------------------------------------------------------------------- case aPeriod of #day, #month, #both: -- nothing void: aPeriod = #both otherwise return #invalidPeriod end case case (aBoolean) of #integer: case aPeriod of #day: showFullDay = aBoolean #month: showFullMonth = aBoolean #both: showFullMonth = aBoolean showFullDay = aBoolean end case #void: case aPeriod of #day: showFullDay = not showFullDay #month: showFullMonth = not showFullMonth #both: showFullMonth = not showFullMonth showFullDay = showFullMonth end case otherwise: return #integerExpected end case return 0 end Calendar_ToggleFullNames on Calendar_SetCallback(me, aHandler, anObject, aParameter) ---------- -- INPUT: should be a symbol or VOID, or an object -- may be a script, an instance, a list or 0. -- Other values will be ignored -- ACTION: Determines which handler will be called when the user -- clicks on a date. To remove the callback, use VOID as -- aHandler. To remove the callback object, use 0 as either -- aHandler or anObject. To set the callback object without -- changing the handler, use a script, an instance or list -- as the first parameter, and leave the second parameter -- void. -- OUTPUT: Return 0 (for no error) or an error symbol -------------------------------------------------------------------- vIlk = ilk(callback) case ilk(aHandler) of #symbol, #void: callback = aHandler #script, #instance, #list, #integer: if voidP(anObject) then anObject = aHandler end if otherwise return #invalidCallback end case case ilk(anObject) of #script, #instance, #list: pObject = anObject #integer: if anObject = 0 then pObject = VOID end if end case pParameter = aParameter if ilk(callback) <> vIlk then -- Add or remove the hyperlinks for the dates me.mCreateCalendar() end if return 0 -- no error end Calendar_SetBrowseMode on Calendar_SetLimits(me, aStartDate, anEndDate, aNoRefresh) --------- -- INPUT: and should be date objects or 0. -- Alternatively could be a property list with -- the format: -- [#start: , -- #end: ] -- All other values leave the current properties as they are -- can be set to any non-zero value to ignore -- the mCreateCalendar() call. This is useful if you are -- setting both end limits and excluded dates, and you don't -- wish to recreate the calendar twice -- ACTION: Sets startDate and endDate as appropriate. If startDate -- is later than endDate, all dates and navigation will be -- disabled. If either limit date is 0, then there is no -- limit in that direction. -- OUTPUT: Returns 0 (no error) or an error symbol -------------------------------------------------------------------- case ilk(aStartDate) of #date: -- adopt new date #propList: -- Unwrap the properties case ilk(anEndDate) of #date, #integer: otherwise: anEndDate = aStartDate[#end] end case aStartDate = aStartDate[#start] #integer: if aStartDate <> 0 then aStartDate = startDate end if otherwise: aStartDate = startDate end case case ilk(anEndDate) of #date: -- adopt new date #integer: if anEndDate <> 0 then anEndDate = endDate end if otherwise: anEndDate = endDate end case startDate = aStartDate endDate = anEndDate if not aNoRefresh then me.mCreateCalendar() end if return 0 -- no error end Calendar_SetLimits on Calendar_GetLimits(me) -------------------------------------------- -- OUTPUT: Returns a property list with the format: -- [#start: , -- #end: ] -- Duplicate date objects are returned, so that the -- originals cannot be altered externally -------------------------------------------------------------------- vOutput = [:] if not startDate then vOutput[#start] = 0 else vOutput[#start] = startDate.duplicate() end if if not endDate then vOutput[#end] = 0 else vOutput[#end] = endDate.duplicate() end if return vOutput end Calendar_GetLimits on Calendar_ExcludeDates(me, aStartDate, anEndDate, aNoRefresh) ------ -- INPUT: and should be date objects. If -- only one date object is given then both aStartDate and -- anEndDate are set to that date. Alternatively, -- aStartDate can be a property list with the format... -- [#start: , #end: ] -- ... or a linear list of such property lists -- If a list of lists is used, and the first entry in the -- list is [#start: date(99999999), #end: date(0)], then all -- current exclusions are deleted before any subsequent -- exclusions are added. -- can be set to any non-zero value to ignore -- the mCreateCalendar() call. This is useful if you are -- setting both end limits and excluded dates, and you don't -- wish to recreate the calendar twice -- ACTION: Adds the entire period between aStartDate and anEndDate -- to the plExclusions list. Dates from aStartDate to -- anEndDate (inclusive) will appear disabled. If an entire -- calendar month or year is excluded, navigation will -- jump directly to the next non-excluded date. If -- aStartDate is after anEndDate, then the intervening dates -- will be re-enabled. -- SAMPLE: 1. aStartDate = date(2005, 5, 5) -- anEndDate = date(2006, 6, 6) -- All dates from 5 May 2005 to 6 June 2006 (inclusive) -- are excluded. -- 2. aStartDate = date(2005, 10, 10) -- anEndDate = date(2005, 9, 9) -- earlier -- Dates from 10 September 2005 to 9 October are re- -- enabled -- OUTPUT: Returns 0 (no error) or an error symbol -------------------------------------------------------------------- vError = 0 case ilk(aStartDate) of #propList: anEndDate = aStartDate[#end] aStartDate = aStartDate[#start] me.Calendar_ExcludeDates(aStartDate, anEndDate, TRUE) if not aNoRefresh then aNoRefresh = anEndDate <> 0 end if #list: vError = [] vCount = aStartDate.count repeat with i = 1 to vCount vPropList = aStartDate[i] -- may be unique date to exclude vResult = me.Calendar_ExcludeDates(vPropList, 0, TRUE) vError.append(vResult) end repeat if not max(vError) then vError = 0 end if if not aNoRefresh then aNoRefresh = anEndDate <> 0 end if #date: if ilk(anEndDate) <> #date then if not aNoRefresh then aNoRefresh = anEndDate <> 0 end if anEndDate = aStartDate end if -- If we get here, aStartDate and anEndDate are date objects if aStartDate > anEndDate then -- We are "unexcluding" the dates in between, not including -- the limiting dates themselves me.mUnexcludeDates(anEndDate, aStartDate) else me.mExcludeDates(aStartDate, anEndDate) end if otherwise: return #dateExpected end case if not aNoRefresh then me.mCreateCalendar() end if return vError end Calendar_ExcludeDates on Calendar_GetExcludedDates(me) ------------------------------------- -- OUTPUT: Returns a linear list of property lists with the format: -- [[#start: , #end: ], ...] -- None of the dates between #start and #end in each sublist -- can be activated or navigated to. The output list can be -- used as input to Calendar_ExcludeDates to restore the -- current state at a later date. -------------------------------------------------------------------- vOutput = [] vCount = plExclusions.count() repeat with i = 1 to vCount vStart = plExclusions.getPropAt(i) vEnd = plExclusions.getAt(i) vList = [:] vList[#start] = vStart.duplicate() vList[#end] = vEnd.duplicate() vOutput.append(vList) end repeat return vOutput -- plExclusions.duplicate() end Calendar_GetExcludedDates on Calendar_GetDateStatus(me, aDateObject) --------------------------- -- INPUT: should be a date object -- OUTPUT: Returns #available | #excluded | #offLimits | -- #dateExpected, depending on the value of a date object -- and its position relative to startDate, endDate, and the -- entries in plExclusions -------------------------------------------------------------------- if ilk(aDateObject) <> #date then return #dateExpected end if if aDateObject < startDate then return #offLimits else if aDateObject > endDate then return #offLimits end if vIndex = plExclusions.findPos(aDateObject) if not vIndex then vIndex = plExclusions.findPosNear(aDateObject) vIndex = vIndex - 1 end if if vIndex then -- aDateObject occurs at or after the beginning of this period... vEnd = plExclusions[vIndex] if aDateObject <= vEnd then -- ... and before the end of it return #excluded end if end if return #available end Calendar_GetDateStatus on Calendar_FirstAvailableDate(me, aDateObject, aDirection) ---------- -- INPUT: should be a date object. If none is given, -- then the earliest possible date on this calendar is -- used by default -- should be a date other than aDateObject, -1 -- (for earlier) or +1 (for later). All other values are -- treated as +1. -- OUTPUT: Returns the first date that is not excluded, or -- #offLimits if there are no available dates between the -- given date and the start- or endDate. -------------------------------------------------------------------- if voidP(aDateObject) then -- Choose the earliest displayable date on this Calendar aDateObject = startDate if not aDateObject then aDateObject = date(0) end if end if vStatus = me.Calendar_GetDateStatus(aDateObject) -- #available | #excluded | #offLimits | #dateExpected case vStatus of #dateExpected: aDateObject = vStatus #offLimits: aDateObject = vStatus #available: -- The date is available: look no further #excluded: vIndex = plExclusions.findPos(aDateObject) if not vIndex then vIndex = plExclusions.findPosNear(aDateObject) vIndex = vIndex - 1 end if if ilk(aDirection, #date) then aDirection = -(aDirection < aDateObject) end if case aDirection of -1: aDateObject = plExclusions.getPropAt(vIndex) - 1 otherwise: -- +1 aDateObject = plExclusions[vIndex] + 1 end case vStatus = me.Calendar_GetDateStatus(aDateObject) case vStatus of #available: -- continue #offLimits: aDateObject = vStatus otherwise: -- Keep looking return me.Calendar_FirstAvailableDate(aDateObject, aDirection) end case end case return aDateObject end Calendar_FirstAvailableDate on Calendar_GetContiguousDate(me, aSourceDate, aTargetDate) ---------- -- INPUT: should be a date object which is neither -- excluded nor off limits -- should be a date other than aDateObject -- OUTPUT: Returns aTargetDate or a date closer to aSourceDate -- with no intervening excluded dates, or an error symbol -- indicating that aSourceDate is not available -- SAMPLE: Suppose you are hiring a hotel room, and you want to -- know if it is available from date1 to date2. You would -- set up the calendar to exclude dates that had already -- been reserved, then call _GetContiguousDates() with -- date1 and date2 as the parameters. If the output is -- different from date2 then the room is booked out to -- someone else during that period. The output tells you -- the last day that it can now be booked for before the -- existing booking starts. -------------------------------------------------------------------- if ilk(aSourceDate) <> #date then return #dateExpected else if ilk(aTargetDate) <> #date then return #dateExpected end if -- Check if the sourceDate is off limits or excluded. If the -- sourceDate is available then determine what the first and last -- possible contiguous date could be. If there are no limits, then -- use 0. if aSourceDate < startDate then return #offLimits else if aSourceDate > endDate then return #offLimits end if vSourceIndex = plExclusions.findPos(aSourceDate) if vSourceIndex then return #excluded end if vSourceIndex = plExclusions.findPosNear(aSourceDate) if vSourceIndex > plExclusions.count then vLastDate = endDate -- may be 0 else vLastDate = plExclusions.getPropAt(vSourceIndex) - 1 end if vSourceIndex = vSourceIndex - 1 if vSourceIndex then -- The exclusion starts before aStartDate. Does it end after it? vFirstDate = plExclusions[vSourceIndex] + 1 if aSourceDate < vFirstDate then return #excluded end if else vFirstDate = startDate -- may be 0 end if -- If we get here, the date we are looking for must be between -- vFirstDate and vLastDate (either of which may be 0) vTargetIndex = plExclusions.findPos(aTargetDate) if vTargetIndex then -- The target date is excluded else vTargetIndex = plExclusions.findPosNear(aTargetDate) vTargetIndex = vTargetIndex - 1 if not vTargetIndex then vNotExcluded = TRUE else vEnd = plExclusions[vTargetIndex] if aTargetDate > vEnd then vNotExcluded = TRUE end if end if end if if vNotExcluded then if vSourceIndex = vTargetIndex then -- Both dates are in the same zone return aTargetDate end if end if if aTargetDate < aSourceDate then return vFirstDate else return vLastDate end if end Calendar_GetContiguousDate on Calendar_GetBooked(me, aStartDate, anEndDate) --------------------- -- INPUT: and #anEndDate> should be a date objects -- OUTPUT: Returns 0 or a property list with the format... -- [#start: , #end: ] -- ... representing the first and last excluded dates -- between the two given dates. If either of the two given -- dates is itself excluded, it will be returned as the -- #start or #end value in the property list. -- If either date is off limits, #offLimits will be -- returned. -- If the output is not 0, parts of the intervening period -- may in fact be available, but this handler is concerned -- with continuous avalability for the period as a whole. -- SAMPLE: Suppose you are hiring a hotel room, and you want to -- know if it is available from date1 to date2. You would -- set up the calendar to exclude dates that had already -- been reserved, then call Calendar_GetBooked() with -- date1 and date2 as the parameters. If the output is -- not 0, then you can show a dialog asking the user to -- adjust the booking dates to suit availability. -------------------------------------------------------------------- if ilk(aStartDate) <> #date then return #dateExpected else if ilk(anEndDate) <> #date then return #dateExpected end if vUnavailable = [:] if aStartDate > anEndDate then vTemp = aStartDate aStartDate = anEndDate anEndDate = vTemp end if if aStartDate < startDate then return #offLimits else if anEndDate > endDate then return #offLimits end if vIndex = plExclusions.findPosNear(aStartDate) if vIndex = 1 then if vIndex > plExclusions.count() then -- The entire period is available; there are no exclusions return 0 end if vStart = plExclusions.getPropAt(vIndex) if vStart > anEndDate then -- The entire period is available; all exclusions are later return 0 else vUnavailable[#start] = vStart end if else -- aStartDate may be during previous exclusion vEnd = plExclusions[vIndex - 1] if vEnd < aStartDate then -- aStartDate is not excluded if vIndex > plExclusions.count() then -- The entire period is available; all exclusions are earlier return 0 else vStart = plExclusions.getPropAt(vIndex) if vStart > anEndDate then -- The selected period falls in a gap between two exclusions return 0 else vUnavailable[#start] = vStart end if end if else -- aStartDate itself is excluded vUnavailable[#start] = aStartDate end if end if -- If we get here, there is a period of unavailability. When does -- it end? vIndex = plExclusions.findPos(anEndDate) if vIndex then -- anEndDate coincides with the beginning of a new exclusion vUnavailable[#end] = anEndDate else vIndex = plExclusions.findPosNear(anEndDate) vEnd = plExclusions[vIndex - 1] if anEndDate > vEnd then vUnavailable[#end] = vEnd else -- anEndDate itself is excluded vUnavailable[#end] = anEndDate end if end if return vUnavailable end Calendar_GetBooked on Calendar_GetReference(me) -- Returns a reference to the behavior for Lingo calls return me end Calendar_GetReference -- AUTHOR-DEFINED PARAMETERS -- on isOKToAttach(me, spriteType, spriteNumber) if spriteType = #graphic then return (sprite(spriteNumber).member.type = #text) else return FALSE end if end isOKToAttach on getPropertyDescriptionList(me) vLanguages = me.mGetSupportedLanguages() vMonths = GetMonthNames(0) vMonths.addAt(1, "next month") vMonths.addAt(1, "this month") vPropertyList = [:] vPropertyList[ \ #autoDate] = [ \ #comment: "Create Calendar on beginSprite:", \ #format: #boolean, \ #default: TRUE \ ] vPropertyList[ \ #period] = [ \ #comment: "Display entry for:", \ #format: #string, \ #range: vMonths, \ #default: vMonths[1] \ ] vPropertyList[ \ #language] = [ \ #comment: "Display date in which language:", \ #format: #symbol, \ #range: vLanguages, \ #default: vLanguages[1] \ ] vPropertyList[ \ #showFullMonth] = [ \ #comment: "Display month names in full?", \ #format: #boolean, \ #default: TRUE \ ] vPropertyList[ \ #showFullDay] = [ \ #comment: "Display weekday names in full?", \ #format: #boolean, \ #default: FALSE \ ] vPropertyList[ \ #titleSize] = [ \ #comment: "Text size for header:", \ #format: #integer, \ #range: [#min: 9, #max: 36], \ #default: 14 \ ] vPropertyList[ \ #dateSize] = [ \ #comment: "Text size for dates:", \ #format: #integer, \ #range: [#min: 9, #max: 36], \ #default: 12 \ ] vPropertyList[ \ #browseMode] = [ \ #comment: "Enable hypertext links by:", \ #format: #string, \ #range: ["year and month", "month only", "no means"], \ #default: "year and month" \ ] vPropertyList[ \ #startDate] = [ \ #comment: "Earliest active date (YYYYMMDD | 0):", \ #format: #integer, \ #default: 0 \ ] vPropertyList[ \ #endDate] = [ \ # comment: "Latest active date (YYYYMMDD | 0):", \ #format: #integer, \ #default: 0 \ ] vPropertyList[ \ #callback] = [ \ #comment: "Handler to call when a date is clicked:", \ #format: #string, \ #default: "calendarCallback" \ ] return vPropertyList end getPropertyDescriptionList on getBehaviorTooltip(me) return \ "Create a monthly calendar with hyperlinks"&RETURN&\ "to turn to different months. The calendar is"&RETURN&\ "accurate from Jan 1 0 to Dec 31 9999."&RETURN&RETURN&\ "Choose the language the date is displayed in"&RETURN&\ "and whether weekdays and month names appear"&RETURN&\ "in full or abbreviated form."&RETURN&RETURN&\ "To step through the calendar year by year,"&RETURN&\ "click on the year and then on the arrows."&RETURN&\ "To step through the calendar month by month,"&RETURN&\ "click on the name of the month and then on"&RETURN&\ "the arrows. Set start and end limits for"&RETURN&RETURN&\ "navigation."&RETURN&RETURN&\ "Set a handler to be called when the use clicks"&RETURN&RETURN&\ "on a date. Disable dates within a give period."&RETURN&RETURN&\ "This behavior can also be used as an object to"&RETURN&\ "calcu late dates and time intervals." end getBehaviorTooltip on getBehaviorDescription(me) return \ "CALENDAR"&RETURN&RETURN&\ "Drop this behavior onto a Text member to create a calendar. The behavior functions for any date between the year 0 and 9999, except for the month of September 1752. Dates between 1584 and 1752 are treated according to the Julian Calendar."&\ RETURN &RETURN&\ "If you 'Enable hyperlinks to other months', the user can step through the calendar by clicking on '<' or '>' (last and next). To step through months or years, first click on the appropriate date item in the heading. If your set the Earliest or Latest Active Dates, you can limit navigation to a given period. You can also use this to display a calendar for a different year."&\ RETURN&RETURN&\ "By default, a callback is set to trigger whenever the user clicks on a date. Use of a callback also causes the dates to change color on rollover, and to display a finger cursor. Using Lingo, you can disable certain dates. If an entire month is disable, it will remain possible to navigate to that month. However, a Shift-Click on the navigation arrows will take you to the first month where dates are available."&\ RETURN&RETURN&\ "You can easily teach the behavior to speak new languages: consult the last paragraph of the 'Notes for Developers' in the script itself."&RETURN&RETURN&\ "PERMITTED MEMBER TYPES"&RETURN&"Text"&RETURN&RETURN&\ "PARAMETERS:"&RETURN&\ "* Period to display"&RETURN&\ " - this month: displays the calendar for the current month"&RETURN&\ " - next month: displays the calendar for the coming month"&RETURN&\ " - month: displays the calendar for the given month of the current year"&RETURN&\ " (This setting may be overruled by Earliest and Latest dates)"&RETURN&\ "* Language to use for displaying dates (default = English)"&RETURN&\ "* Display weekdays and month names in full or short form"&RETURN&\ "* Choose text size for title and dates"&RETURN&\ "* Allow navigation to other dates"&RETURN&\ " (year and month | month only | none)"&RETURN&\ "* Set earliest and latest dates for navigation"&RETURN&\ "* Set callback to make dates interactive"&RETURN&RETURN&\ "PUBLIC METHODS:"&RETURN&\ "=> Display calendar for any month between 0 and 9999"&RETURN&\ "=> Change the language in which the calendar displays"&RETURN&\ "=> Display month and weekday names in full or short form"&RETURN&\ "=> Enable/disable navigation to other dates"&RETURN&\ "=> Return an object reference to the behavior"&RETURN&\ "=> Define a callback handler if the user clicks on a date"&RETURN&\ "=> Set and Get the start and end limits of navigation"&RETURN&\ "=> Exclude specific dates or whole periods"&RETURN&\ "=> Get a list of excluded dates"&RETURN&\ "=> Check if a date is off limits, excluded or available"&RETURN&\ "=> Find the first available date before or after a given date" end getBehaviorDescription --34567890123456789012345678901234567890123456789012345678901234567890