]> code.delx.au - comingnext/blob - comingNext/index.html
reload calendar data every 6 hours only, updating display without actually reloading...
[comingnext] / comingNext / index.html
1 <?xml version="1.0" encoding="UTF-8"?>
2 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
3 <html xmlns="http://www.w3.org/1999/xhtml">
4 <head>
5
6 <title>Coming Next</title>
7
8 <style type="text/css">
9 /* The following classes can be modified by widget settings */
10 .background { }
11 .backgroundFullscreen { }
12 .weekDay { }
13 .date { }
14 .today { }
15 .tomorrow { }
16 .time { }
17 .now { }
18 .description { }
19 .icon { }
20 .overdue {}
21 .calendar1 {}
22 .calendar2 {}
23 .calendar3 {}
24 .calendar4 {}
25 .calendar5 {}
26 .calendar6 {}
27 </style>
28
29 <script type="text/javascript" src="localizedTextStrings.js" charset="utf-8" />
30
31 <script>
32 // valid types for the config object are 'Int', 'Bool', 'String', 'Enum', 'UID', 'Array'
33 var config = {
34 monthRange: { Type: 'Int', Default: 2, Value: 2,},
35 includeTodos: { Type: 'Bool', Default: true, Value: true,},
36 useBackgroundImage: { Type: 'Bool', Default: true, Value: true,},
37 backgroundImageLocation: { Type: 'Enum', Default: 'internal', Value: 'internal', ValidValues: ['internal', 'external']},
38 showCombinedDateTime: { Type: 'Bool', Default: false, Value: false,},
39 showLocation: { Type: 'Bool', Default: true, Value: true,},
40 showTodayAsText: { Type: 'Bool', Default: true, Value: true,},
41 todayText: { Type: 'String', Default: getLocalizedText('settings.default.todayText'), Value: getLocalizedText('settings.default.todayText'),},
42 tomorrowText: { Type: 'String', Default: getLocalizedText('settings.default.tomorrowText'), Value: getLocalizedText('settings.default.tomorrowText'),},
43 showNowAsText: { Type: 'Bool', Default: true, Value: true,},
44 nowText: { Type: 'String', Default: getLocalizedText('settings.default.nowText'), Value: getLocalizedText('settings.default.nowText'),},
45 markOverdueTodos: { Type: 'Bool', Default: true, Value: true,},
46 overdueText: {Type: 'String', Default: getLocalizedText('settings.default.overdueText'), Value: getLocalizedText('settings.default.overdueText'),},
47 dateSeparator: { Type: 'String', Default: getLocalizedText('settings.default.dateSeparator'), Value: getLocalizedText('settings.default.dateSeparator'),},
48 dateFormat: { Type: 'Enum', Default: 'auto', Value: 'auto', ValidValues: ['auto', 'DDMM', 'MMDD'],},
49 weekDayLength: { Type: 'Int', Default: 2, Value: 2,},
50 updateDataInterval: { Type: 'Int', Default: 5, Value: 5,},
51 calendarApp: { Type: 'UID', Default: 0x10005901, Value: 0x10005901,},
52 eventsPerWidget: { Type: 'Int', Default: 4, Value: 4,},
53 showNothingText: { Type: 'Bool', Default: true, Value: true,},
54 nothingText: { Type: 'String', Default: getLocalizedText('settings.default.nothingText'), Value: getLocalizedText('settings.default.nothingText'),},
55 enableDaylightSaving: { Type: 'Bool', Default: true, Value: true,},
56 daylightSavingOffset: { Type: 'Int', Default: 1, Value: 1,},
57 hideWidgetOnCalendarOpen: { Type: 'Bool', Default: false, Value: false,},
58 showCalendarIndicator: { Type: 'Bool', Default: true, Value: true,},
59 excludedCalendars: { Type: 'Array', Default: [], Value: [],},
60 enableLogging: { Type: 'Bool', Default: false, Value: false,},
61 cssStyle_background: { Type: 'String', Default: 'color:#ffffff; background-color:#000000', Value: 'color:#ffffff; background-color:#000000',},
62 cssStyle_backgroundFullscreen: { Type: 'String', Default: 'color:#ffffff; background-color:#000000', Value: 'color:#ffffff; background-color:#000000',},
63 cssStyle_weekDay: { Type: 'String', Default: '', Value: '',},
64 cssStyle_date: { Type: 'String', Default: '', Value: '',},
65 cssStyle_today: { Type: 'String', Default: 'color:#ff0000', Value: 'color:#ff0000',},
66 cssStyle_tomorrow: { Type: 'String', Default: 'color:#0000ff', Value: 'color:#0000ff',},
67 cssStyle_time: { Type: 'String', Default: '', Value: '',},
68 cssStyle_now: { Type: 'String', Default: 'color:#ff00ff', Value: 'color:#ff00ff',},
69 cssStyle_description: { Type: 'String', Default: '', Value: '',},
70 cssStyle_icon: { Type: 'String', Default: 'width:15px; height:15px', Value: 'width:15px; height:15px',},
71 cssStyle_overdue: { Type: 'String', Default: 'color:#ffff00', Value: 'color:#ffff00',},
72 cssStyle_calendar1: { Type: 'String', Default: 'background-color:#0757cf', Value: 'background-color:#0757cf',},
73 cssStyle_calendar2: { Type: 'String', Default: 'background-color:#579f37', Value: 'background-color:#579f37',},
74 cssStyle_calendar3: { Type: 'String', Default: 'background-color:#ff9f07', Value: 'background-color:#ff9f07',},
75 cssStyle_calendar4: { Type: 'String', Default: 'background-color:#af8fef', Value: 'background-color:#af8fef',},
76 cssStyle_calendar5: { Type: 'String', Default: 'background-color:#57afbf', Value: 'background-color:#57afbf',},
77 cssStyle_calendar6: { Type: 'String', Default: 'background-color:#9fdf57', Value: 'background-color:#9fdf57',},
78 }
79
80
81
82 //-------------------------------------------------------
83 // Nothing of interest from here on...
84 //-------------------------------------------------------
85 var panelNum = 0; // use 1 for second panel
86 var version = "1.31";
87 var versionURL = "http://comingnext.sourceforge.net/version.xml";
88 var calendarService = null;
89 var cacheEntriesHtml = [];
90 var months_translated = [];
91 var orientation = '';
92 var now = new Date();
93 var mode = 0; // 0 = homescreen, 1 = fullscreen, 2 = settings, 3 = about, 4 = check for update
94 var reqV = null;
95 var settingsCalEntryId = null;
96 var settingsCache = null;
97 var notificationRequests = new Array();
98 var calendarList = [];
99 var calendarColors = [];
100 var updateTimer = null;
101 var screenRotationTimer = null;
102 var lastUpdateTime = now; // last time we updated the display
103 var lastReloadTime = null; // last time we fetched calendar data
104 var reloadInterval = 6 * 60 * 60 * 1000; // = 6 hours; time interval for reloading calendar data
105 var errorOccured = false;
106 var entryLists = null; // stores all fetched calendar entries until data is refreshed
107
108 // vars for daylight saving time
109 var summertime = false; // true, if current date is in summer, false if in winter
110 var daylightSavingDates = new Object(); // caches calculated DST winter and summer time shift dates
111
112 // this is a list of data fields a calendar event can have
113 var entryFields = [
114 "id",
115 "Type",
116 "CalendarName",
117 "Summary",
118 "Location",
119 "Status",
120 "StartTime",
121 "EndTime",
122 "InstanceStartTime",
123 "InstanceEndTime"
124 ];
125
126 window.onload = init;
127 window.onresize = updateScreen;
128 window.onshow = updateScreen;
129
130 function isLeapYear( year ) {
131 if (( year % 4 == 0 && year % 100 != 0 ) || year % 400 == 0 )
132 return true;
133 else
134 return false;
135 }
136
137 function calcLeapYear(year, days)
138 {
139 if (isLeapYear(year))
140 return ++days;
141 else
142 return days;
143 }
144
145 function subToSunday(myDate, year, days, prevMonthDays)
146 {
147 for (i = myDate.getDay(); i > 0 ;i--)
148 days--;
149 days -= prevMonthDays;
150 days = isLeapYear(year) ? --days : days;
151 return days;
152 }
153
154 function isSummertime(curDate)
155 {
156 var summer = false;
157 var winter = false;
158
159 // if we already calculated DST summer and winter time dates for this year, use cached values
160 var dst = daylightSavingDates[curDate.getFullYear()];
161 if (!dst) {
162 var thisYearS = new Date(curDate.getFullYear(), 3, 0, 0, 0, 0 );
163 var thisYearW = new Date(curDate.getFullYear(), 10, 0, 0, 0, 0 );
164 var nextYearS = new Date(curDate.getFullYear() + 1, 3, 0, 0, 0, 0 );
165 var nextYearW = new Date(curDate.getFullYear() + 1, 10, 0, 0, 0, 0 );
166
167 thisYearSDays = nextYearSDays = 90;
168 thisYearWDays = nextYearWDays = 304;
169
170 thisYearSDays = calcLeapYear(curDate.getFullYear(), thisYearSDays);
171 thisYearWDays = calcLeapYear(curDate.getFullYear(), thisYearWDays);
172 nextYearSDays = calcLeapYear(curDate.getFullYear() + 1, nextYearSDays);
173 nextYearWDays = calcLeapYear(curDate.getFullYear() + 1, nextYearWDays);
174
175 thisYearSDays = subToSunday(thisYearS, curDate.getFullYear(), thisYearSDays, 59);
176 thisYearWDays = subToSunday(thisYearW, curDate.getFullYear(), thisYearWDays, 273);
177 nextYearSDays = subToSunday(nextYearS, curDate.getFullYear() + 1, nextYearSDays, 59);
178 nextYearWDays = subToSunday(nextYearW, curDate.getFullYear() + 1, nextYearWDays, 273);
179
180 dst = {
181 Summer: new Date (curDate.getFullYear(), 03-1, thisYearSDays, 2, 0, 0),
182 Winter: new Date (curDate.getFullYear(), 10-1, thisYearWDays, 2, 0, 0),
183 }
184 daylightSavingDates[curDate.getFullYear()] = dst;
185 }
186
187 if (dst.Summer < curDate)
188 summer = true;
189 if (dst.Winter < curDate)
190 winter = true;
191 if (summer && !winter)
192 return true;
193 else
194 return false;
195 }
196
197 function error(message)
198 {
199 console.info('Error: ' + message);
200 document.getElementById("calendarList").innerHTML = 'Error: ' + message;
201 document.getElementById("fullscreenCalendarList").innerHTML = 'Error: ' + message;
202 errorOccured = true;
203 document.onclick = null;
204 }
205
206 function areDatesEqual(date1, date2)
207 {
208 return (date1.getFullYear() == date2.getFullYear() &&
209 date1.getMonth() == date2.getMonth() &&
210 date1.getDate() == date2.getDate());
211 }
212
213 function isTomorrow(date)
214 {
215 // tommorow = now + 1 day
216 // ToDo: some days can be shorter as 24 hours(daylight saving change day)
217 return areDatesEqual(date, new Date (now.getTime() + 24*60*60*1000));
218 }
219
220 function isToday(date)
221 {
222 return areDatesEqual(date, now);
223 }
224
225 function collectLocales()
226 {
227 var tmpyear = 2000 + panelNum;
228 var month = 0;
229
230 if (months_translated.length > 0)
231 return;
232 for (month = 0; month < 12; month++) {
233 var startDate = new Date(tmpyear, month, 15);
234
235 var item = new Object();
236 item.Type = "DayEvent";
237 item.StartTime = startDate;
238 item.Summary = "__temp" + month;
239
240 var criteria = new Object();
241 criteria.Type = "CalendarEntry";
242 criteria.Item = item;
243
244 try {
245 var result = calendarService.IDataSource.Add(criteria);
246 if (result.ErrorCode)
247 error(result.ErrorMessage);
248 } catch (e) {
249 error("collectLocales: " + e + ', line ' + e.line);
250 }
251 }
252 try {
253 var startTime = new Date(tmpyear,0,1);
254 var endTime = new Date(tmpyear,11,31);
255 var listFiltering = {
256 Type:'CalendarEntry',
257 Filter:{
258 StartRange: startTime,
259 EndRange: endTime,
260 SearchText: '__temp',
261 Type: 'DayEvent'
262 }
263 }
264 var result = calendarService.IDataSource.GetList(listFiltering);
265 if (result.ErrorCode) {
266 error(result.ErrorMessage);
267 return;
268 }
269 var list = result.ReturnValue;
270 } catch(e) {
271 error(e + ', line ' + e.line);
272 return;
273 }
274 var ids = new Array();
275 try {
276 var entry;
277 var counter = 0;
278 var dateArr = [];
279
280 while (list && (entry = list.getNext()) != undefined) {
281 dateArr = entry.StartTime.replace(/,/g,'').replace(/\./g,':').replace(/ /g,' ').split(' ');
282 var day = dateArr[1];
283 var month = dateArr[2];
284 var year = dateArr[3];
285
286 // make sure month is set properly
287 if (isNaN(parseInt(day))) {
288 var tmp = day;
289 day = month;
290 month = tmp;
291 } else if (isNaN(parseInt(year))) {
292 var tmp = year;
293 year = month;
294 month = tmp;
295 }
296
297 log(entry.StartTime + ' -> ' + month + ' ' + counter);
298 ids[counter] = entry.id;
299 months_translated[month] = counter + 1;
300 counter++;
301 }
302 } catch(e) {
303 error(e + ', line ' + e.line);
304 return;
305 }
306 log(ids);
307 try {
308 var criteria = new Object();
309 criteria.Type = "CalendarEntry";
310 criteria.Data = {
311 IdList: ids
312 }
313
314 var result = calendarService.IDataSource.Delete(criteria);
315 if (result.ErrorCode)
316 error(result.ErrorMessage);
317 } catch(e) {
318 error('deleting temp calendar entries:' + e + ', line ' + e.line);
319 return;
320 }
321 }
322
323 function requestNotification()
324 {
325 var criteria = new Object();
326 criteria.Type = "CalendarEntry";
327 criteria.Filter = new Object();
328 for(var i=0; i < calendarList.length; i++) {
329 criteria.Filter.CalendarName = calendarList[i];
330 try {
331 var notificationRequest = calendarService.IDataSource.RequestNotification(criteria, callback);
332 if (notificationRequest.ErrorCode)
333 error('requestNotification failed with error code ' + notificationRequest.ErrorCode);
334 notificationRequests.push(notificationRequest);
335 } catch (e) {
336 error("requestNotification: " + e + ', line ' + e.line);
337 }
338 }
339
340 var criteria2 = new Object();
341 criteria2.Type = "CalendarEntry";
342 criteria2.Filter = new Object();
343 criteria2.Filter.LocalIdList = new Array();
344 criteria2.Filter.LocalIdList[0] = settingsCalEntryId;
345 try {
346 var notificationRequest = calendarService.IDataSource.RequestNotification(criteria2, settingsCallback);
347 if (notificationRequest.ErrorCode)
348 error('requestNotification failed with error code ' + notificationRequest.ErrorCode);
349 notificationRequests.push(notificationRequest);
350 } catch (e) {
351 error("requestNotification: " + e + ', line ' + e.line);
352 }
353 }
354
355 function cancelNotification()
356 {
357 for(var i=0; i < notificationRequests.length; i++) {
358 try {
359 var result = calendarService.IDataSource.Cancel(notificationRequests[i]);
360 if (result.ErrorCode)
361 error('cancelNotification failed with error code ' + result.ErrorCode);
362 } catch (e) {
363 error("cancelNotification: " + e + ', line ' + e.line);
364 }
365 }
366 }
367
368 function callback(transId, eventCode, result)
369 {
370 log("callback(): panelNum: %d transId: %d eventCode: %d result.ErrorCode: %d", panelNum, transId, eventCode, result.ErrorCode);
371 lastReloadTime = null; // force calendar data reload on next update
372 updateData();
373 }
374
375 function settingsCallback(transId, eventCode, result)
376 {
377 log("settingsCallback(): panelNum: %d transId: %d eventCode: %d result.ErrorCode: %d", panelNum, transId, eventCode, result.ErrorCode);
378 loadSettings();
379 }
380
381 function parseDate(dateString)
382 {
383 /*
384 Dates my look very differently. Also keep in mind that the names are localized!!! These are the possibilities depending on the users date format setting:
385 Wednesday, 26 August, 2009 24:00:00
386 Wednesday, 26 August, 2009 12:00:00 am
387 Wednesday, August 26, 2009 12:00:00 am
388 Wednesday, 2009 August, 26 12:00:00 am
389 Wednesday, 2009 August, 28 8.00.00 pm
390 Wednesday, 2009 August, 28 08:00:00 PM
391 */
392
393 if (dateString == "" || dateString == null)
394 return null;
395 var dateArr = dateString.replace(/,/g,'').replace(/\./g,':').replace(/ /g,' ').split(' ');
396 if (dateArr.length != 5 && dateArr.length != 6)
397 return null;
398
399 // parse date
400 var weekDay = dateArr[0];
401 var day = dateArr[1];
402 var month = dateArr[2];
403 var year = dateArr[3];
404 // make sure month is set properly
405 if (isNaN(parseInt(day))) {
406 var tmp = day;
407 day = month;
408 month = tmp;
409 } else if (isNaN(parseInt(year))) {
410 var tmp = year;
411 year = month;
412 month = tmp;
413 }
414 // make sure day and year are set properly
415 if (Number(day) > Number(year)) {
416 var tmp = year;
417 year = day;
418 day = tmp;
419 }
420 month = months_translated[month];
421
422 // parse time
423 var timeArr = dateArr[4].split(':');
424 if (timeArr.length != 3)
425 return null;
426 var hours = Number(timeArr[0]);
427 var minutes = Number(timeArr[1]);
428 var seconds = Number(timeArr[2]);
429 if (dateArr.length == 6 && dateArr[5].toLowerCase() == 'pm' && hours < 12)
430 hours += 12;
431 if (dateArr.length == 6 && dateArr[5].toLowerCase() == 'am' && hours == 12)
432 hours = 0;
433
434 var result = new Date(year, month - 1, day, hours, minutes, seconds);
435
436 // take care of daylight saving time
437 if (config['enableDaylightSaving'].Value) {
438
439 // determine if date is in summer or winter time
440 var dateSummerTime = isSummertime(result);
441
442 // work around bug in Nokias calendar api resulting in dates within a different DST to be off by 1 hour
443 if (summertime && !dateSummerTime) {
444 result = new Date(result.getTime() - 1000 * 60 * 60 * config['daylightSavingOffset'].Value); // -1 hour
445 log('parseDate(): fixing time -1h: ' + result);
446 }
447 else if (!summertime && dateSummerTime) {
448 result = new Date(result.getTime() + 1000 * 60 * 60 * config['daylightSavingOffset'].Value); // +1 hour
449 log('parseDate(): fixing time +1h: ' + result);
450 }
451 }
452
453 return result;
454 }
455
456 // returns a short date as string ("31.12" or "12.31") based on the format string which should look like "Wednesday, 26 August, 2009 12:00:00 am"
457 function formatDate(date, format)
458 {
459 var day = date.getDate().toString();
460 var month = (date.getMonth() + 1).toString();
461 while (day.length < 2) { day = '0' + day; }
462 while (month.length < 2) { month = '0' + month; }
463
464 if (config['showTodayAsText'].Value && isToday(date))
465 return '<span class="today">' + config['todayText'].Value + '</span>';
466 if (config['showTodayAsText'].Value && isTomorrow(date))
467 return '<span class="tomorrow">' + config['tomorrowText'].Value + '</span>';
468
469 var dateArr = format.replace(/,/g,'').replace(/\./g,':').replace(/ /g,' ').split(' ');
470 if (dateArr.length != 5 && dateArr.length != 6) {
471 // we don't know how to format this
472 if (config['dateFormat'].Value == 'auto' || config['dateFormat'].Value == 'DDMM')
473 return day + config['dateSeparator'].Value + month;
474 else
475 return month + config['dateSeparator'].Value + day;
476 }
477
478 var dayFirst = true;
479 if (config['dateFormat'].Value == 'MMDD')
480 dayFirst = false;
481 else if (config['dateFormat'].Value == 'DDMM')
482 dayFirst = true;
483 else {
484 // config['dateFormat'].Value == 'auto', try to detect system setting
485 // parse date
486 var day_ = dateArr[1];
487 var month_ = dateArr[2];
488 var year_ = dateArr[3];
489 // make sure month is set properly
490 if (isNaN(parseInt(day_))) {
491 var tmp = day_;
492 day_ = month_;
493 month_ = tmp;
494 dayFirst = false;
495 } else if (isNaN(parseInt(year_))) {
496 var tmp = year_;
497 year_ = month_;
498 month_ = tmp;
499 dayFirst = true;
500 }
501 // make sure day and year are set properly
502 if (Number(day_) > Number(year_))
503 dayFirst = false;
504 }
505
506 if (dayFirst)
507 return day + config['dateSeparator'].Value + month;
508 else
509 return month + config['dateSeparator'].Value + day;
510 }
511
512 function formatTime(date)
513 {
514 // date is a Date() object
515 date.setSeconds(0); // we don't care about seconds
516 var time = date.toLocaleTimeString().replace(/[\.:]00/, ''); // remove seconds from string
517 if (time.replace(/\./, ':').split(':')[0].length < 2)
518 time = '0' + time;
519 if (config['showNowAsText'].Value && date.getTime() == now.getTime())
520 time = '<span class="now">' + config['nowText'].Value + '</span>';
521 return time;
522 }
523
524 function updateData()
525 {
526 log('updateData()');
527 if (errorOccured) {
528 return;
529 }
530
531 // check if we got additional or less calendars since our last update
532 var newCalendarList = listCalendars();
533 if (newCalendarList.length != calendarList.length) {
534 calendarList = newCalendarList;
535 updateCalendarColors();
536 cancelNotification();
537 requestNotification();
538 lastReloadTime = null; // force calendar data reload on this update
539 }
540
541 now = new Date();
542
543 // only reload calendar data every 6 hours, visual updates occure more often
544 if (!lastReloadTime || now.getTime() - lastReloadTime.getTime() > reloadInterval) {
545 log('updateData(): reloading calendar data');
546 try {
547 // meetings have time
548 // note: anniveraries have a start time of 12:00am. So since we want to include them, we have to query the whole day and check if events have passed later
549 summertime = isSummertime(now); // cache summer time info for today
550 var meetingList = [];
551 for(var i=0; i < calendarList.length; i++) {
552 // ignore excluded calendars
553 if (config['excludedCalendars'].Value.indexOf(calendarList[i]) != -1)
554 continue;
555 var meetingListFiltering = {
556 Type:'CalendarEntry',
557 Filter:{
558 CalendarName: calendarList[i],
559 StartRange: (new Date(now.getFullYear(), now.getMonth(), now.getDate(), 0, 0, 0)),
560 EndRange: (new Date(now.getFullYear(), now.getMonth() + config['monthRange'].Value, now.getDate(), 0, 0, 0))
561 }
562 }
563 var meetingResult = calendarService.IDataSource.GetList(meetingListFiltering);
564 if (meetingResult.ErrorCode != 0)
565 throw("Error fetching calendar data: " + meetingResult.ErrorCode + ': ' + meetingResult.ErrorMessage);
566 var list = meetingResult.ReturnValue;
567 meetingList = meetingList.concat(listToArray(list, calendarList[i]));
568 }
569 log("updateData(): meetingList.sort()");
570 meetingList.sort(sortCalendarEntries);
571
572 // todos don't, they start on 00:00 hrs., but should be visible anyway
573 // this will generate a list of passed todos. We have to check if they have been marked as "done" yet
574 if (config['includeTodos'].Value) {
575 var todayTodoList = [];
576 for(var i=0; i < calendarList.length; i++) {
577 // ignore excluded calendars
578 if (config['excludedCalendars'].Value.indexOf(calendarList[i]) != -1)
579 continue;
580 var todayTodoListFiltering = {
581 Type:'CalendarEntry',
582 Filter:{
583 CalendarName: calendarList[i],
584 Type: 'ToDo',
585 StartRange: (new Date(now.getFullYear() - 1, now.getMonth(), now.getDate(), 0, 0, 0)),
586 EndRange: (new Date(now.getFullYear(), now.getMonth(), now.getDate(), 0, 0, 1))
587 }
588 }
589 var todayTodoResult = calendarService.IDataSource.GetList(todayTodoListFiltering);
590 var list = todayTodoResult.ReturnValue;
591 todayTodoList = todayTodoList.concat(listToArray(list, calendarList[i]));
592 }
593 log("updateData(): todayTodoList.sort()");
594 todayTodoList.sort(sortCalendarEntries);
595 entryLists = [todayTodoList, meetingList];
596 } else {
597 entryLists = [meetingList];
598 }
599 lastReloadTime = new Date();
600 } catch(e) {
601 error('loading Calendar items list:' + e + ', line ' + e.line);
602 return;
603 }
604 }
605
606 try {
607 var entry;
608 var counter = 0;
609 var entryDate = '';
610 var dateArr = [];
611 var fontsize = 'normal';
612 if (mode == 0) {
613 if (config['eventsPerWidget'].Value == 3) {
614 fontsize = '17pt';
615 changeCssClass('.icon', 'width:20px; height:20px');
616 }
617 else if (config['eventsPerWidget'].Value == 5) {
618 fontsize = '10pt';
619 changeCssClass('.icon', 'width:10px; height:10px');
620 }
621 else if (config['eventsPerWidget'].Value == 6) {
622 fontsize = '8pt';
623 changeCssClass('.icon', 'width:8px; height:8px');
624 }
625 }
626 else
627 changeCssClass('.icon', config['cssStyle_icon'].Value);
628 var entriesHtml = '<table style="font-size:' + fontsize + ';">';
629 var eventIds = [];
630 var max;
631 if (mode == 0)
632 max = (panelNum + 1) * config['eventsPerWidget'].Value;
633 else
634 max = 30; // we can display a lot more events in fullscreen mode
635
636 if (config['enableLogging'].Value) {
637 var listinfo = "";
638 for (var i=0; i < entryLists.length; i++) {
639 listinfo = listinfo + " " + entryLists[i].length;
640 var entrieslist = "";
641 for (var j=0; j < entryLists[i].length; j++) {
642 entrieslist += entryLists[i][j].Summary + ", ";
643 }
644 log("updateData(): entrieslist: " + entrieslist);
645 }
646 log("updateData(): inner loop, " + entryLists.length + " lists, [" + listinfo + "] entries");
647 }
648
649 // the first outer loop iteration is for passed ToDos, the second loop is for all upcomming events (may also include ToDos)
650 for (var i=0; counter < max && i < entryLists.length; i++) {
651 for (var j=0; (counter < max) && (j < entryLists[i].length); j++) {
652 entry = entryLists[i][j];
653 counter++;
654
655 // output event info for debugging
656 var entryInfo = "event: ";
657 for(var k=0; k < entryFields.length; ++k) {
658 if (entry[entryFields[k]] != undefined) {
659 entryInfo += entryFields[k] + "=" + entry[entryFields[k]] + ",";
660 }
661 }
662 log(entryInfo);
663
664 // we don't want ToDos when includeTodos == false or when they are completed
665 if (entry.Type == 'ToDo' && (entry.Status == "TodoCompleted" || !config['includeTodos'].Value)) {
666 log('skipping ' + entry.id );
667 counter--;
668 continue;
669 }
670
671 // make sure that we don't include an event twice (useful for ToDos that might come up twice)
672 if (eventIds[entry.id] == 1 && entry.Type == 'ToDo') {
673 log('skipped (already included) ' + entry.id);
674 counter--;
675 continue;
676 } else
677 eventIds[entry.id] = 1;
678
679 // summary can be undefined!
680 var Summary = ((entry.Summary == null) ? '' : entry.Summary);
681 if (entry.Type == 'Meeting' && entry.Location != '' && config['showLocation'].Value)
682 Summary += ', ' + entry.Location;
683
684 // fix by yves: determine start and end dates/times
685 entryStartTime = ((entry.InstanceStartTime == null) ? entry.StartTime : entry.InstanceStartTime);
686 entryEndTime = ((entry.InstanceEndTime == null) ? entry.EndTime : entry.InstanceEndTime);
687
688 // there can be ToDos that have no date at all!
689 if (entry.Type == 'ToDo' && entry.EndTime == null)
690 entryDate = ""; // this will cause parseDate(entryDate) to return null;
691 else
692 entryDate = ((entry.Type == 'ToDo') ? entryEndTime : entryStartTime); // ToDo's use their EndTime, the rest use StartTime
693
694 // Convert date/time string to Date object
695 var date = parseDate(entryDate);
696 log('date: ' + date);
697 var endDate = ((entryEndTime == null) ? null : parseDate(entryEndTime));
698 log('endDate: ' + endDate);
699
700 // check if meeting event has already passed
701 if (entry.Type == 'Meeting') {
702 var compareTime = ((endDate == null) ? date.getTime() : endDate.getTime());
703 if (now.getTime() > compareTime) {
704 log('skipping Meeting (already passed) ' + entry.id);
705 counter--;
706 eventIds[entry.id] = 0;
707 continue;
708 }
709 }
710
711 // check if anniversary passed (not sure why they are in the list, the query was only for today - nokia?)
712 if (entry.Type == 'Anniversary') {
713 var tmp = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 0,0,0);
714 if (date.getTime() < tmp.getTime()) {
715 log('skipping Anniversary (already passed) ' + entry.id);
716 counter--;
717 eventIds[entry.id] = 0;
718 continue;
719 }
720 }
721
722 // fix DayEvents end time. End times are off by 1 Second. It's possible that the event has already passed
723 if (entry.Type == 'DayEvent' && endDate != null) {
724 endDate.setMinutes(endDate.getMinutes() - 1);
725 log('fixing DayEvent endDate: ' + endDate);
726 if (now.getTime() > endDate.getTime()) {
727 log('event already passed ' + entry.id);
728 counter--;
729 eventIds[entry.id] = 0;
730 continue;
731 }
732 }
733
734 // check if the event is currently taking place
735 if (entryStartTime != null && entryEndTime != null && date != null && endDate != null) {
736 // check if we are between start and endtime
737 if ((date.getTime() < now.getTime()) && (now.getTime() < endDate.getTime())) {
738 date = now; // change appointment date/time to now
739 log('event is currently taking place: ' + date);
740 }
741 }
742
743 // skip events for the first panel in case this is the second one and we're not in fullscreen mode
744 if (mode == 0 && panelNum > 0 && counter < panelNum * config['eventsPerWidget'].Value + 1) {
745 log('skipping (already in first widget) ' + entry.id);
746 continue;
747 }
748
749 // mark overdue todos
750 var overdue = false;
751 if (entry.Type == 'ToDo' && date != null) {
752 var tmp1 = new Date(date.getFullYear(), date.getMonth(), date.getDate(), 0,0,0);
753 var tmp2 = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 0,0,0);
754 if (tmp1.getTime() < tmp2.getTime()) {
755 overdue = true;
756 }
757 }
758
759 // generate html output
760 entriesHtml += '<tr>';
761 if (config['showCalendarIndicator'].Value && calendarList.length - config['excludedCalendars'].Value.length > 1) {
762 entriesHtml += '<td><span class="calendar' + calendarColors[entry.CalendarName] + '">&nbsp;</span></td>';
763 }
764 entriesHtml += '<td><img class="icon" src="' + entry.Type + '.png" /></td>';
765 if(date == null) {
766 // some languages have very strange locale date formats, can't parse all those. Also some todos don't have dates at all.
767 entriesHtml += '<td colspan="4"><span class="date">' + entryDate + '</span> ';
768 } else {
769 var weekDay = date.toLocaleDateString().substr(0,config['weekDayLength'].Value);
770 var time = formatTime(date);
771 var dateStr = formatDate(date, entryDate);
772 if (entry.Type == 'ToDo' && overdue && config['markOverdueTodos'].Value) {
773 dateStr = '<span class="overdue">' + config['overdueText'].Value + '</span>';
774 entriesHtml += '<td colspan="4" width="1px"><span class="date">' + dateStr + '</span> ';
775 } else if (entry.Type == 'ToDo' || entry.Type == 'Anniversary' || entry.Type == 'DayEvent' || entry.Type == 'Reminder') {
776 if ((isToday(date) || isTomorrow(date)) && config['showTodayAsText'].Value) // show weekday if the date string is not text. looks odd otherwise
777 entriesHtml += '<td colspan="4" width="1px"><span class="date">' + dateStr + '</span> ';
778 else
779 entriesHtml += '<td class="weekDay" width="1px">' + weekDay + '</td><td width="1px" class="date">' + dateStr + '</td><td colspan="2">';
780 } else if (entry.Type == 'Meeting') {
781 if (config['showCombinedDateTime'].Value) {
782 if (isToday(date))
783 entriesHtml += '<td width="1px" colspan="4"><span class="today">' + time + '</span> ';
784 else if (isTomorrow(date))
785 entriesHtml += '<td width="1px" colspan="4"><span class="tomorrow">' + dateStr + '</span> <span class="time">' + time + '</span> ';
786 else
787 entriesHtml += '<td width="1px" class="weekDay">' + weekDay + '</td><td width="1px" class="date">' + dateStr + '</td><td colspan="2">';
788 } else {
789 if ((isToday(date) || isTomorrow(date)) && config['showTodayAsText'].Value)
790 entriesHtml += '<td colspan="4" width="1px"><span class="today">' + dateStr + '</span> <span class="time">' + time + '</span> ';
791 else
792 entriesHtml += '<td width="1px" class="weekDay">' + weekDay + '</td><td width="1px" class="date">' + dateStr + '</td><td width="1px" class="time">' + time + '</td><td>';
793 }
794 }
795 }
796 entriesHtml += '<span class="description">' + Summary + '</span></td></tr>';
797 }
798 }
799 entriesHtml += '</table>';
800 if (config['showNothingText'].Value && entriesHtml == '<table></table>') {
801 var text = config['nothingText'].Value.replace(/%d/, config['monthRange'].Value);
802 entriesHtml = '<div style="width:295px; height:75px; text-align:center; line-height:75px; overflow:visible;">' + text + '</div>';
803 }
804 if (cacheEntriesHtml != entriesHtml) {
805 if (mode == 0)
806 document.getElementById('calendarList').innerHTML = entriesHtml;
807 else
808 document.getElementById('fullscreenCalendarList').innerHTML = entriesHtml;
809 cacheEntriesHtml = entriesHtml;
810 }
811
812 lastUpdateTime = new Date();
813 } catch(e) {
814 error('displaying list:' + e + ', line ' + e.line);
815 return;
816 }
817 }
818
819 function updateScreen()
820 {
821 // check if opening fullscreen
822 if( window.innerHeight > 91 && mode == 0) {
823 mode = 1;
824 cacheEntriesHtml = '';
825 document.getElementById('body').style.backgroundImage = "";
826 showFullscreen();
827 }
828 else if (window.innerHeight <= 91 && mode != 0) {
829 mode = 0;
830 cacheEntriesHtml = '';
831 showHomescreen();
832 }
833
834 if (mode == 0)
835 updateHomescreen();
836 else if (mode == 1)
837 updateFullscreen();
838
839 var time = new Date();
840 if (time.getTime() - lastUpdateTime.getTime() > config['updateDataInterval'].Value * 60 * 1000) {
841 log('updateScreen(): force updateData() because last update was too long ago (' + (time.getTime() - lastUpdateTime.getTime()) / 1000 + 's)');
842 clearUpdateTimer();
843 updateData();
844 setUpdateTimer(); // reinitialize update timer
845 }
846 }
847
848 function launchCalendar()
849 {
850 try {
851 widget.openApplication(config['calendarApp'].Value, "");
852 if (config['hideWidgetOnCalendarOpen'].Value)
853 window.close();
854 } catch(e) {
855 error('starting Calendar App');
856 return;
857 }
858 }
859
860 function init()
861 {
862 log('New widget instance starting up...');
863
864 try {
865 // call calendar service
866 if (device != "undefined")
867 calendarService = device.getServiceObject("Service.Calendar", "IDataSource");
868 else
869 throw('device object does not exist');
870 } catch(e) {
871 error('loading Calendar service: ' + e + ', line ' + e.line + '<br /><a onclick="widget.openURL(\'http://comingnext.sf.net/help\'); return false;" href="http://comingnext.sf.net/help">' + getLocalizedText('menu.help') + '</a>');
872 //return;
873 }
874
875 calendarList = listCalendars();
876 loadSettings();
877 updateCalendarColors();
878 collectLocales();
879 //updateData();
880 requestNotification();
881 document.getElementById("settingsTitle").innerHTML = getLocalizedText('menu.settings');
882 setUpdateTimer();
883 if (window.innerHeight > 91) {
884 mode = 0; // we're starting fullscreen, we set mode to homescreen in order to let updateScreen() do all the work for us
885 }
886 else {
887 mode = 1;
888 }
889 log("init(): updateScreen()");
890 updateScreen();
891 if (config['useBackgroundImage'].Value)
892 // check for screen rotation every 1 secs
893 screenRotationTimer = window.setInterval('updateScreen()', 1000 * 1);
894 log("init(): finished...");
895 }
896
897 function setUpdateTimer()
898 {
899 updateTimer = window.setInterval('updateTimerCallback()', 1000 * 60 * config['updateDataInterval'].Value);
900 }
901
902 function clearUpdateTimer()
903 {
904 window.clearInterval(updateTimer);
905 }
906
907 function updateTimerCallback()
908 {
909 log("updateTimerCallback()");
910 updateData();
911 }
912
913 function createMenu()
914 {
915 window.menu.setLeftSoftkeyLabel("",null);
916 window.menu.setRightSoftkeyLabel("",null);
917 var id = 0;
918 var menuSettings = new MenuItem(getLocalizedText('menu.settings'), id++);
919 var menuCallApp = new MenuItem(getLocalizedText('menu.openCalendarApp'), id++);
920 var menuHelp = new MenuItem(getLocalizedText('menu.help'), id++);
921 var menuUpdate = new MenuItem(getLocalizedText('menu.update'), id++);
922 var menuAbout = new MenuItem(getLocalizedText('menu.about'), id++);
923 menuSettings.onSelect = showSettings;
924 menuAbout.onSelect = showAbout;
925 menuCallApp.onSelect = launchCalendar;
926 menuUpdate.onSelect = showUpdate;
927 menuHelp.onSelect = showHelp;
928 window.menu.clear();
929 window.menu.append(menuCallApp);
930 window.menu.append(menuSettings);
931 window.menu.append(menuHelp);
932 window.menu.append(menuUpdate);
933 window.menu.append(menuAbout);
934 }
935
936 function showSettings()
937 {
938 mode = 2;
939 hideViews();
940 document.getElementById("settingsView").style.display = "block";
941 document.onclick = null;
942
943 window.menu.setLeftSoftkeyLabel(getLocalizedText('settings.save'), function()
944 {
945 for (var key in config) {
946 if (config[key].Type == 'String')
947 config[key].Value = document.forms[0].elements["settings." + key].value;
948 else if (config[key].Type == 'Int') {
949 config[key].Value = parseInt(document.forms[0].elements["settings." + key].value);
950 if (config[key].Value < 0)
951 config[key].Value = config[key].Default;
952 }
953 else if (config[key].Type == 'Bool')
954 config[key].Value = document.forms[0].elements["settings." + key].checked;
955 else if (config[key].Type == 'UID')
956 config[key].Value = parseInt(document.forms[0].elements["settings." + key].value);
957 else if (config[key].Type == 'Enum') {
958 config[key].Value = document.forms[0].elements["settings." + key].value;
959 if (config[key].ValidValues.indexOf(config[key].Value) == -1)
960 config[key].Value = config[key].Default;
961 }
962 else if (config[key].Type == 'Array') {
963 if (key == 'excludedCalendars') {
964 config[key].Value = new Array();
965 for(var i=0; i < calendarList.length; i++) {
966 var element = document.forms[0].elements["settings." + key + "." + calendarList[i]];
967 if (element != null && element.checked == false)
968 config[key].Value.push(calendarList[i]);
969 }
970 }
971 }
972 }
973
974 updateCssClasses();
975
976 saveSettings();
977
978 mode = 1;
979 showFullscreen();
980 });
981 window.menu.setRightSoftkeyLabel(getLocalizedText('settings.cancel'), function()
982 {
983 mode = 1;
984 showFullscreen();
985 });
986
987 var settingsHtml = '<form>';
988 for (var key in config) {
989 if (config[key].Type == 'String') {
990 var prefix = "";
991 if (key.substring(0,9) == "cssStyle_")
992 prefix = getLocalizedText('settings.cssStyle_prefix');
993 settingsHtml += '<table><tr><td>' + prefix + getLocalizedText('settings.name.' + key) + '<br /><input class="textInput" name="settings.' + key + '" type="text" value="' + config[key].Value + '" /></td>' + printHintBox(getLocalizedText('settings.info.' + key)) + '<hr />';
994 }
995 else if (config[key].Type == 'Int')
996 settingsHtml += '<table><tr><td>' + getLocalizedText('settings.name.' + key) + '<br /><input class="textInput" name="settings.' + key + '" type="text" value="' + config[key].Value + '" /></td>' + printHintBox(getLocalizedText('settings.info.' + key)) + '<hr />';
997 else if (config[key].Type == 'Bool')
998 settingsHtml += '<table><tr><td>' + getLocalizedText('settings.name.' + key) + '<br /><input name="settings.' + key + '" type="checkbox" value="true" ' + (config[key].Value ? 'checked="checked"' : '') + '/></td>' + printHintBox(getLocalizedText('settings.info.' + key)) + '<hr />';
999 else if (config[key].Type == 'UID')
1000 settingsHtml += '<table><tr><td>' + getLocalizedText('settings.name.' + key) + '<br /><input class="textInput" name="settings.' + key + '" type="text" value="0x' + config[key].Value.toString(16) + '" /></td>' + printHintBox(getLocalizedText('settings.info.' + key)) + '<hr />';
1001 else if (config[key].Type == 'Enum') {
1002 settingsHtml += '<table><tr><td>' + getLocalizedText('settings.name.' + key) + '<br /><select name="settings.' + key + '" size="1">';
1003 for(var i = 0; i < config[key].ValidValues.length; i++)
1004 settingsHtml += '<option value="' + config[key].ValidValues[i] + '"' + (config[key].Value == config[key].ValidValues[i] ? ' selected="selected"' : '') + '>' + getLocalizedText('settings.validValues.' + key + '.' + config[key].ValidValues[i]) + '</option>';
1005 settingsHtml += '</select></div></td>' + printHintBox(getLocalizedText('settings.info.' + key)) + '<hr />';
1006 }
1007 else if (config[key].Type == 'Array') {
1008 settingsHtml += '<table><tr><td>' + getLocalizedText('settings.name.' + key) + '<br />';
1009 if (key == 'excludedCalendars') {
1010 for(var i=0; i < calendarList.length; i++) {
1011 var checked = 'checked="checked"';
1012 if (config[key].Value.indexOf(calendarList[i]) != -1)
1013 checked = '';
1014 settingsHtml += '<input name="settings.' + key + '.' + calendarList[i] + '" type="checkbox" value="' + calendarList[i] + '" ' + checked + '/> ' + calendarList[i] + '<br />';
1015 }
1016 }
1017 settingsHtml += '</td>' + printHintBox(getLocalizedText('settings.info.' + key)) + '<hr />';
1018 }
1019 }
1020 settingsHtml += '<input name="reset" type="button" value="' + getLocalizedText('settings.restoreDefaults') + '" onclick="javascript:restoreDefaultSettings();showSettings();" />';
1021 settingsHtml += '</form>';
1022 document.getElementById("settingsList").innerHTML = settingsHtml;
1023 }
1024
1025 function changeCssClass(classname, properties)
1026 {
1027 for(var i = 0; i < document.styleSheets[0]['cssRules'].length; i++)
1028 {
1029 if (document.styleSheets[0]['cssRules'][i].selectorText == classname) {
1030 document.styleSheets[0].deleteRule(i);
1031 document.styleSheets[0].insertRule(classname + ' { ' + properties + ' }', document.styleSheets[0]['cssRules'].length);
1032 break;
1033 }
1034 }
1035 }
1036
1037 function updateCssClasses()
1038 {
1039 for(var key in config) {
1040 changeCssClass(getLocalizedText('settings.name.' + key), config[key].Value);
1041 }
1042 }
1043
1044 function getSettingsCalEntryId()
1045 {
1046 if (settingsCalEntryId == null) {
1047 // check if entry already exists
1048 var listFiltering = {
1049 Type:'CalendarEntry',
1050 Filter:{
1051 StartRange: new Date(2000, 0, 1),
1052 EndRange: new Date(2000, 0, 1),
1053 SearchText: 'ComingNext Settings|',
1054 Type: 'DayEvent'
1055 }
1056 }
1057 var result = calendarService.IDataSource.GetList(listFiltering);
1058 if (result.ErrorCode) {
1059 error(result.ErrorMessage);
1060 return;
1061 }
1062 var list = result.ReturnValue;
1063 var entry = list.getNext();
1064 if (entry != undefined) {
1065 settingsCalEntryId = entry.LocalId;
1066 log("settingsCalEntryId=" + settingsCalEntryId);
1067 }
1068 else { // create settings item
1069 var item = new Object();
1070 item.Type = "DayEvent";
1071 item.StartTime = new Date(2000, 0, 1);
1072 item.Summary = "ComingNext Settings|";
1073
1074 var criteria = new Object();
1075 criteria.Type = "CalendarEntry";
1076 criteria.Item = item;
1077
1078 try {
1079 var result = calendarService.IDataSource.Add(criteria);
1080 if (result.ErrorCode)
1081 error(result.ErrorMessage);
1082 } catch (e) {
1083 error("getSettingsCalEntryId: " + e + ', line ' + e.line);
1084 }
1085
1086 getSettingsCalEntryId();
1087 }
1088 }
1089 }
1090
1091 function restoreDefaultSettings()
1092 {
1093 for (var key in config)
1094 config[key].Value = config[key].Default;
1095 }
1096
1097 function loadSettings()
1098 {
1099 getSettingsCalEntryId();
1100 var listFiltering = {
1101 Type:'CalendarEntry',
1102 Filter:{
1103 LocalId: settingsCalEntryId
1104 }
1105 }
1106 var result = calendarService.IDataSource.GetList(listFiltering);
1107 if (result.ErrorCode) {
1108 error(result.ErrorMessage);
1109 return;
1110 }
1111 var entry = result.ReturnValue.getNext();
1112 if (entry != undefined) {
1113 log("Loading Settings...");
1114 // only reload settings if they chanced since the last reload
1115 if (settingsCache != entry.Summary)
1116 {
1117 restoreDefaultSettings();
1118 var stringlist = entry.Summary.split("|");
1119 // skip the first two entries, those contain header and version info
1120 for(var i = 2; i < stringlist.length - 1; i++) {
1121 var pair = stringlist[i].split('=');
1122 var key = pair[0];
1123 var value = pair[1];
1124 log('stringlist: ' + key + '=\'' + value + '\'');
1125 if (config[key].Type == 'Int')
1126 config[key].Value = Number(value);
1127 else if (config[key].Type == 'String')
1128 config[key].Value = value;
1129 else if (config[key].Type == 'Bool')
1130 config[key].Value = (value == 'true')
1131 else if (config[key].Type == 'Enum')
1132 config[key].Value = value;
1133 else if (config[key].Type == 'UID')
1134 config[key].Value = Number(value);
1135 else if (config[key].Type == 'Array') {
1136 config[key].Value = value.split("^");
1137 if (config[key].Value.length == 1 && config[key].Value[0] == "") {
1138 config[key].Value = [];
1139 }
1140 }
1141 }
1142 settingsCache = entry.Summary;
1143 updateCssClasses();
1144 }
1145 else {
1146 log("Settings already cached and did not change");
1147 }
1148 }
1149 else {
1150 error("Failed to load settings, calendar entry could not be found");
1151 }
1152 }
1153
1154 function saveSettings()
1155 {
1156 getSettingsCalEntryId();
1157 var item = new Object();
1158 item.Type = "DayEvent";
1159 item.StartTime = new Date(2000, 0, 1);
1160 item.LocalId = settingsCalEntryId;
1161 item.Summary = "ComingNext Settings|" + version + "|";
1162
1163 for (var key in config) {
1164 if (config[key].Type == 'Int')
1165 item.Summary += key + "=" + config[key].Value.toString() + "|";
1166 else if (config[key].Type == 'String')
1167 item.Summary += key + "=" + config[key].Value + "|";
1168 else if (config[key].Type == 'Bool')
1169 item.Summary += key + "=" + (config[key].Value ? 'true' : 'false') + "|";
1170 else if (config[key].Type == 'Enum')
1171 item.Summary += key + "=" + config[key].Value + "|";
1172 else if (config[key].Type == 'UID')
1173 item.Summary += key + "=" + config[key].Value.toString() + "|";
1174 else if (config[key].Type == 'Array')
1175 item.Summary += key + "=" + config[key].Value.join("^") + "|";
1176 }
1177 settingsCache = item.Summary;
1178
1179 var criteria = new Object();
1180 criteria.Type = "CalendarEntry";
1181 criteria.Item = item;
1182
1183 log("Saving settings to calendar entry: " + item.Summary);
1184 try {
1185 var result = calendarService.IDataSource.Add(criteria);
1186 if (result.ErrorCode)
1187 error(result.ErrorMessage);
1188 } catch (e) {
1189 error("saveSettings: " + e + ', line ' + e.line);
1190 }
1191
1192 lastReloadTime = null; // force calendar data reload on next update
1193 clearUpdateTimer();
1194 setUpdateTimer();
1195 }
1196
1197 function toggleVisibility(elementId)
1198 {
1199 if (document.getElementById(elementId).style.display == "none")
1200 document.getElementById(elementId).style.display = "block";
1201 else
1202 document.getElementById(elementId).style.display = "none";
1203 }
1204
1205 var uniqueId = 0;
1206 function printHintBox(text)
1207 {
1208 uniqueId++;
1209 return '<td width="1%" align="right" onclick="javascript:toggleVisibility(\'info' + uniqueId + '\')">' + getLocalizedText('settings.help') + '</td></tr></table>'+
1210 '<div class="settingsInfo" id="info' + uniqueId + '">' + text + '</div>';
1211 }
1212
1213 function showAbout()
1214 {
1215 mode = 3;
1216 hideViews();
1217 document.getElementById("aboutView").style.display = "block";
1218 document.onclick = null;
1219
1220 window.menu.setLeftSoftkeyLabel(" ", function(){});
1221 window.menu.setRightSoftkeyLabel(getLocalizedText('softkey.back'), function()
1222 {
1223 mode = 1;
1224 showFullscreen();
1225 });
1226
1227 //document.getElementById("aboutView").innerHTML = 'aboutView';
1228 document.getElementById("name").innerHTML = "Coming Next " + version;
1229 }
1230
1231 function showHelp() {
1232 widget.openURL('http://comingnext.sf.net/help');
1233 }
1234
1235 function updateFullscreen()
1236 {
1237 }
1238
1239 function showFullscreen()
1240 {
1241 log("showFullscreen()");
1242 hideViews();
1243 document.getElementById("fullscreenView").style.display = "block";
1244 document.getElementById('body').className = "backgroundFullscreen";
1245 if (!errorOccured)
1246 document.onclick = launchCalendar;
1247 createMenu();
1248 updateData();
1249 }
1250
1251 function getBackgroundImage()
1252 {
1253 if (errorOccured)
1254 return '';
1255 var bgImage;
1256 if (config['backgroundImageLocation'].Value == config['backgroundImageLocation'].ValidValues[0]) // internal
1257 bgImage = 'background_' + orientation + '.png';
1258 else
1259 bgImage = 'C:/Data/background_' + panelNum + '_' + orientation + '.png';
1260 return bgImage;
1261 }
1262
1263 function updateHomescreen()
1264 {
1265 if (config['useBackgroundImage'].Value) {
1266 // check for screen rotation
1267 if (orientation != 'portrait' && screen.width == 360 && screen.height == 640) {
1268 window.widget.prepareForTransition("fade");
1269 orientation = 'portrait';
1270 document.getElementById('body').style.backgroundImage = 'url(' + getBackgroundImage() + ')';
1271 document.getElementById('body').style.backgroundColor = 'none';
1272 window.widget.performTransition();
1273 } else if (orientation != 'landscape' && screen.width == 640 && screen.height == 360) {
1274 window.widget.prepareForTransition("fade");
1275 orientation = 'landscape';
1276 document.getElementById('body').style.backgroundImage = 'url(' + getBackgroundImage() + ')';
1277 document.getElementById('body').style.backgroundColor = 'none';
1278 window.widget.performTransition();
1279 }
1280 else if (document.getElementById('body').style.backgroundImage == "")
1281 {
1282 document.getElementById('body').style.backgroundImage = 'url(' + getBackgroundImage() + ')';
1283 }
1284 }
1285 }
1286
1287 function showHomescreen()
1288 {
1289 log("showHomescreen()");
1290 hideViews();
1291 document.getElementById("homescreenView").style.display = "block";
1292 document.getElementById('body').className = "background";
1293 document.onclick = null;
1294 updateData();
1295 }
1296
1297 function getLocalizedText(p_Txt)
1298 {
1299 if (localizedText[p_Txt])
1300 return localizedText[p_Txt];
1301 else
1302 return 'ERROR: missing translation for ' + p_Txt;
1303 }
1304
1305 function showUpdate()
1306 {
1307 mode = 4;
1308 hideViews();
1309 document.getElementById("updateView").style.display = "block";
1310 document.onclick = null;
1311
1312 window.menu.setLeftSoftkeyLabel(getLocalizedText('update.checknow'), function(){
1313 checkForUpdate();
1314 });
1315 window.menu.setRightSoftkeyLabel(getLocalizedText('softkey.back'), function()
1316 {
1317 mode = 1;
1318 showFullscreen();
1319 });
1320
1321 document.getElementById("currentVersion").innerHTML = getLocalizedText("update.current") + version;
1322 checkForUpdate();
1323 }
1324
1325 function checkForUpdate()
1326 {
1327 // asynch XHR to server url
1328 reqV = new XMLHttpRequest();
1329 reqV.onreadystatechange = checkForUpdateCallback;
1330 document.getElementById("updateDiv").innerHTML = getLocalizedText("update.checking");
1331 reqV.open("GET", versionURL, true);
1332 reqV.setRequestHeader( "If-Modified-Since", "Sat, 1 Jan 2000 00:00:00 GMT" ); // disable caching
1333 reqV.send(null);
1334 }
1335
1336 function checkForUpdateCallback()
1337 {
1338 if (reqV.readyState == 4) {
1339 if (reqV.status == 200) {
1340 var resultXml = reqV.responseText;
1341 if (resultXml) {
1342 var div = document.getElementById("tmp");
1343 div.innerHTML = resultXml;
1344 var newVersion = div.getElementsByTagName('version')[0].innerHTML;
1345 var newVersionURL = div.getElementsByTagName('url')[0].innerHTML;
1346 div.innerHTML = "";
1347 if (version != newVersion) {
1348 document.getElementById("updateDiv").innerHTML = getLocalizedText("update.download").replace(/%1/, newVersion).replace(/%2/, newVersionURL);
1349 }
1350 else {
1351 document.getElementById("updateDiv").innerHTML = getLocalizedText("update.nonewversion");
1352 }
1353 }
1354 }
1355 else {
1356 document.getElementById("updateDiv").innerHTML = getLocalizedText("update.error") + reqV.status + " " + reqV.responseText;
1357 }
1358 }
1359 }
1360
1361 function hideViews()
1362 {
1363 document.getElementById("homescreenView").style.display = "none";
1364 document.getElementById("fullscreenView").style.display = "none";
1365 document.getElementById("aboutView").style.display = "none";
1366 document.getElementById("settingsView").style.display = "none";
1367 document.getElementById("updateView").style.display = "none";
1368 }
1369
1370 function listCalendars()
1371 {
1372 try {
1373 var criteria = {
1374 Type:'Calendar',
1375 Filter:{
1376 DefaultCalendar: false
1377 }
1378 }
1379
1380 var calendarsResult = calendarService.IDataSource.GetList(criteria);
1381 if (calendarsResult.ErrorCode != 0)
1382 throw("Error fetching list of calendars: " + calendarsResult.ErrorCode + ': ' + calendarsResult.ErrorMessage);
1383 var calendarListIterator = calendarsResult.ReturnValue;
1384
1385 var calendars = [];
1386 var count = 0;
1387 var item;
1388 while (( item = calendarListIterator.getNext()) != undefined ) {
1389 calendars[count++] = item;
1390 }
1391 log("Available Calendars: " + calendars.join(", "));
1392 return calendars;
1393 } catch(e) {
1394 error('listing calendars:' + e + ', line ' + e.line);
1395 return null;
1396 }
1397 }
1398
1399 // Copies all objects and their properties to an array. Data is copied so nothing gets lost when the reference is removed
1400 // Note: this will also set the "CalendarName" property of every entry to the name specified by the calendarName parameter if it has not been defined already
1401 function listToArray(list, calendarName)
1402 {
1403 var array = new Array();
1404 var item;
1405 var txt = "";
1406 while (( item = list.getNext()) != undefined ) {
1407 var itemCopy = new Object();
1408 for(var i=0; i < entryFields.length; i++) {
1409 itemCopy[entryFields[i]] = item[entryFields[i]];
1410 }
1411 // for some reason, the CalendarName property is never correctly queried, so we assign it manually here
1412 if (!itemCopy['CalendarName']) {
1413 itemCopy['CalendarName'] = calendarName;
1414 }
1415 array.push(itemCopy);
1416 txt += array[array.length - 1].Summary + ", ";
1417 }
1418 log("listToArray(): " + txt);
1419 return array;
1420 }
1421
1422 function sortCalendarEntries(a, b)
1423 {
1424 var atime, btime;
1425 log("sortCalendarEntries(" + a.Summary + "," + b.Summary + ")");
1426
1427 if (a.InstanceStartTime != null) {
1428 atime = a.InstanceStartTime;
1429 }
1430 else if (a.StartTime != null) {
1431 atime = a.StartTime;
1432 }
1433 else if (a.InstanceEndTime != null) {
1434 atime = a.InstanceEndTime;
1435 }
1436 else if (a.EndTime != null) {
1437 atime = a.EndTime;
1438 }
1439
1440 if (b.InstanceStartTime != null) {
1441 btime = b.InstanceStartTime;
1442 }
1443 else if (b.StartTime != null) {
1444 btime = b.StartTime;
1445 }
1446 else if (b.InstanceEndTime != null) {
1447 btime = b.InstanceEndTime;
1448 }
1449 else if (b.EndTime != null) {
1450 btime = b.EndTime;
1451 }
1452
1453 if (atime && btime) {
1454
1455 atime = parseDate(atime);
1456 btime = parseDate(btime);
1457
1458 // sort by date & time
1459 if (atime < btime) {
1460 return -1;
1461 }
1462 else if (atime > btime) {
1463 return 1;
1464 }
1465 // sort by type
1466 else if (a.Type != b.Type) {
1467 if (a.Type < b.Type) {
1468 return -1;
1469 }
1470 else if (a.Type > b.Type) {
1471 return 1;
1472 }
1473 }
1474 // sort by description
1475 else if (a.Summary && b.Summary && a.Summary != b.Summary) {
1476 if (a.Summary < b.Summary) {
1477 return -1;
1478 }
1479 else if (a.Summary > b.Summary) {
1480 return 1;
1481 }
1482 }
1483 }
1484 // NOTE: events my have no date information at all. In that case, we list events without date first
1485 else if (atime && !btime) {
1486 return 1;
1487 }
1488 else if (!atime && btime) {
1489 return -1;
1490 }
1491 else if (!atime && !btime) {
1492 // sort by type
1493 if (a.Type != b.Type) {
1494 if (a.Type < b.Type) {
1495 return -1;
1496 }
1497 else if (a.Type > b.Type) {
1498 return 1;
1499 }
1500 }
1501 // sort by description
1502 else if (a.Summary && b.Summary && a.Summary != b.Summary) {
1503 if (a.Summary < b.Summary) {
1504 return -1;
1505 }
1506 else if (a.Summary > b.Summary) {
1507 return 1;
1508 }
1509 }
1510 }
1511
1512 return 0;
1513 }
1514
1515 function updateCalendarColors()
1516 {
1517 var maxColors = 6;
1518 calendarColors = [];
1519 if (calendarList.length > maxColors) {
1520 log("updateCalendarColors(): Warning: more calendars than available indicator colors");
1521 }
1522 for(var i=0; i < calendarList.length; i++) {
1523 calendarColors[calendarList[i]] = (i % maxColors) + 1;
1524 }
1525 }
1526
1527 function log(message) {
1528 if (config['enableLogging'].Value) {
1529 console.info(message);
1530 }
1531 }
1532
1533 </script>
1534
1535 <style type="text/css">
1536 a { color:#aaccff }
1537 table { margin:0px; padding:0px; border-spacing:0px; }
1538 td { padding:0px 5px 0px 0px; white-space:nowrap; overflow:hidden; }
1539 hr { color:#ffffff; background-color:#ffffff; height:1px; text-align:left; border-style:none; }
1540 .settingsInfo { display:none; font-style:italic; }
1541 .title { font-weight:bold; font-size:14pt; }
1542 .textInput { width:90%; }
1543 .credits { margin-left:40px; text-indent: -20px; margin-bottom:0px; }
1544 #homescreenView { width: 315px; height:91px; overflow:hidden; }
1545 #calendarList { position:absolute; left:5px; top:4px; width:295px; height:75px; overflow:hidden; }
1546 #name { text-align:center; }
1547 #appicon { display: block; margin-left: auto; margin-right: auto; margin-top: 10px; }
1548 #smallappicon { width:22px; height:22px; margin-right:10px; float:left; }
1549 </style>
1550
1551 </head>
1552
1553 <body id="body" class="background">
1554 <div id="homescreenView">
1555 <div id="calendarList"></div>
1556 </div>
1557 <div id="fullscreenView" style="display:none;">
1558 <img src="Icon.png" id="smallappicon">
1559 <h1 class="title">Coming Next</h1>
1560 <hr />
1561 <div id="fullscreenCalendarList">loading...</div>
1562 </div>
1563 <div id="settingsView" style="display:none">
1564 <img src="Icon.png" id="smallappicon">
1565 <h1 id="settingsTitle" class="title">Settings</h1>
1566 <hr />
1567 <div id="settingsList"></div>
1568 </div>
1569 <div id="aboutView" style="display:none">
1570 <img src="Icon.png" id="appicon">
1571 <h1 id="name">Coming Next</h1>
1572 <hr />
1573 <p>Created by Dr. Cochambre and Michael Prager.</p>
1574 <p>Contributions:</p>
1575 <p class="credits">Paul Moore (bug fixes, new features and code cleanup)</p>
1576 <p class="credits">Manfred Hanselmann (DST support)</p>
1577 <p class="credits">Christophe Milsent (translation support & french translation)</p>
1578 <p class="credits">Flavio Nathan (portuguese-brazilian translation)</p>
1579 <p class="credits">Tokeda (russian translation)</p>
1580 <p class="credits">Marcella Ferrari (italian translation)</p>
1581 <p class="credits">Venos (italian translation)</p>
1582 <p>This software is open source and licensed under the GPLv3.</p>
1583 <p>Visit <a onclick="widget.openURL('http://comingnext.sf.net/'); return false;" href="http://comingnext.sf.net/">comingnext.sf.net</a> for free updates.</p>
1584 <hr />
1585 </div>
1586 <div id="updateView" style="display:none">
1587 <img src="Icon.png" id="smallappicon">
1588 <h1 class="title">Check for update</h1>
1589 <hr />
1590 <div id="currentVersion">Coming Next ??</div>
1591 <div id="updateDiv"></div>
1592 <div id="tmp" style="display:none;"></div>
1593 </div>
1594 </body>
1595
1596 </html>