1 /* -*- Mode: javascript; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 *
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
9 *
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
13 * License.
14 *
15 * The Original Code is Mozilla Calendar code.
16 *
17 * The Initial Developer of the Original Code is
18 * Joey Minta <jminta@gmail.com>
19 * Portions created by the Initial Developer are Copyright (C) 2006
20 * the Initial Developer. All Rights Reserved.
21 *
22 * Contributor(s):
23 * Matthew Willis <lilmatt@mozilla.com>
24 *
25 * Alternatively, the contents of this file may be used under the terms of
26 * either the GNU General Public License Version 2 or later (the "GPL"), or
27 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 * in which case the provisions of the GPL or the LGPL are applicable instead
29 * of those above. If you wish to allow use of your version of this file only
30 * under the terms of either the GPL or the LGPL, and not to allow others to
31 * use your version of this file under the terms of the MPL, indicate your
32 * decision by deleting the provisions above and replace them with the notice
33 * and other provisions required by the GPL or the LGPL. If you do not delete
34 * the provisions above, a recipient may use your version of this file under
35 * the terms of any one of the MPL, the GPL or the LGPL.
36 *
37 * ***** END LICENSE BLOCK ***** */
38
39 /**
40 * Prints a two column view of a week of events, much like a paper day-planner
41 */
42
43 function calWeekPrinter() {
44 this.wrappedJSObject = this;
45 }
46
47 calWeekPrinter.prototype.QueryInterface =
48 function QueryInterface(aIID) {
49 if (!aIID.equals(Components.interfaces.nsISupports) &&
50 !aIID.equals(Components.interfaces.calIPrintFormatter)) {
51 throw Components.results.NS_ERROR_NO_INTERFACE;
52 }
53
54 return this;
55 };
56
57 calWeekPrinter.prototype.getName =
58 function weekPrint_getName() {
59 return calGetString("calendar", "weekPrinterName");
60 };
61 calWeekPrinter.prototype.__defineGetter__("name", calWeekPrinter.prototype.getName);
62
63 calWeekPrinter.prototype.formatToHtml =
64 function weekPrint_format(aStream, aStart, aEnd, aCount, aItems, aTitle) {
65 // Create the e4x framework of the HTML document
66 var html = <html/>;
67 html.appendChild(
68 <head>
69 <title>{aTitle}</title>
70 <meta http-equiv='Content-Type' content='text/html; charset=UTF-8'/>
71 <style type='text/css'/>
72 </head>);
73 html.head.style = ".main-table { font-size: 26px; font-weight:bold; }\n";
74 html.head.style += ".day-name { border: 1px solid #000; background-color: #e0e0e0; font-size: 12px; font-weight: bold; }\n";
75
76 var body = <body/>;
77
78 // helper: returns the passed item's startDate, entryDate or dueDate, in
79 // that order. If the item doesn't have one of those dates, this
80 // doesn't return.
81 function hasUsableDate(item) {
82 return item.startDate || item.entryDate || item.dueDate;
83 }
84
85 // Clean out the item list so it only contains items we will want to
86 // include in the printout.
87 var filteredItems = aItems.filter(hasUsableDate);
88
89 var calIEvent = Components.interfaces.calIEvent;
90 var calITodo = Components.interfaces.calITodo
91 function compareItems(a, b) {
92 // Sort tasks before events
93 if (a instanceof calIEvent && b instanceof calITodo) {
94 return 1;
95 }
96 if (a instanceof calITodo && b instanceof calIEvent) {
97 return -1;
98 }
99 if (a instanceof calIEvent) {
100 var startCompare = a.startDate.compare(b.startDate);
101 if (startCompare != 0) {
102 return startCompare;
103 }
104 return a.endDate.compare(b.endDate);
105 }
106 var dateA = a.entryDate || a.dueDate;
107 var dateB = b.entryDate || b.dueDate;
108 return dateA.compare(dateB);
109 }
110 var sortedList = filteredItems.sort(compareItems);
111
112 var weekFormatter = Components.classes["@mozilla.org/calendar/weektitle-service;1"]
113 .getService(Components.interfaces.calIWeekTitleService);
114
115 // Start at the beginning of the week that aStart is in, and loop until
116 // we're at aEnd. In the loop we build the HTML table for each day, and
117 // get the day's items using getDayTd().
118 var start = aStart || sortedList[0].startDate || sortedList[0].entryDate ||
119 sortList[0].dueDate;
120 ASSERT(start, "can't find a good starting date to print");
121
122 var lastItem = sortedList[sortedList.length-1];
123 var end = aEnd || lastItem.startDate || lastItem.entryDate ||
124 lastItem.dueDate;
125 ASSERT(end, "can't find a good ending date to print");
126
127 var date = start.startOfWeek;
128 var startOfWeek = getPrefSafe("calendar.week.start", 0);
129 date.day += startOfWeek;
130 // Make sure we didn't go too far ahead
131 if (date.compare(start) == 1) {
132 date.day -= 7;
133 }
134
135 while(date.compare(end) == -1) {
136 var weekno = weekFormatter.getWeekTitle(date);
137 var weekTitle = calGetString("calendar", 'WeekTitle', [weekno]);
138 body.appendChild(
139 <table border='0' width='100%' class='main-table'>
140 <tr>
141 <td align='center' valign='bottom'>{weekTitle}</td>
142 </tr>
143 </table>);
144 var mainWeek = <table width='100%' height="90%" border='solid 1px;'/>
145
146 // Create the <td> for each day, and put it into an array.
147 var dayTds = new Array();
148 for (var i = 0; i < 7 ; i++) {
149 dayTds[date.weekday] = this.getDayTd(date, sortedList);
150 date.day += 1;
151 }
152
153 var monRow = <tr height="33%"/>;
154 monRow.appendChild(dayTds[1]); // Monday
155 monRow.appendChild(dayTds[4]); // Thursday
156 mainWeek.appendChild(monRow);
157
158 var tueRow = <tr height="33%"/>;
159 tueRow.appendChild(dayTds[2]); // Tuesday
160 tueRow.appendChild(dayTds[5]); // Friday
161 mainWeek.appendChild(tueRow);
162
163 var wedRow = <tr height="33%"/>;
164 wedRow.appendChild(dayTds[3]); // Wednesday
165
166 // Saturday and Sunday are half-size
167 var satSunTd = <td height="33%"/>;
168 var weekendTable = <table border="1" width="100%" height="100%"/>;
169
170 var satRow = <tr valign='top'/>;
171 satRow.appendChild(dayTds[6]); // Saturday
172 weekendTable.appendChild(satRow);
173
174 var sunRow = <tr valign='top'/>;
175 sunRow.appendChild(dayTds[0]); // Sunday
176 weekendTable.appendChild(sunRow);
177
178 satSunTd.appendChild(weekendTable);
179 wedRow.appendChild(satSunTd);
180 mainWeek.appendChild(wedRow);
181
182 body.appendChild(mainWeek);
183 // Make sure each month gets put on its own page
184 body.appendChild(<br style="page-break-after: always;"/>);
185 }
186 html.appendChild(body);
187
188 // Stream out the resulting HTML
189 var convStream = Components.classes["@mozilla.org/intl/converter-output-stream;1"]
190 .getService(Components.interfaces.nsIConverterOutputStream);
191 convStream.init(aStream, 'UTF-8', 0, 0x0000);
192 convStream.writeString(html.toXMLString());
193 };
194
195 calWeekPrinter.prototype.getDayTd =
196 /**
197 * Given a calIDateTime and an array of items, this function creates an HTML
198 * table containing the items, using the appropriate formatting and colours.
199 */
200 function weekPrint_getDayTable(aDate, aItems) {
201 // mainTd is the <td> element from the parent HTML table that will hold
202 // the child HTML tables containing the date string and this day's items.
203 var mainTd = <td border='1px solid black;' width="50%" valign='top'/>
204 var dateFormatter = Components.classes["@mozilla.org/calendar/datetime-formatter;1"]
205 .getService(Components.interfaces.calIDateTimeFormatter);
206 var defaultTimezone = calendarDefaultTimezone();
207 var dateString = dateFormatter.formatDateLong(aDate.getInTimezone(defaultTimezone));
208
209 // Add the formatted date string (in its own child HTML table)
210 mainTd.appendChild(
211 <table class='day-name' width='100%' style='border: 1px solid black;'>
212 <tr>
213 <td align='center' valign='bottom'>{dateString}</td>
214 </tr>
215 </table>);
216
217 // Add the formatted items (in their child HTML table)
218 var innerTable = <table valign='top' style='font-size: 10px;'/>;
219 for each (var item in aItems) {
220 var sDate = item.startDate || item.entryDate || item.dueDate;
221 var eDate = item.endDate || item.dueDate || item.entryDate;
222 if (sDate) {
223 sDate = sDate.getInTimezone(defaultTimezone);
224 }
225 if (eDate) {
226 eDate = eDate.getInTimezone(defaultTimezone);
227 }
228
229 // End dates are exclusive. Adjust the eDate accordingly.
230 if (sDate && sDate.isDate && eDate) {
231 eDate = eDate.clone();
232 eDate.day -= 1;
233 }
234
235 // If the item has no end date, or if the item's end date is aDate or
236 // is before aDate, skip to the next item.
237 if (!eDate || (eDate.compare(aDate) < 0)) {
238 continue;
239 }
240
241 // No start date or a start date that's after the date we want is bad.
242 if (!sDate || (sDate.compare(aDate) > 0)) {
243 break;
244 }
245
246 var time = "";
247 if (sDate && eDate && !sDate.isDate) {
248 time = dateFormatter.formatTime(sDate) + '-' + dateFormatter.formatTime(eDate);
249 } else if (sDate && !sDate.isDate) {
250 time = dateFormatter.formatTime(sDate);
251 } else if (eDate && !eDate.isDate) {
252 time = dateFormatter.formatTime(eDate);
253 }
254
255 // Get calendar and category colours and apply them to the item's
256 // table cell.
257 var calColor = item.calendar.getProperty('color');
258 if (!calColor) {
259 calColor = "#A8C2E1";
260 }
261 var pb2 = Components.classes["@mozilla.org/preferences-service;1"]
262 .getService(Components.interfaces.nsIPrefBranch2);
263 var catColor;
264 try {
265 catColor = pb2.getCharPref("calendar.category.color."+item.getProperty("CATEGORIES").toLowerCase());
266 } catch(ex) {}
267
268 var style = 'font-size: 11px; background-color: ' + calColor + ';';
269 style += ' color: ' + getContrastingTextColor(calColor);
270 if (catColor) {
271 style += ' border: solid ' + catColor + ' 2px;';
272 }
273 var item = <tr>
274 <td valign='top' align='left' style={style}>{time} {item.title}</td>
275 </tr>;
276 innerTable.appendChild(item);
277 }
278 innerTable.appendChild(<p> </p>);
279 mainTd.appendChild(innerTable);
280 return mainTd;
281 };