Monday, November 08, 2010 at 12:05 AM.

system.verbs.builtins.radio.weblog.drawCalendar

on drawCalendar (adrblog=radio.weblog.init (), d=clock.now (), archiveFileExtension="html") {
	<<Changes:
		<<12/3/02; 3:59:11 PM by JES
			<<Use the CSS class hCalendarDayRow for all calendar day rows, not just the first one.
		<<12/3/02; 3:50:47 PM by JES
			<<Don't add an empty table row for months which start on Sunday. Fixes an HTML 4.01 Transitional validation error.
		<<2/8/02; 2:45:27 AM by JES
			<<Fixed a bug where days which had category posts, but none of the posts appeared on the home page, would get links in the home page archive calendar even though they shouldn't.
		<<1/27/02; 4:30:57 PM by JES
			<<Fixed a bug where one too few table cells were added at to the last day-row, when the last day of the month is not a Saturday.
		<<1/25/02; 8:29:38 PM by JES
			<<Only return the cached home page calendar, when there's no date argument present in the URL. Fixes a bug where navigating using the calendar links in the desktop website home page would not update the calendar.
		<<1/24/02; 8:01:54 PM by JES
			<<New optional parameter, archiveFileExtension. Defaults to "html", and replaces the hard-coded local declared within the script. If pta^.renderedFileExtension is defined, use its value instead of the value of the archiveFileExtension parameter.
		<<1/22/02; 3:30:35 PM by JES
			<<Cache the calendar text for the dynamic home page. The dynamic home page calendar is now rendered a maximum of once per day. This accounts for as much as a 40% improvement in rendering speed for the desktop website home page.
		<<1/6/02; 2:05:30 AM by JES
			<<Added css class names to calendar table elements.
		<<1/5/02; 9:43:26 PM by JES
			<<Localized month and day of week names in the calendar.
		<<11/28/01; 12:51:51 AM by JES
			<<Fixed bad next/prev month links on category pages.
		<<11/17/01; 2:44:12 AM by JES
			<<Pass catname to calls to radio.weblog.getLastPostBeforeDate. This fixes the bug where the previous month would be improperly linked on category pages which had no posts for the previous month.
		<<11/14/01; 2:45:21 PM by JES
			<<Fixed the links to work with both weblog archive pages, and category archive pages.
		<<11/3/01; 11:02:32 PM by JES
			<<Fixed an error if there are no posts in the weblog.
		<<11/1/01; 2:16:02 AM by JES
			<<Fixed a bug where if there were no posts for the current day, this verb would throw an error.
		<<10/30/01; 11:15:17 AM by PBS
			<<Fixed a bug introduced yesterday where one or more of the first days with posts would be left off the calendar.
		<<10/29/01; 8:38:09 PM by PBS
			<<Major speed boost. Don't call radio.weblog.getNextPostAfterDate for each day. Instead, call it for the first day in the month, then keep the index into the posts table, and increment as needed to find posts.
		<<9/3/01; 3:59:02 PM by JES
			<<If pta^.radioResponder.weblogDay is defined, and is non-nil, use its value for the value of d (the current date). This is needed in order for the calendar to render properly on the archive pages.
		<<9/3/01; 3:15:30 AM by JES
			<<If the page is being statically rendered, then link to the static archive pages, instead of the dynamic ones.
		<<8/31/01; 12:56:23 AM by JES
			<<Preserve searchArgs so the Editors Only menu will retain its state.
		<<3/1/01; 4:03:49 PM by PBS
			<<Fixed an error getting the last post in a range.
		<<2/26/01; 3:04:26 PM by PBS
			<<Highlight the background of the current day, since the bold face wasn't showing up very well.
		<<2/23/01; 10:05:55 PM by JES
			<<Fixed an error if there are no posts in the weblog. Increased the space between the month name links below the calendar.
		<<2/22/01; 9:29:06 PM by PBS
			<<The current day is in bold face, not a link.
		<<2/22/01; 4:26:02 PM by PBS
			<<Draw a calendar for the month that contains d. The day specified by d is in bold-face and not a link.
	
	local (pta = html.getPageTableAddress ());
	if defined (pta^.renderedFileExtension) {
		archiveFileExtension = pta^.renderedFileExtension;
		if archiveFileExtension beginsWith "." { //it's expected not to start with '.'
			archiveFileExtension = string.delete (archiveFileExtension, 1, 1)}};
	<<workspace.pt = pta^; edit (@workspace.pt)
	<<if defined (pta^.radioResponder.weblogDay)
		<<if pta^.radioResponder.weblogDay != nil
			<<d = pta^.radioResponder.weblogDay
	<<local (extension = "html")
	local (flArchivePage = false);
	bundle { //set flArchivePage
		<<if pta^.radioResponder.flStaticRendering
		if defined (pta^.flArchivePage) {
			flArchivePage = pta^.flArchivePage}};
	local (catname = "");
	if defined (pta^.categoryName) { //set catname
		catname = pta^.categoryName};
	if defined (pta^.archiveDate) { //set d
		local (yr = string.nthField (pta^.archiveDate, "/", 1));
		local (mo = string.nthField (pta^.archiveDate, "/", 2));
		local (day = string.nthField (pta^.archiveDate, "/", 3));
		d = date.set (day, mo, yr, 0, 0, 0)};
	
	local (adrincache);
	bundle { //possibly set adrincache -- only for the dynamic home page
		if not defined (adrblog^.cache) {
			new (tableType, @adrblog^.cache)};
		local (adrcache = @adrblog^.cache.calendar);
		if not defined (adrcache^) {
			new (tableType, adrcache)};
		if not flArchivePage {
			if catname == "" {
				if not pta^.radioResponder.flStaticRendering {
					adrincache = @adrcache^.dynamicHomePage;
					if not defined (adrincache^) {
						new (tableType, adrincache)};
					if not defined (adrincache^.d) {
						adrincache^.d = date (0)};
					if not defined (adrincache^.htext) {
						new (wpTextType, @adrincache^.htext)}}}}};
	bundle { //possibly return the calendar HTML from the cache
		if adrincache != nil {
			if not defined (pta^.radioResponder.getArgs.d) {
				if date.sameDay (d, adrincache^.d) {
					return (string (adrincache^.htext))}}}};
	
	local (htmlText = "", indentLevel = 0);
	on add (s) {
		htmlText = htmlText + (string.filledString ("\t", indentLevel) + s + "\r");};
	
	local (args = "");
	bundle { //get searchArgs string without the d= element
		if defined (pta^.radioResponder.getArgs) {
			local (t = pta^.radioResponder.getArgs);
			if defined (t.d) {
				delete (@t.d)};
			if defined (t.itemToEdit) {
				delete (@t.itemToEdit)};
			args = webserver.encodeArgs (@t);
			if args != "" {
				args = "&" + args}}};
	
	bundle { //set d based on searchArgs
		try {
			local (pta = html.getPageTableAddress ());
			local (args);
			new (tabletype, @args);
			webserver.parseargs (pta^.searchargs, @args);
			if defined (args.d) {
				local (yr = string.nthField (args.d, '/', 1));
				local (mo = string.nthField (args.d, '/', 2));
				local (dy = string.nthField (args.d, '/', 3));
				d = date.set (dy, mo, yr, 0, 0, 0)}}};
	
	local (day, month, year, hour, minute, second);
	date.get (d, @day, @month, @year, @hour, @minute, @second);
	
	local (timeFirst = date.set (1, month, year, 0, 0, 0));
	local (lastDay = date.daysInMonth (d));
	local (timeLast = date.set (lastDay, month, year, 23, 59, 59));
	
	local (adrFirst);
	radio.weblog.getNextPostAfterDate (adrBlog, timeFirst, @adrFirst); //adrFirst == nil if no posts
	local (dayFirstPost);
	if adrFirst == nil {
		if sizeOf (adrblog^.posts) > 0 { //don't error if there are no posts
			adrBlog^.posts[sizeOf (adrBlog^.posts)].when}}
	else {
		dayFirstPost = adrFirst^.when};
	date.get (dayFirstPost, @day, @month, @year, @hour, @minute, @second);
	dayFirstPost = date.set (day, month, year, 0, 0, 0);
	local (adrLast);
	radio.weblog.getLastPostBeforeDate (adrblog, timeLast, @adrLast, catname);
	if sizeOf (archiveFileExtension) > 0 {
		archiveFileExtension = "." + archiveFileExtension};
	
	bundle { //draw the month and year
		add ("<table cellspacing=\"0\" border=\"0\" class=\"hCalendarTable\">"); indentLevel++;
		local (monthYearString = radio.string.getLocalizedString ("month." + string.padWithZeros (month, 2)) + " " + year);
		add ("<tr class=\"hCalendarMonthYearRow\"><td colspan=\"7\" align=\"center\"><b>" + monthYearString + "</b></td></tr>")};
	bundle { //draw the day of week names
		add ("<tr class=\"hCalendarDayNameRow\">"); indentLevel++;
		on addDayName (name) {
			add ("<td width=\"19\" height=\"10\" align=\"center\" style=\"font-size:9px\">" + name + "</td>")};
		local (i);
		for i = 1 to 7 {
			addDayName (radio.string.getLocalizedString ("dayOfWeekShort." + i))};
		add ("</tr>"); indentLevel--};
	bundle { //draw the days of the month
		local (midnight);
		bundle { //we need midnight of the day specified by d
			local (day, month, year, hour, minute, second);
			date.get (d, @day, @month, @year, @hour, @minute, @second);
			midnight = date.set (day, month, year, 0, 0, 0)};
		local (i, ct = 1);
		local (ixPost = indexOf (adrFirst^)); //PBS 10/30/01: index of first post in month in posts table
		local (adrPosts = parentOf (adrFirst^));
		on newWeek () {
			add ("</tr>"); indentLevel--;
			add ("<tr class=\"hCalendarDayRow\">"); indentLevel++};
		on addDay (ix) {
			local (link = ix);
			local (thisDay = date.set (ix, month, year, 0, 0, 0));
			local (flHighlight = false);
			local (classname = "hCalendarDay");
			if thisDay == midnight { //the current day is in bold, not a link
				flHighlight = true}
				<<link = "<font color=\"red\">" + ix + "</font>" //PBS 02/26/01: use red, not bold, since the bold didn't show up very well
			else {
				if thisDay >= dayFirstPost {
					local (adrPost);
					if ixPost == 0 {
						if radio.weblog.getNextPostAfterDate (adrblog, thisDay, @adrPost) {
							ixPost = indexOf (adrPost^);
							adrPosts = parentOf (adrPost^)}}
					else {
						loop {
							if ixPost > sizeOf (adrPosts^) {
								break};
							adrPost = @adrPosts^ [ixPost];
							local (whenOnePost = adrPost^.when);
							if whenOnePost >= thisDay {
								if whenOnePost < date.tomorrow (thisDay) {
									local (flLink = false);
									if catname == "" {
										if pta^.radioResponder.flStaticRendering {
											if defined (adrpost^.flNotOnHomePage) {
												if not adrpost^.flNotOnHomePage {
													flLink = true}}
											else {
												flLink = true}}
										else {
											flLink = true}}
									else { //category
										if defined (adrpost^.categories) {
											if defined (adrpost^.categories.[catname]) {
												flLink = adrpost^.categories.[catname]}}};
									if flLink {
										classname = "hCalendarDayLinked";
										local (url = year + "/" + string.padWithZeros (month, 2) + "/" + string.padWithZeros (ix, 2) + args);
										if flArchivePage { //relative link from archive page
											url = "../../" + url};
										if pta^.radioResponder.flStaticRendering {
											url = url + archiveFileExtension}
										else { //dynamic page
											if pta^.path == radio.data.systemUrls.weblogEditor {
												url = "?d=" + url}};
										link = html.getLink (ix, url);
										break}}
								else { //no posts for this day [for this category]
									break}};
							ixPost++}}}};
			if flHighlight { //PBS 02/26/01: put a background color behind the current day
				add ("<td class=\"hCalendarDayCurrent\" align=\"center\" bgcolor=\"gainsboro\" style=\"font-size:9px\">" + link + "</td>")}
			else {
				add ("<td class=\"" + classname + "\" align=\"center\" style=\"font-size:9px\">" + link + "</td>")}};
		for i = 1 to lastDay {
			if ct == 1 {
				add ("<tr class=\"hCalendarDayRow\">");
				local (firstDayIndex = date.dayOfWeek (timeFirst));
				local (j);
				for j = 1 to firstDayIndex - 1 {
					add ("<td> </td>");
					ct++}};
			if (mod (ct, 7) == 1) and (i > 1) { //JES 12/3/02: Don't add an empty row for months that start on Sun
				newWeek ()};
			addDay (i);
			ct++};
		while (mod (ct - 1, 7) != 0) {
			ct++;
			add ("<td> </td>")};
		add ("</tr>"); indentLevel--};
	bundle { //draw links to next and previous months
		local (adrBeforeFirst);
		radio.weblog.getLastPostBeforeDate (adrblog, timeFirst, @adrBeforeFirst, catname);
		local (ixPrevMonth);
		ixPrevMonth = indexOf (adrBeforeFirst^);
		local (ixNextMonth);
		if catname == nil {
			ixNextMonth = indexOf (adrLast^) + 1}
		else {
			local (firstPostNextMonth);
			if radio.weblog.getNextPostAfterDate (adrblog, date (number (timeLast) + 1), @firstPostNextMonth, catname) {
				ixNextMonth = indexOf (firstPostNextMonth^)}
			else {
				ixNextMonth = sizeOf (adrblog^.posts) + 1}};
		local (flPrev = false, flNext = false);
		local (monthLinks);
		on shortMonthName (d) {
			local (day, month, year, hour, minute, second);
			date.get (d, @day, @month, @year, @hour, @minute, @second);
			local (monthName = radio.string.getLocalizedString ("monthShort." + string.padWithZeros (month, 2)));
			<<local (monthName = date.monthToString (month))
			<<monthName = string.mid (monthName, 1, 3)
			return (monthName)};
		on monthLink (d) {
			local (day, month, year, hour, minute, second);
			date.get (d, @day, @month, @year, @hour, @minute, @second);
			local (url = year + "/" + string.padWithZeros (month, 2) + "/" + string.padWithZeros (day, 2) + args);
			if flArchivePage { //relative link from archive page
				url = "../../" + url};
			if pta^.radioResponder.flStaticRendering {
				url = url + archiveFileExtension}
			else {
				if pta^.path == radio.data.systemUrls.weblogEditor {
					url = "?d=" + url}};
			local (link = html.getLink (shortMonthName (d), url));
			return (link)};
		if ixPrevMonth > 0 {
			monthLinks = monthLink (adrblog^.posts [ixPrevMonth].when)}
		else {
			monthLinks = shortMonthName (date.prevMonth (d))};
		monthLinks = monthLinks + "   ";
		if ixNextMonth <= sizeOf (adrblog^.posts) {
			monthLinks = monthLinks + monthLink (adrblog^.posts [ixNextMonth].when)}
		else {
			monthLinks = monthLinks + shortMonthName (date.nextMonth (d))};
		add ("<tr><td colspan=\"7\" align=\"center\" style=\"font-size:9px\">" + monthLinks + "</td></tr>")};
	
	add ("</table>"); indentLevel--;
	
	bundle { //possibly set the cache
		if adrincache != nil {
			if not defined (pta^.radioResponder.getArgs.d) {
				wp.newTextObject (htmltext, @adrincache^.htext);
				adrincache^.d = d}}};
	
	return (htmlText)}
<<bundle //test code
	<<webBrowser.displayText (drawCalendar (d:clock.now ()))
	<<webBrowser.displayText (drawCalendar (d:date.prevmonth (clock.now ())))
<<bundle //more test code
	<<html.setPageTableAddress (@workspace.pt)
	<<wp.newTextObject (drawCalendar (), @workspace.calendarHtml); edit (@workspace.calendarHtml)
	<<html.deletePageTableAddress ()



This listing is for code that runs in the OPML Editor environment. I created these listings because I wanted the search engines to index it, so that when I want to look up something in my codebase I don't have to use the much slower search functionality in my object database. Dave Winer.