-module(html_calendar). -export([cal/2,extract_date/1]). -import(lists,[map/2,foldl/3]). % The macro's below can be user defined %%%%%%%%%%%%%%%%%%%%%%%%%% % but are server dependent, i.e., defined once, all calendars on that % server will look the similar (depending on type setting commands % in the style file. -define(FIRSTDAY,{1997,8,25}). -define(LASTDAY,element(1,calendar:local_time())). -define(OPENDAY,element(1,calendar:local_time())). -define(PreviousButton,"Previous"). -define(NoPreviousButton," "). -define(NextButton,"Next"). -define(NoNextButton," "). -define(LeftButton,"<"). -define(NoLeftButton," "). -define(RightButton,">"). -define(NoRightButton," "). -define(CAL,{class,"calendar"}). % typeset with calendar class % % the tags to define in the class definition in html are, % either all, or table, tr, td and a % % EXAMPLE: % % The macro's above can be user defined %%%%%%%%%%%%%%%%%%%%%%%%%% % Do not change the macro's below in the code, they can be % manipulated by the function call, such that different % calendars may exist on the same server. -define(MONTHS,["Jan","Feb","Mar","Apr","May","Jun","Jul", "Aug","Sep","Oct","Nov","Dec"]). % The system assumes that MONDAY is day 1 (inherited from calendar.erl) -define(WEEKDAYS,["m","t","w","t","f","s","s"]). % cal(yaws_record(),Options) -> {ehtml,ehtml()}. % where Options = % [{monday_color,color_string()} | ... | % {sunday_color,color_string()} | % {monday_bgcolor,color_string() | ... | % {sunday_bgcolor,color_string() | % {today_color,color_string()} | % {today_bgcolor,color_string()} | % {months,[month_string()]} | % {weekdays,[weekday_string()]}] % % EXAMPLE: % out(A) -> html_calendar:cal(A, % [{sunday_bgcolor,"#FF0000"}, % {today_color,"#FFFF00"}, % {weekdays,["må","ti","on","to",fr","lö","sö"]}]). cal(Arg,Options) -> Date = extract_date(Arg), Months = alternative(Options,months,?MONTHS), Weekdays = alternative(Options,weekdays,?WEEKDAYS), Colors = days_to_numbers(Options), {ehtml,{table,[?CAL], [{tr,[?CAL],[ {td,[?CAL,{align,"left"},{colspan,"3"}], ref_yesterday(Date)}, {td,[?CAL,{align,"right"},{colspan,"4"}], ref_tomorrow(Date)} ]}, {tr,[?CAL],[ {td,[?CAL,{colspan,"7"}]," "} ]}, {tr,[?CAL],[ {td,[?CAL,{colspan,"2"},{align,"right"}], ref_lastmonth(Date)}, {td,[?CAL,{colspan,"3"},{align,"center"}], lists:nth(month(Date),Months)}, {td,[?CAL,{colspan,"2"},{align,"left"}], ref_nextmonth(Date)} ]}, {tr,[?CAL],[ {td,[?CAL,{colspan,"2"},{align,"right"}], ref_lastyear(Date)}, {td,[?CAL,{colspan,"3"},{align,"center"}], integer_to_list(year(Date))}, {td,[?CAL,{colspan,"2"},{align,"left"}], ref_nextyear(Date)} ]}, {tr,[?CAL],[{td,[?CAL,{colspan,"7"}]," "}]}, {tr,[?CAL], foldl(fun(D,TDs) -> TDs++ [{td,[?CAL,{align,"right"}| check_colors(Colors,length(TDs)+1,false)], {text, check_colors(Colors,length(TDs)+1,false),D}}] end,[],Weekdays)}, map(fun(Week) -> {tr,[?CAL],the_days(Week,Date,Colors,1)} end, weeks(Date)) ]}}. extract_date(Arg) -> PostedDate = case catch lists:keysearch(date,1,yaws_api:parse_query(Arg)) of {value, {date, [Y1,Y2,Y3,Y4,M1,M2,D1,D2]}} -> Year = list_to_integer([Y1,Y2,Y3,Y4]), Month = list_to_integer([M1,M2]), Day = list_to_integer([D1,D2]), case calendar:valid_date(Year,Month,Day) of true -> {Year,Month,Day}; false -> ?OPENDAY end; _ -> ?OPENDAY end. ref_yesterday(Date) -> case Date > ?FIRSTDAY of true -> {a,[?CAL,date_to_ref(yesterday(Date))], ?PreviousButton}; false -> ?NoPreviousButton end. ref_tomorrow(Date) -> case Date < ?LASTDAY of true -> {a,[?CAL,date_to_ref(tomorrow(Date))], ?NextButton}; false -> ?NoNextButton end. ref_lastyear(Date) -> case lastyear(Date) > ?FIRSTDAY of true -> {a,[?CAL,date_to_ref(lastyear(Date))], ?LeftButton}; false -> ?NoLeftButton end. ref_nextyear(Date) -> case nextyear(Date) < ?LASTDAY of true -> {a,[?CAL,date_to_ref(nextyear(Date))], ?RightButton}; false -> ?NoRightButton end. ref_lastmonth(Date) -> case lastmonth(Date) > ?FIRSTDAY of true -> {a,[?CAL,date_to_ref(lastmonth(Date))], ?LeftButton}; false -> ?NoLeftButton end. ref_nextmonth(Date) -> case nextmonth(Date) < ?LASTDAY of true -> {a,[?CAL,date_to_ref(nextmonth(Date))], ?RightButton}; false -> ?NoRightButton end. the_days([],_,_,_) -> []; the_days([0|Ds],Date,Colors,WeekDay) -> [{td,[?CAL],""}|the_days(Ds,Date,Colors,WeekDay+1)]; the_days([D|Ds],Date,Colors,WeekDay) -> {YY,MM,_} = Date, % ugly abstraction violation Today = ({YY,MM,D} == element(1,calendar:local_time())), [{td,[?CAL,{align,right}|check_colors(Colors,WeekDay,Today)], case (?FIRSTDAY =< {YY,MM,D}) and ({YY,MM,D} =< ?LASTDAY) of true -> {a,[?CAL,date_to_ref({YY,MM,D})| check_colors(Colors,WeekDay,Today)], integer_to_list(D)}; false -> {text,check_colors(Colors,WeekDay,Today), integer_to_list(D)} end}|the_days(Ds,Date,Colors,WeekDay+1)]. check_colors(Colors,N,Today) -> FG = case Today of true -> case check_color(Colors,0) of [] -> check_color(Colors,N); FGColor -> FGColor end; false -> check_color(Colors,N) end, BG = case Today of true -> case check_bgcolor(Colors,0) of [] -> check_bgcolor(Colors,N); BGColor -> BGColor end; false -> check_bgcolor(Colors,N) end, case join(FG++BG,"; ") of "" -> []; Style -> [{style,Style}] end. check_color(Colors,N) -> case lists:keysearch(N,1,[ {D,V} || {color,D,V}<-Colors ]) of {value,{_,FGColor}} -> ["color:"++FGColor]; _ -> [] end. check_bgcolor(Colors,N) -> case lists:keysearch(N,1,[ {D,V} || {bgcolor,D,V}<-Colors ]) of {value,{_,BGColor}} -> ["background-color:"++BGColor]; _ -> [] end. % destructors %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % year({Y,M,D}) -> Y. month({Y,M,D}) -> M. day({Y,M,D}) -> D. % constructors %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % lastyear({Year,Month,Day}) -> {Year-1,Month,Day}. nextyear({Year,Month,Day}) -> {Year+1,Month,Day}. lastmonth({Year,Month,Day}) -> case Month==1 of true -> {Year-1,12,Day}; false -> NrDays = calendar:last_day_of_the_month(Year,Month-1), case Day>NrDays of true -> {Year,Month-1,NrDays}; false -> {Year,Month-1,Day} end end. nextmonth({Year,Month,Day}) -> case Month==12 of true -> {Year+1,1,Day}; false -> NrDays = calendar:last_day_of_the_month(Year,Month+1), case Day>NrDays of true -> {Year,Month+1,NrDays}; false -> {Year,Month+1,Day} end end. yesterday({Year,Month,Day}) -> case Day==1 of true -> lastmonth({Year,Month,31}); false -> {Year,Month,Day-1} end. tomorrow({Year,Month,Day}) -> case calendar:last_day_of_the_month(Year,Month) == Day of true -> nextmonth({Year,Month,1}); false -> {Year,Month,Day+1} end. weeks(Date) -> {Year,Month,Day} = Date, Days = nodays(calendar:day_of_the_week(Year,Month,1))++ lists:seq(1,calendar:last_day_of_the_month(Year,Month)), splitweek(Days). nodays(1) -> []; nodays(N) when N>1 -> [0|nodays(N-1)]. splitweek([]) -> []; splitweek(Days) -> [heads(7,Days)|splitweek(tails(7,Days))]. heads(N,[]) -> []; heads(0,Xs) -> []; heads(N,[X|Xs]) -> [X|heads(N-1,Xs)]. tails(N,[]) -> []; tails(0,Xs) -> Xs; tails(N,[X|Xs]) -> tails(N-1,Xs). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% date_to_ref(Date) -> {href,"?date="++ integer_to_list(year(Date))++ case month(Date)<10 of true -> "0"; false -> "" end ++ integer_to_list(month(Date))++ case day(Date)<10 of true -> "0"; false -> "" end ++integer_to_list(day(Date))}. join([],Sep) -> ""; join([S],Sep) -> S; join([S|Ss],Sep) -> S++Sep++join(Ss,Sep). alternative(KeyList,Key,Default) -> case lists:keysearch(Key,1,KeyList) of {value,{_,Value}} -> if is_list(Value) and is_list(Default) -> case length(Value)==length(Default) of true -> Value; false -> Default end; true -> Value end; _ -> Default end. % The price to pay for readable yaws pages % days_to_numbers(Options) -> foldl(fun({K,V},Cs) -> case K of today_color -> [{color,0,V}|Cs]; today_bgcolor -> [{bgcolor,0,V}|Cs]; monday_color -> [{color,1,V}|Cs]; monday_bgcolor -> [{bgcolor,1,V}|Cs]; tuesday_color -> [{color,2,V}|Cs]; tuesday_bgcolor -> [{bgcolor,2,V}|Cs]; wednesday_color -> [{color,3,V}|Cs]; wednesday_bgcolor -> [{bgcolor,3,V}|Cs]; thursday_color -> [{color,4,V}|Cs]; thursday_bgcolor -> [{bgcolor,4,V}|Cs]; friday_color -> [{color,5,V}|Cs]; friday_bgcolor -> [{bgcolor,5,V}|Cs]; saterday_color -> [{color,6,V}|Cs]; saterday_bgcolor -> [{bgcolor,6,V}|Cs]; sunday_color -> [{color,7,V}|Cs]; sunday_bgcolor -> [{bgcolor,7,V}|Cs] end end,[],Options).