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">
6 <title>Coming Next
</title>
8 <style type=
"text/css">
9 /* The following classes can be modified by widget settings */
10 .background { color:#ffffff; background-color:#
000000 }
11 .backgroundFullscreen { color:#ffffff; background-color:#
000000 }
14 .today { color:#ff0000 }
15 .tomorrow { color:#
0000ff }
17 .now { color:#ff00ff }
19 .icon { width:
15px; height:
15px }
20 .overdue { color:#ffff00 }
21 .calendar1 { background-color:#
0757cf }
22 .calendar2 { background-color:#
579f37 }
23 .calendar3 { background-color:#ff9f07 }
24 .calendar4 { background-color:#af8fef }
25 .calendar5 { background-color:#
57afbf }
26 .calendar6 { background-color:#
9fdf57 }
29 <script type=
"text/javascript" src=
"localizedTextStrings.js" charset=
"utf-8" />
32 // valid types for the config object are 'Int', 'Bool', 'String', 'Enum', 'UID', 'Array'
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',},
82 //-------------------------------------------------------
83 // Nothing of interest from here on...
84 //-------------------------------------------------------
85 var panelNum =
0; // use
1 for second panel
87 var versionURL =
"http://comingnext.sourceforge.net/version.xml";
88 var calendarService = null;
89 var cacheEntriesHtml = [];
90 var months_translated = [];
91 var weekdays_translated = [];
94 var mode =
0; //
0 = homescreen,
1 = fullscreen,
2 = settings,
3 = about,
4 = check for update
96 var settingsCalEntryId = null;
97 var settingsCache = null;
98 var notificationRequests = new Array();
99 var calendarList = [];
100 var calendarColors = [];
101 var updateTimer = null;
102 var screenRotationTimer = null;
103 var lastUpdateTime = now; // last time we updated the display
104 var lastReloadTime = null; // last time we fetched calendar data
105 var reloadInterval =
6 *
60 *
60 *
1000; // =
6 hours; time interval for reloading calendar data
106 var errorOccured = false;
107 var entryLists = null; // stores all fetched calendar entries until data is refreshed
108 var statupSuccessful = false; // indicates if everything started up wihtout errors. If we detect an error after that, it might just be a temporary problem e.g. by a backup process.
109 var use12hoursTimeFormat = false; // defines how time should be formated:
19:
00 or
07:
00 pm
110 var timeFormatSeparator =
":"; // format time
19:
00 or
19.00 depending on system setting
112 // vars for daylight saving time
113 var summertime = false; // true, if current date is in summer, false if in winter
114 var daylightSavingDates = new Object(); // caches calculated DST winter and summer time shift dates
116 // this is a list of data fields a calendar event can have
130 function isLeapYear( year ) {
131 if (( year %
4 ==
0 && year %
100 !=
0 ) || year %
400 ==
0 )
137 function calcLeapYear(year, days)
139 if (isLeapYear(year))
145 function subToSunday(myDate, year, days, prevMonthDays)
147 for (i = myDate.getDay(); i
> 0 ;i--)
149 days -= prevMonthDays;
150 days = isLeapYear(year) ? --days : days;
154 function isSummertime(curDate)
159 // if we already calculated DST summer and winter time dates for this year, use cached values
160 var dst = daylightSavingDates[curDate.getFullYear()];
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 );
167 thisYearSDays = nextYearSDays =
90;
168 thisYearWDays = nextYearWDays =
304;
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);
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);
181 Summer: new Date (curDate.getFullYear(),
03-
1, thisYearSDays,
2,
0,
0),
182 Winter: new Date (curDate.getFullYear(),
10-
1, thisYearWDays,
2,
0,
0),
184 daylightSavingDates[curDate.getFullYear()] = dst;
187 if (dst.Summer < curDate)
189 if (dst.Winter < curDate)
191 if (summer && !winter)
197 function error(message)
199 console.info('Error: ' + message);
200 document.getElementById(
"calendarList").innerHTML = 'Error: ' + message;
201 document.getElementById(
"fullscreenCalendarList").innerHTML = 'Error: ' + message;
203 document.onclick = null;
206 function areDatesEqual(date1, date2)
208 return (date1.getFullYear() == date2.getFullYear() &&
209 date1.getMonth() == date2.getMonth() &&
210 date1.getDate() == date2.getDate());
213 function isTomorrow(date)
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));
220 function isToday(date)
222 return areDatesEqual(date, now);
225 function collectLocales()
227 var tmpyear =
2000 + panelNum;
230 if (months_translated.length
> 0)
232 for (month =
0; month <
12; month++) {
233 var startDate = new Date(tmpyear, month,
15);
235 var item = new Object();
236 item.Type =
"DayEvent";
237 item.StartTime = startDate;
238 item.Summary =
"__temp" + month;
240 var criteria = new Object();
241 criteria.Type =
"CalendarEntry";
242 criteria.Item = item;
245 var result = calendarService.IDataSource.Add(criteria);
246 if (result.ErrorCode)
247 throw(result.ErrorMessage);
249 error(
"collectLocales: " + e + ', line ' + e.line);
252 for (weekday =
0; weekday <
7; weekday++) {
253 var startDate = new Date(
2000,
0,
2 + weekday); // date that we know for sure is a sunday
255 var item = new Object();
256 item.Type =
"DayEvent";
257 item.StartTime = startDate;
258 item.Summary =
"__weekday_temp" + weekday;
260 var criteria = new Object();
261 criteria.Type =
"CalendarEntry";
262 criteria.Item = item;
265 var result = calendarService.IDataSource.Add(criteria);
266 if (result.ErrorCode)
267 throw(result.ErrorMessage);
269 error(
"collectLocales: " + e + ', line ' + e.line);
273 var startTime = new Date(tmpyear,
0,
1);
274 var endTime = new Date(tmpyear,
11,
31);
275 var listFiltering = {
276 Type:'CalendarEntry',
278 StartRange: startTime,
280 SearchText: '__temp',
284 var result = calendarService.IDataSource.GetList(listFiltering);
285 if (result.ErrorCode)
286 throw(result.ErrorMessage);
287 var list = result.ReturnValue;
289 error(
"collectLocales: " + e + ', line ' + e.line);
292 var ids = new Array();
298 while (list && (entry = list.getNext()) != undefined) {
299 dateArr = (entry.StartTime + '').replace(/,/g,'').replace(/\./g,':').replace(/ /g,' ').split(' ');
300 var day = dateArr[
1];
301 var month = dateArr[
2];
302 var year = dateArr[
3];
304 // make sure month is set properly
305 if (isNaN(parseInt(day))) {
309 } else if (isNaN(parseInt(year))) {
315 log(entry.StartTime + ' -
> ' + month + ' ' + counter);
316 ids[counter] = entry.id;
317 months_translated[month] = counter +
1;
321 error(
"collectLocales: " + e + ', line ' + e.line);
325 var startTime = new Date(
2000,
0,
2);
326 var endTime = new Date(
2000,
0,
9);
327 var listFiltering = {
328 Type:'CalendarEntry',
330 StartRange: startTime,
332 SearchText: '__weekday_temp',
336 var result = calendarService.IDataSource.GetList(listFiltering);
337 if (result.ErrorCode)
338 throw(result.ErrorMessage);
339 var weekdaylist = result.ReturnValue;
341 error(
"collectLocales: " + e + ', line ' + e.line);
349 while (weekdaylist && (entry = weekdaylist.getNext()) != undefined) {
350 detectTimeFormat(entry.StartTime + '');
351 curWeekday = (entry.StartTime + '').split(',')[
0];
352 log(entry.StartTime + ' -
> ' + curWeekday + ' ' + counter2);
353 ids[counter + counter2] = entry.id;
354 weekdays_translated[counter2] = curWeekday;
358 error(
"collectLocales: " + e + ', line ' + e.line);
363 var criteria = new Object();
364 criteria.Type =
"CalendarEntry";
369 var result = calendarService.IDataSource.Delete(criteria);
370 if (result.ErrorCode)
371 throw(result.ErrorMessage);
373 error('deleting temp calendar entries:' + e + ', line ' + e.line);
378 // detects the system's current time format by parsing a native calendar timestamp (this is the only reliable formating across all devices and firmwares)
379 function detectTimeFormat(localeTimeString)
381 localeTimeString = localeTimeString.toLowerCase();
382 use12hoursTimeFormat = localeTimeString.indexOf(
"am") != -
1 || localeTimeString.indexOf(
"pm") != -
1 ? true : false;
383 timeFormatSeparator = localeTimeString.indexOf(
":") != -
1 ?
":" :
".";
386 function requestNotification()
388 var criteria = new Object();
389 criteria.Type =
"CalendarEntry";
390 criteria.Filter = new Object();
391 for(var i=
0; i < calendarList.length; i++) {
392 criteria.Filter.CalendarName = calendarList[i];
394 var notificationRequest = calendarService.IDataSource.RequestNotification(criteria, callback);
395 if (notificationRequest.ErrorCode)
396 error('requestNotification failed with error code ' + notificationRequest.ErrorCode);
397 notificationRequests.push(notificationRequest);
399 error(
"requestNotification: " + e + ', line ' + e.line);
403 var criteria2 = new Object();
404 criteria2.Type =
"CalendarEntry";
405 criteria2.Filter = new Object();
406 criteria2.Filter.LocalIdList = new Array();
407 criteria2.Filter.LocalIdList[
0] = settingsCalEntryId;
409 var notificationRequest = calendarService.IDataSource.RequestNotification(criteria2, settingsCallback);
410 if (notificationRequest.ErrorCode)
411 error('requestNotification failed with error code ' + notificationRequest.ErrorCode);
412 notificationRequests.push(notificationRequest);
414 error(
"requestNotification: " + e + ', line ' + e.line);
418 function cancelNotification()
420 for(var i=
0; i < notificationRequests.length; i++) {
422 var result = calendarService.IDataSource.Cancel(notificationRequests[i]);
423 if (result.ErrorCode)
424 error('cancelNotification failed with error code ' + result.ErrorCode);
426 error(
"cancelNotification: " + e + ', line ' + e.line);
431 function callback(transId, eventCode, result)
433 log(
"callback(): panelNum: " + panelNum +
" transId: " + transId +
" eventCode: " + eventCode +
" result.ErrorCode: " + result.ErrorCode);
434 lastReloadTime = null; // force calendar data reload on next update
438 function settingsCallback(transId, eventCode, result)
440 log(
"settingsCallback(): panelNum: " + panelNum +
" transId: " + transId +
" eventCode: " + eventCode +
" result.ErrorCode: " + result.ErrorCode);
444 function parseDate(dateString)
447 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:
448 Wednesday,
26 August,
2009 24:
00:
00
449 Wednesday,
26 August,
2009 12:
00:
00 am
450 Wednesday, August
26,
2009 12:
00:
00 am
451 Wednesday,
2009 August,
26 12:
00:
00 am
452 Wednesday,
2009 August,
28 8.00.00 pm
453 Wednesday,
2009 August,
28 08:
00:
00 PM
457 if (dateString ==
"" || dateString == null || dateString == undefined)
459 if (dateString instanceof Date) {
460 // we already have a date object, no need to parse string here
464 var dateArr = (dateString + '').replace(/,/g, '').replace(/\./g, ':').replace(/ /g, ' ').split(' ');
465 if (dateArr.length !=
5 && dateArr.length !=
6)
469 var weekDay = dateArr[
0];
470 var day = dateArr[
1];
471 var month = dateArr[
2];
472 var year = dateArr[
3];
473 // make sure month is set properly
474 if (isNaN(parseInt(day))) {
480 if (isNaN(parseInt(year))) {
485 // make sure day and year are set properly
486 if (Number(day)
> Number(year)) {
491 month = months_translated[month];
494 var timeArr = dateArr[
4].split(':');
495 if (timeArr.length !=
3)
497 var hours = Number(timeArr[
0]);
498 var minutes = Number(timeArr[
1]);
499 var seconds = Number(timeArr[
2]);
500 if (dateArr.length ==
6 && dateArr[
5].toLowerCase() == 'pm' && hours <
12)
502 if (dateArr.length ==
6 && dateArr[
5].toLowerCase() == 'am' && hours ==
12)
505 result = new Date(year, month -
1, day, hours, minutes, seconds);
508 // take care of daylight saving time
509 if (config['enableDaylightSaving'].Value) {
511 // determine if date is in summer or winter time
512 var dateSummerTime = isSummertime(result);
514 // work around bug in Nokias calendar api resulting in dates within a different DST to be off by
1 hour
515 if (summertime && !dateSummerTime) {
516 result = new Date(result.getTime() -
1000 *
60 *
60 * config['daylightSavingOffset'].Value); // -
1 hour
517 log('parseDate(): fixing time -
1h: ' + result);
519 else if (!summertime && dateSummerTime) {
520 result = new Date(result.getTime() +
1000 *
60 *
60 * config['daylightSavingOffset'].Value); // +
1 hour
521 log('parseDate(): fixing time +
1h: ' + result);
528 function getWeekdayLocalized(date) {
529 var localizedString = date.toLocaleDateString();
530 if (localizedString.indexOf(
",") == -
1) {
531 return weekdays_translated[date.getDay()];
533 return localizedString.split(',')[
0];
536 // 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"
537 function formatDate(date, format)
539 var day = date.getDate().toString();
540 var month = (date.getMonth() +
1).toString();
541 while (day.length <
2) { day = '
0' + day; }
542 while (month.length <
2) { month = '
0' + month; }
544 if (config['showTodayAsText'].Value && isToday(date))
545 return '
<span class=
"today">' + config['todayText'].Value + '
</span>';
546 if (config['showTodayAsText'].Value && isTomorrow(date))
547 return '
<span class=
"tomorrow">' + config['tomorrowText'].Value + '
</span>';
549 if (format instanceof Date) {
550 // we don't know how to format this
551 if (config['dateFormat'].Value == 'auto' || config['dateFormat'].Value == 'DDMM')
552 return day + config['dateSeparator'].Value + month;
554 return month + config['dateSeparator'].Value + day;
556 var dateArr = format.replace(/,/g,'').replace(/\./g,':').replace(/ /g,' ').split(' ');
557 if (dateArr.length !=
5 && dateArr.length !=
6) {
558 // we don't know how to format this
559 if (config['dateFormat'].Value == 'auto' || config['dateFormat'].Value == 'DDMM')
560 return day + config['dateSeparator'].Value + month;
562 return month + config['dateSeparator'].Value + day;
566 if (config['dateFormat'].Value == 'MMDD')
568 else if (config['dateFormat'].Value == 'DDMM')
571 // config['dateFormat'].Value == 'auto', try to detect system setting
573 var day_ = dateArr[
1];
574 var month_ = dateArr[
2];
575 var year_ = dateArr[
3];
576 // make sure month is set properly
577 if (isNaN(parseInt(day_))) {
582 } else if (isNaN(parseInt(year_))) {
588 // make sure day and year are set properly
589 if (Number(day_)
> Number(year_))
594 return day + config['dateSeparator'].Value + month;
596 return month + config['dateSeparator'].Value + day;
599 function formatTime(date)
601 // date is a Date() object
602 var hour = date.getHours();
603 var minute = date.getMinutes();
605 // don't use Date().toLocaleTimeString() as it is utterly broken on newer firmwares
606 if (use12hoursTimeFormat) {
617 minute =
"0" + minute;
618 time = hour + timeFormatSeparator + minute +
" " + ap;
624 minute =
"0" + minute;
625 time = hour + timeFormatSeparator + minute;
628 if (config['showNowAsText'].Value && date.getTime() == now.getTime())
629 time = '
<span class=
"now">' + config['nowText'].Value + '
</span>';
630 log(
"formatTime(): " + time +
", use12hoursTimeFormat=" + use12hoursTimeFormat +
", timeFormatSeparator=" + timeFormatSeparator +
", date.toLocateTimeString(): " + date.toLocaleTimeString());
634 function updateData()
641 // check if we got additional or less calendars since our last update
642 var newCalendarList = listCalendars();
643 if (newCalendarList == null) {
644 // Something went wrong fetching the calendars list.
645 // This usually happens when a backup is being made.
646 // Retry the next time updateData() is called by
647 // resetting errorOccured
648 log('updateData(): listCalendars() failed, trying again later...');
649 cacheEntriesHtml = ''; // make sure we replace the currently shown error message on the next update
650 errorOccured = false;
653 if (newCalendarList.length != calendarList.length) {
654 calendarList = newCalendarList;
655 updateCalendarColors();
656 cancelNotification();
657 requestNotification();
658 lastReloadTime = null; // force calendar data reload on this update
663 // only reload calendar data every
6 hours, visual updates occure more often
664 if (!lastReloadTime || now.getTime() - lastReloadTime.getTime()
> reloadInterval) {
665 log('updateData(): reloading calendar data');
667 // meetings have time
668 // 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
669 summertime = isSummertime(now); // cache summer time info for today
670 var meetingList = [];
671 for(var i=
0; i < calendarList.length; i++) {
672 // ignore excluded calendars
673 if (config['excludedCalendars'].Value.indexOf(calendarList[i]) != -
1)
675 var meetingListFiltering = {
676 Type:'CalendarEntry',
678 CalendarName: calendarList[i],
679 StartRange: (new Date(now.getFullYear(), now.getMonth(), now.getDate(),
0,
0,
0)),
680 EndRange: (new Date(now.getFullYear(), now.getMonth() + config['monthRange'].Value, now.getDate(),
0,
0,
0))
683 var meetingResult = calendarService.IDataSource.GetList(meetingListFiltering);
684 if (meetingResult.ErrorCode !=
0)
685 throw(
"Error fetching calendar data: " + meetingResult.ErrorCode + ': ' + meetingResult.ErrorMessage);
686 var list = meetingResult.ReturnValue;
687 meetingList = meetingList.concat(listToArray(list, calendarList[i]));
689 log(
"updateData(): meetingList.sort()");
690 meetingList.sort(sortCalendarEntries);
692 // todos don't, they start on
00:
00 hrs., but should be visible anyway
693 // this will generate a list of passed todos. We have to check if they have been marked as
"done" yet
694 if (config['includeTodos'].Value) {
695 var todayTodoList = [];
696 for(var i=
0; i < calendarList.length; i++) {
697 // ignore excluded calendars
698 if (config['excludedCalendars'].Value.indexOf(calendarList[i]) != -
1)
700 var todayTodoListFiltering = {
701 Type:'CalendarEntry',
703 CalendarName: calendarList[i],
705 StartRange: (new Date(now.getFullYear() -
1, now.getMonth(), now.getDate(),
0,
0,
0)),
706 EndRange: (new Date(now.getFullYear(), now.getMonth(), now.getDate(),
0,
0,
1))
709 var todayTodoResult = calendarService.IDataSource.GetList(todayTodoListFiltering);
710 var list = todayTodoResult.ReturnValue;
711 todayTodoList = todayTodoList.concat(listToArray(list, calendarList[i]));
713 log(
"updateData(): todayTodoList.sort()");
714 todayTodoList.sort(sortCalendarEntries);
715 entryLists = [todayTodoList, meetingList];
717 entryLists = [meetingList];
719 lastReloadTime = new Date();
721 error('loading Calendar items list:' + e + ', line ' + e.line);
731 var fontsize = 'normal';
732 var lineheight = 'normal';
734 fontsize = parseInt(
72 / config['eventsPerWidget'].Value) + 'px';
735 lineheight = parseInt(
82 / config['eventsPerWidget'].Value) + 'px';
737 if (config['eventsPerWidget'].Value ==
3) {
738 changeCssClass('.icon', 'width:
20px; height:
20px');
740 else if (config['eventsPerWidget'].Value ==
5) {
741 changeCssClass('.icon', 'width:
10px; height:
10px');
743 else if (config['eventsPerWidget'].Value ==
6) {
744 changeCssClass('.icon', 'width:
8px; height:
8px');
748 changeCssClass('.icon', config['cssStyle_icon'].Value);
749 var entriesHtml = '
<table style=
"font-size:' + fontsize + '; line-height:' + lineheight + ';">';
751 entriesHtml = '
<table width=
"307" height=
"82"><tr><td>' + entriesHtml; // this is needed to center the actual content vertically
755 max = (panelNum +
1) * config['eventsPerWidget'].Value;
757 max =
30; // we can display a lot more events in fullscreen mode
759 if (config['enableLogging'].Value) {
761 for (var i=
0; i < entryLists.length; i++) {
762 listinfo = listinfo +
" " + entryLists[i].length;
763 var entrieslist =
"";
764 for (var j=
0; j < entryLists[i].length; j++) {
765 entrieslist += entryLists[i][j].Summary +
", ";
767 log(
"updateData(): entrieslist: " + entrieslist);
769 log(
"updateData(): inner loop, " + entryLists.length +
" lists, [" + listinfo +
"] entries");
772 // the first outer loop iteration is for passed ToDos, the second loop is for all upcomming events (may also include ToDos)
773 for (var i=
0; counter < max && i < entryLists.length; i++) {
774 for (var j=
0; (counter < max) && (j < entryLists[i].length); j++) {
775 entry = entryLists[i][j];
778 // output event info for debugging
779 var entryInfo =
"event: ";
780 for(var k=
0; k < entryFields.length; ++k) {
781 if (entry[entryFields[k]] != undefined) {
782 entryInfo += entryFields[k] +
"=" + entry[entryFields[k]] +
",";
787 // we don't want ToDos when includeTodos == false or when they are completed
788 if (entry.Type == 'ToDo' && (entry.Status ==
"TodoCompleted" || !config['includeTodos'].Value)) {
789 log('skipping ' + entry.id );
794 // make sure that we don't include an event twice (useful for ToDos that might come up twice)
795 if (eventIds[entry.id] ==
1 && entry.Type == 'ToDo') {
796 log('skipped (already included) ' + entry.id);
800 eventIds[entry.id] =
1;
802 // summary can be undefined!
803 var Summary = ((entry.Summary == null) ? '' : entry.Summary);
804 if (entry.Location != '' && entry.Location != undefined && config['showLocation'].Value)
805 Summary += ', ' + entry.Location;
807 // fix by yves: determine start and end dates/times
808 entryStartTime = ((entry.InstanceStartTime == null) ? entry.StartTime : entry.InstanceStartTime);
809 entryEndTime = ((entry.InstanceEndTime == null) ? entry.EndTime : entry.InstanceEndTime);
811 // there can be ToDos that have no date at all!
812 if (entry.Type == 'ToDo' && entry.EndTime == null)
813 entryDate =
""; // this will cause parseDate(entryDate) to return null;
815 entryDate = ((entry.Type == 'ToDo') ? entryEndTime : entryStartTime); // ToDo's use their EndTime, the rest use StartTime
817 // Convert date/time string to Date object
818 var date = parseDate(entryDate);
819 log('date: ' + date);
820 var endDate = ((entryEndTime == null) ? null : parseDate(entryEndTime));
821 log('endDate: ' + endDate);
823 // check if Meeting is actually a DayEvent. Bug introduced by
"Anna" updates to various Symbian^
3 devices.
824 // Note that this workaround is not
100% save! It might missinterpret some meetings as dayevents of starting and ending on
00:
00
825 if (entry.Type == 'Meeting' && date.getHours() ==
0 && date.getMinutes() ==
0 &&
826 endDate != null && endDate.getHours() ==
0 && endDate.getMinutes() ==
0) {
827 log('fixing event type: changed from
"Meeting" to
"DayEvent".');
828 entry.Type = 'DayEvent';
831 // check if meeting event has already passed
832 if (entry.Type == 'Meeting') {
833 var compareTime = ((endDate == null) ? date.getTime() : endDate.getTime());
834 if (now.getTime()
> compareTime) {
835 log('skipping Meeting (already passed) ' + entry.id);
837 eventIds[entry.id] =
0;
842 // check if anniversary passed (not sure why they are in the list, the query was only for today - nokia?)
843 if (entry.Type == 'Anniversary') {
844 var tmp = new Date(now.getFullYear(), now.getMonth(), now.getDate(),
0,
0,
0);
845 if (date.getTime() < tmp.getTime()) {
846 log('skipping Anniversary (already passed) ' + entry.id);
848 eventIds[entry.id] =
0;
853 // fix DayEvents end time. End times are off by
1 Second. It's possible that the event has already passed
854 if (entry.Type == 'DayEvent' && endDate != null) {
855 endDate.setMinutes(endDate.getMinutes() -
1);
856 log('fixing DayEvent endDate: ' + endDate);
857 if (now.getTime()
> endDate.getTime()) {
858 log('event already passed ' + entry.id);
860 eventIds[entry.id] =
0;
865 // check if the event is currently taking place
866 if (entryStartTime != null && entryEndTime != null && date != null && endDate != null) {
867 // check if we are between start and endtime
868 if ((date.getTime() < now.getTime()) && (now.getTime() < endDate.getTime())) {
869 date = now; // change appointment date/time to now
870 log('event is currently taking place: ' + date);
874 // skip events for the first panel in case this is the second one and we're not in fullscreen mode
875 if (mode ==
0 && panelNum
> 0 && counter < panelNum * config['eventsPerWidget'].Value +
1) {
876 log('skipping (already in first widget) ' + entry.id);
880 // mark overdue todos
882 if (entry.Type == 'ToDo' && date != null) {
883 var tmp1 = new Date(date.getFullYear(), date.getMonth(), date.getDate(),
0,
0,
0);
884 var tmp2 = new Date(now.getFullYear(), now.getMonth(), now.getDate(),
0,
0,
0);
885 if (tmp1.getTime() < tmp2.getTime()) {
890 // generate html output
891 entriesHtml += '
<tr>';
892 if (config['showCalendarIndicator'].Value && calendarList.length - config['excludedCalendars'].Value.length
> 1) {
893 entriesHtml += '
<td><span class=
"calendar' + calendarColors[entry.CalendarName] + '"> </span></td>';
895 entriesHtml += '
<td><img class=
"icon" src=
"' + entry.Type + '.png" /></td>';
897 // some languages have very strange locale date formats, can't parse all those. Also some todos don't have dates at all.
898 entriesHtml += '
<td colspan=
"4"><span class=
"date">' + entryDate + '
</span> ';
900 var weekDay = getWeekdayLocalized(date).substr(
0,config['weekDayLength'].Value);
901 log('date.toLocaleDateString(): ' + date.toLocaleDateString());
902 log('weekDay: ' + weekDay);
903 var time = formatTime(date);
904 var dateStr = formatDate(date, entryDate);
905 if (entry.Type == 'ToDo' && overdue && config['markOverdueTodos'].Value) {
906 dateStr = '
<span class=
"overdue">' + config['overdueText'].Value + '
</span>';
907 entriesHtml += '
<td colspan=
"4" width=
"1px"><span class=
"date">' + dateStr + '
</span> ';
908 } else if (entry.Type == 'ToDo' || entry.Type == 'Anniversary' || entry.Type == 'DayEvent' || entry.Type == 'Reminder') {
909 if ((isToday(date) || isTomorrow(date)) && config['showTodayAsText'].Value) // show weekday if the date string is not text. looks odd otherwise
910 entriesHtml += '
<td colspan=
"4" width=
"1px"><span class=
"date">' + dateStr + '
</span> ';
912 entriesHtml += '
<td class=
"weekDay" width=
"1px">' + weekDay + '
</td><td width=
"1px" class=
"date">' + dateStr + '
</td><td colspan=
"2">';
913 } else if (entry.Type == 'Meeting') {
914 if (config['showCombinedDateTime'].Value) {
916 entriesHtml += '
<td width=
"1px" colspan=
"4"><span class=
"today">' + time + '
</span> ';
917 else if (isTomorrow(date))
918 entriesHtml += '
<td width=
"1px" colspan=
"4"><span class=
"tomorrow">' + dateStr + '
</span> <span class=
"time">' + time + '
</span> ';
920 entriesHtml += '
<td width=
"1px" class=
"weekDay">' + weekDay + '
</td><td width=
"1px" class=
"date">' + dateStr + '
</td><td colspan=
"2">';
922 if ((isToday(date) || isTomorrow(date)) && config['showTodayAsText'].Value)
923 entriesHtml += '
<td colspan=
"4" width=
"1px"><span class=
"today">' + dateStr + '
</span> <span class=
"time">' + time + '
</span> ';
925 entriesHtml += '
<td width=
"1px" class=
"weekDay">' + weekDay + '
</td><td width=
"1px" class=
"date">' + dateStr + '
</td><td width=
"1px" class=
"time">' + time + '
</td><td>';
929 entriesHtml += '
<span class=
"description">' + Summary + '
</span></td></tr>';
932 entriesHtml += '
</table>';
934 entriesHtml = entriesHtml + '
</td></tr></table>';
935 if (config['showNothingText'].Value && entriesHtml == '
<table></table>') {
936 var text = config['nothingText'].Value.replace(/%d/, config['monthRange'].Value);
937 entriesHtml = '
<div style=
"width:295px; height:75px; text-align:center; line-height:75px; overflow:visible;">' + text + '
</div>';
939 log(
"output: " + entriesHtml);
940 if (cacheEntriesHtml != entriesHtml) {
942 document.getElementById('calendarList').innerHTML = entriesHtml;
944 document.getElementById('fullscreenCalendarList').innerHTML = entriesHtml;
945 cacheEntriesHtml = entriesHtml;
948 lastUpdateTime = new Date();
950 error('displaying list:' + e + ', line ' + e.line);
955 // called by handleOnShow() and onResize events
956 function updateScreen()
958 log('updateScreen()');
960 // check if opening fullscreen
961 if( window.innerHeight
> 91 && mode ==
0) {
963 cacheEntriesHtml = '';
964 document.getElementById('body').style.backgroundImage =
"";
967 else if (window.innerHeight <=
91 && mode !=
0) {
969 cacheEntriesHtml = '';
974 updateHomescreen(); // check for screen rotation
979 function handleOnShow()
983 var time = new Date();
984 if (time.getTime() - lastUpdateTime.getTime()
> config['updateDataInterval'].Value *
60 *
1000) {
985 log('updateScreen(): force updateData() because last update was too long ago (' + (time.getTime() - lastUpdateTime.getTime()) /
1000 + 's)');
988 setUpdateTimer(); // reinitialize update timer
992 function launchCalendar()
995 widget.openApplication(config['calendarApp'].Value,
"");
996 if (config['hideWidgetOnCalendarOpen'].Value)
999 error('starting Calendar App');
1006 log('New widget instance starting up...');
1009 // call calendar service
1010 if (device !=
"undefined")
1011 calendarService = device.getServiceObject(
"Service.Calendar",
"IDataSource");
1013 throw('device object does not exist');
1015 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>');
1019 calendarList = listCalendars();
1021 updateCalendarColors();
1024 requestNotification();
1025 document.getElementById(
"settingsTitle").innerHTML = getLocalizedText('menu.settings');
1027 if (window.innerHeight
> 91) {
1028 mode =
0; // we're starting fullscreen, we set mode to homescreen in order to let updateScreen() do all the work for us
1033 log(
"init(): updateScreen()");
1035 if (config['useBackgroundImage'].Value)
1036 // check for screen rotation every
1 secs
1037 screenRotationTimer = window.setInterval('checkOrientation()',
1000 *
1);
1039 // call updateScreen() when widget changes from background to forground
1040 window.widget.onshow = handleOnShow;
1042 log(
"init(): finished...");
1044 statupSuccessful = true;
1047 function checkOrientation()
1051 updateHomescreen(); // check for screen rotation
1054 function setUpdateTimer()
1056 updateTimer = window.setInterval('updateTimerCallback()',
1000 *
60 * config['updateDataInterval'].Value);
1059 function clearUpdateTimer()
1061 window.clearInterval(updateTimer);
1064 function updateTimerCallback()
1066 log(
"updateTimerCallback()");
1070 function createMenu()
1072 window.menu.setLeftSoftkeyLabel(
"",null);
1073 window.menu.setRightSoftkeyLabel(
"",null);
1075 var menuSettings = new MenuItem(getLocalizedText('menu.settings'), id++);
1076 var menuCallApp = new MenuItem(getLocalizedText('menu.openCalendarApp'), id++);
1077 var menuHelp = new MenuItem(getLocalizedText('menu.help'), id++);
1078 var menuUpdate = new MenuItem(getLocalizedText('menu.update'), id++);
1079 var menuAbout = new MenuItem(getLocalizedText('menu.about'), id++);
1080 menuSettings.onSelect = showSettings;
1081 menuAbout.onSelect = showAbout;
1082 menuCallApp.onSelect = launchCalendar;
1083 menuUpdate.onSelect = showUpdate;
1084 menuHelp.onSelect = showHelp;
1085 window.menu.clear();
1086 window.menu.append(menuCallApp);
1087 window.menu.append(menuSettings);
1088 window.menu.append(menuHelp);
1089 window.menu.append(menuUpdate);
1090 window.menu.append(menuAbout);
1093 function showSettings()
1097 document.getElementById(
"settingsView").style.display =
"block";
1098 document.onclick = null;
1100 window.menu.setLeftSoftkeyLabel(getLocalizedText('settings.save'), function()
1102 for (var key in config) {
1103 if (config[key].Type == 'String')
1104 config[key].Value = document.forms[
0].elements[
"settings." + key].value;
1105 else if (config[key].Type == 'Int') {
1106 config[key].Value = parseInt(document.forms[
0].elements[
"settings." + key].value);
1107 if (config[key].Value <
0 || isNaN(config[key].Value))
1108 config[key].Value = config[key].Default;
1110 else if (config[key].Type == 'Bool')
1111 config[key].Value = document.forms[
0].elements[
"settings." + key].checked;
1112 else if (config[key].Type == 'UID') {
1113 config[key].Value = parseInt(document.forms[
0].elements[
"settings." + key].value);
1114 if (isNaN(config[key].Value))
1115 config[key].Value = config[key].Default;
1117 else if (config[key].Type == 'Enum') {
1118 config[key].Value = document.forms[
0].elements[
"settings." + key].value;
1119 if (config[key].ValidValues.indexOf(config[key].Value) == -
1)
1120 config[key].Value = config[key].Default;
1122 else if (config[key].Type == 'Array') {
1123 if (key == 'excludedCalendars') {
1124 config[key].Value = new Array();
1125 for(var i=
0; i < calendarList.length; i++) {
1126 var element = document.forms[
0].elements[
"settings." + key +
"." + calendarList[i]];
1127 if (element != null && element.checked == false)
1128 config[key].Value.push(calendarList[i]);
1141 window.menu.setRightSoftkeyLabel(getLocalizedText('settings.cancel'), function()
1147 var settingsHtml = '
<form>';
1148 for (var key in config) {
1149 if (config[key].Type == 'String') {
1151 if (key.substring(
0,
9) ==
"cssStyle_")
1152 prefix = getLocalizedText('settings.cssStyle_prefix');
1153 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 />';
1155 else if (config[key].Type == 'Int')
1156 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 />';
1157 else if (config[key].Type == 'Bool')
1158 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 />';
1159 else if (config[key].Type == 'UID')
1160 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 />';
1161 else if (config[key].Type == 'Enum') {
1162 settingsHtml += '
<table><tr><td>' + getLocalizedText('settings.name.' + key) + '
<br /><select name=
"settings.' + key + '" size=
"1">';
1163 for(var i =
0; i < config[key].ValidValues.length; i++)
1164 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>';
1165 settingsHtml += '
</select></div></td>' + printHintBox(getLocalizedText('settings.info.' + key)) + '
<hr />';
1167 else if (config[key].Type == 'Array') {
1168 settingsHtml += '
<table><tr><td>' + getLocalizedText('settings.name.' + key) + '
<br />';
1169 if (key == 'excludedCalendars') {
1170 for(var i=
0; i < calendarList.length; i++) {
1171 var checked = '
checked=
"checked"';
1172 if (config[key].Value.indexOf(calendarList[i]) != -
1)
1174 settingsHtml += '
<input name=
"settings.' + key + '.' + calendarList[i] + '" type=
"checkbox" value=
"' + calendarList[i] + '" ' + checked + '
/> ' + calendarList[i] + '
<br />';
1177 settingsHtml += '
</td>' + printHintBox(getLocalizedText('settings.info.' + key)) + '
<hr />';
1180 settingsHtml += '
<input name=
"reset" type=
"button" value=
"' + getLocalizedText('settings.restoreDefaults') + '" onclick=
"javascript:restoreDefaultSettings();showSettings();" />';
1181 settingsHtml += '
</form>';
1182 document.getElementById(
"settingsList").innerHTML = settingsHtml;
1185 function changeCssClass(classname, properties)
1187 for(var i =
0; i < document.styleSheets[
0]['cssRules'].length; i++)
1189 if (document.styleSheets[
0]['cssRules'][i].selectorText == classname) {
1190 document.styleSheets[
0].deleteRule(i);
1191 document.styleSheets[
0].insertRule(classname + ' { ' + properties + ' }', document.styleSheets[
0]['cssRules'].length);
1197 function updateCssClasses()
1199 for(var key in config) {
1200 changeCssClass(getLocalizedText('settings.name.' + key), config[key].Value);
1204 function getSettingsCalEntryId()
1206 if (settingsCalEntryId == null) {
1207 // check if entry already exists
1208 var listFiltering = {
1209 Type:'CalendarEntry',
1211 StartRange: new Date(
1999,
11,
30), // note: due to Nokia's buggy calendar API, the settings event can be on
01.01.2000 AND on
31.12.1999, depending on when the calendar entry was created (in summer or winter). It is not even possible to narrow the search down to these two days (probably because of DST offsets). So we're looking for an event between
30.12.1999 and
02.01.2000!
1212 EndRange: new Date(
2000,
0,
2),
1213 SearchText: 'ComingNext Settings|',
1219 result = calendarService.IDataSource.GetList(listFiltering);
1220 if (result.ErrorCode)
1221 throw(result.ErrorMessage);
1224 error(
"getSettingsCalEntryId: GetList() failed: " + e + ', line ' + e.line);
1227 var list = result.ReturnValue;
1228 var entry = list.getNext();
1229 if (entry != undefined) {
1230 settingsCalEntryId = entry.LocalId;
1231 log(
"settingsCalEntryId=" + settingsCalEntryId);
1233 else { // create settings item
1234 var item = new Object();
1235 item.Type =
"DayEvent";
1236 item.StartTime = new Date(
2000,
0,
1);
1237 item.Summary =
"ComingNext Settings|";
1239 var criteria = new Object();
1240 criteria.Type =
"CalendarEntry";
1241 criteria.Item = item;
1244 var result = calendarService.IDataSource.Add(criteria);
1245 if (result.ErrorCode)
1246 throw(result.ErrorMessage);
1248 error(
"getSettingsCalEntryId: " + e + ', line ' + e.line);
1251 getSettingsCalEntryId();
1256 function restoreDefaultSettings()
1258 for (var key in config)
1259 config[key].Value = config[key].Default;
1262 function loadSettings()
1264 getSettingsCalEntryId();
1265 var listFiltering = {
1266 Type:'CalendarEntry',
1268 LocalId: settingsCalEntryId
1273 result = calendarService.IDataSource.GetList(listFiltering);
1274 if (result.ErrorCode)
1275 throw(result.ErrorMessage);
1278 error(
"loadSettings: GetList() failed: " + e + ', line ' + e.line);
1281 var entry = result.ReturnValue.getNext();
1282 if (entry != undefined) {
1283 log(
"Loading Settings...");
1284 // only reload settings if they chanced since the last reload
1285 if (settingsCache != entry.Summary)
1287 restoreDefaultSettings();
1288 var stringlist = entry.Summary.split(
"|");
1289 // skip the first two entries, those contain header and version info
1290 for(var i =
2; i < stringlist.length -
1; i++) {
1291 var pair = stringlist[i].split('=');
1293 var value = pair[
1];
1294 if (key == null || value == null || config[key] == null) {
1295 log('Warning: unknown or invalid setting: ' + stringlist[i]);
1298 log('stringlist[' + i + ']: ' + key + '=\'' + value + '\'');
1299 if (config[key].Type == 'Int') {
1300 config[key].Value = Number(value);
1301 if (isNaN(config[key].Value))
1302 config[key].Value = config[key].Default;
1304 else if (config[key].Type == 'String')
1305 config[key].Value = value;
1306 else if (config[key].Type == 'Bool')
1307 config[key].Value = (value == 'true')
1308 else if (config[key].Type == 'Enum')
1309 config[key].Value = value;
1310 else if (config[key].Type == 'UID') {
1311 config[key].Value = Number(value);
1312 if (isNaN(config[key].Value))
1313 config[key].Value = config[key].Default;
1315 else if (config[key].Type == 'Array') {
1316 config[key].Value = value.split(
"^");
1317 if (config[key].Value.length ==
1 && config[key].Value[
0] ==
"") {
1318 config[key].Value = [];
1322 settingsCache = entry.Summary;
1326 log(
"Settings already cached and did not change");
1330 error(
"Failed to load settings, calendar entry could not be found");
1334 function saveSettings()
1336 getSettingsCalEntryId();
1337 var item = new Object();
1338 item.Type =
"DayEvent";
1339 item.StartTime = new Date(
2000,
0,
1);
1340 item.LocalId = settingsCalEntryId;
1341 item.Summary =
"ComingNext Settings|" + version +
"|";
1343 for (var key in config) {
1344 if (config[key].Type == 'Int')
1345 item.Summary += key +
"=" + config[key].Value.toString() +
"|";
1346 else if (config[key].Type == 'String')
1347 item.Summary += key +
"=" + config[key].Value +
"|";
1348 else if (config[key].Type == 'Bool')
1349 item.Summary += key +
"=" + (config[key].Value ? 'true' : 'false') +
"|";
1350 else if (config[key].Type == 'Enum')
1351 item.Summary += key +
"=" + config[key].Value +
"|";
1352 else if (config[key].Type == 'UID')
1353 item.Summary += key +
"=" + config[key].Value.toString() +
"|";
1354 else if (config[key].Type == 'Array')
1355 item.Summary += key +
"=" + config[key].Value.join(
"^") +
"|";
1357 settingsCache = item.Summary;
1359 var criteria = new Object();
1360 criteria.Type =
"CalendarEntry";
1361 criteria.Item = item;
1363 log(
"Saving settings to calendar entry: " + item.Summary);
1365 var result = calendarService.IDataSource.Add(criteria);
1366 if (result.ErrorCode)
1367 throw(result.ErrorMessage);
1369 error(
"saveSettings: " + e + ', line ' + e.line);
1372 lastReloadTime = null; // force calendar data reload on next update
1377 function toggleVisibility(elementId)
1379 if (document.getElementById(elementId).style.display ==
"none")
1380 document.getElementById(elementId).style.display =
"block";
1382 document.getElementById(elementId).style.display =
"none";
1386 function printHintBox(text)
1389 return '
<td width=
"1%" align=
"right" onclick=
"javascript:toggleVisibility(\'info' + uniqueId + '\')">' + getLocalizedText('settings.help') + '
</td></tr></table>'+
1390 '
<div class=
"settingsInfo" id=
"info' + uniqueId + '">' + text + '
</div>';
1393 function showAbout()
1397 document.getElementById(
"aboutView").style.display =
"block";
1398 document.onclick = null;
1400 window.menu.setLeftSoftkeyLabel(
" ", function(){});
1401 window.menu.setRightSoftkeyLabel(getLocalizedText('softkey.back'), function()
1407 //document.getElementById(
"aboutView").innerHTML = 'aboutView';
1408 document.getElementById(
"name").innerHTML =
"Coming Next " + version;
1411 function showHelp() {
1412 widget.openURL('http://comingnext.sf.net/help');
1415 function updateFullscreen()
1419 function showFullscreen()
1421 log(
"showFullscreen()");
1423 document.getElementById(
"fullscreenView").style.display =
"block";
1424 document.getElementById('body').className =
"backgroundFullscreen";
1426 document.onclick = launchCalendar;
1431 function getBackgroundImage()
1436 if (config['backgroundImageLocation'].Value == config['backgroundImageLocation'].ValidValues[
0]) // internal
1437 bgImage = 'background_' + orientation + '.png';
1439 bgImage = 'C:/Data/background_' + panelNum + '_' + orientation + '.png';
1443 function updateHomescreen()
1445 if (config['useBackgroundImage'].Value) {
1446 // check if we have a completely unknown screen resolution
1447 var screenHeight = screen.height;
1448 var screenWidth = screen.width;
1449 if (screenHeight !=
640 && screenHeight !=
480 && screenHeight !=
360)
1450 screenHeight =
360; // we can only assume we're in portrait mode, so we set the screen dims as needed for the following code
1451 if (screenWidth !=
640 && screenWidth !=
480 && screenWidth !=
360)
1452 screenWidth =
640; // we can only assume we're in portrait mode, so we set the screen dims as needed for the following code
1454 // check for screen rotation
1455 if (orientation != 'portrait' && ((screenWidth ==
360 && screenHeight ==
640) || (screenWidth ==
640 && screenHeight ==
480))) {
1456 window.widget.prepareForTransition(
"fade");
1457 orientation = 'portrait';
1458 document.getElementById('body').style.backgroundImage = 'url(' + getBackgroundImage() + ')';
1459 document.getElementById('body').style.backgroundColor = 'none';
1460 window.widget.performTransition();
1461 } else if (orientation != 'landscape' && ((screenWidth ==
640 && screenHeight ==
360) || (screenWidth ==
480 && screenHeight ==
640))) {
1462 window.widget.prepareForTransition(
"fade");
1463 orientation = 'landscape';
1464 document.getElementById('body').style.backgroundImage = 'url(' + getBackgroundImage() + ')';
1465 document.getElementById('body').style.backgroundColor = 'none';
1466 window.widget.performTransition();
1468 else if (document.getElementById('body').style.backgroundImage ==
"")
1470 document.getElementById('body').style.backgroundImage = 'url(' + getBackgroundImage() + ')';
1475 function showHomescreen()
1477 log(
"showHomescreen()");
1479 document.getElementById(
"homescreenView").style.display =
"block";
1480 document.getElementById('body').className =
"background";
1481 document.onclick = null;
1485 function getLocalizedText(p_Txt)
1487 if (localizedText[p_Txt])
1488 return localizedText[p_Txt];
1490 return 'ERROR: missing translation for ' + p_Txt;
1493 function showUpdate()
1497 document.getElementById(
"updateView").style.display =
"block";
1498 document.onclick = null;
1500 window.menu.setLeftSoftkeyLabel(getLocalizedText('update.checknow'), function(){
1503 window.menu.setRightSoftkeyLabel(getLocalizedText('softkey.back'), function()
1509 document.getElementById(
"currentVersion").innerHTML = getLocalizedText(
"update.current") + version;
1513 function checkForUpdate()
1515 // asynch XHR to server url
1516 reqV = new XMLHttpRequest();
1517 reqV.onreadystatechange = checkForUpdateCallback;
1518 document.getElementById(
"updateDiv").innerHTML = getLocalizedText(
"update.checking");
1519 reqV.open(
"GET", versionURL, true);
1520 reqV.setRequestHeader(
"If-Modified-Since",
"Sat, 1 Jan 2000 00:00:00 GMT" ); // disable caching
1524 function checkForUpdateCallback()
1526 if (reqV.readyState ==
4) {
1527 if (reqV.status ==
200) {
1528 var resultXml = reqV.responseText;
1530 var div = document.getElementById(
"tmp");
1531 div.innerHTML = resultXml;
1532 var newVersion = div.getElementsByTagName('version')[
0].innerHTML;
1533 var newVersionURL = div.getElementsByTagName('url')[
0].innerHTML;
1535 if (version != newVersion) {
1536 document.getElementById(
"updateDiv").innerHTML = getLocalizedText(
"update.download").replace(/%
1/, newVersion).replace(/%
2/, newVersionURL);
1539 document.getElementById(
"updateDiv").innerHTML = getLocalizedText(
"update.nonewversion");
1544 document.getElementById(
"updateDiv").innerHTML = getLocalizedText(
"update.error") + reqV.status +
" " + reqV.responseText;
1549 function hideViews()
1551 document.getElementById(
"homescreenView").style.display =
"none";
1552 document.getElementById(
"fullscreenView").style.display =
"none";
1553 document.getElementById(
"aboutView").style.display =
"none";
1554 document.getElementById(
"settingsView").style.display =
"none";
1555 document.getElementById(
"updateView").style.display =
"none";
1558 function listCalendars()
1568 DefaultCalendar: false
1572 var calendarsResult = calendarService.IDataSource.GetList(criteria);
1573 if (calendarsResult.ErrorCode !=
0)
1574 throw(
"Error fetching list of calendars: " + calendarsResult.ErrorCode + ': ' + calendarsResult.ErrorMessage);
1575 var calendarListIterator = calendarsResult.ReturnValue;
1580 while (( item = calendarListIterator.getNext()) != undefined ) {
1581 calendars[count++] = item;
1583 log(
"Available Calendars: " + calendars.join(
", "));
1586 error('listing calendars:' + e + ', line ' + e.line);
1591 // Copies all objects and their properties to an array. Data is copied so nothing gets lost when the reference is removed
1592 // 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
1593 function listToArray(list, calendarName)
1595 var array = new Array();
1598 while (( item = list.getNext()) != undefined ) {
1599 var itemCopy = new Object();
1600 for(var i=
0; i < entryFields.length; i++) {
1601 itemCopy[entryFields[i]] = item[entryFields[i]];
1603 // for some reason, the CalendarName property is never correctly queried, so we assign it manually here
1604 if (!itemCopy['CalendarName']) {
1605 itemCopy['CalendarName'] = calendarName;
1607 array.push(itemCopy);
1608 txt += array[array.length -
1].Summary +
", ";
1610 log(
"listToArray(): " + txt);
1614 function sortCalendarEntries(a, b)
1617 log(
"sortCalendarEntries(" + a.Summary +
"," + b.Summary +
")");
1619 if (a.InstanceStartTime != null) {
1620 atime = a.InstanceStartTime;
1622 else if (a.StartTime != null) {
1623 atime = a.StartTime;
1625 else if (a.InstanceEndTime != null) {
1626 atime = a.InstanceEndTime;
1628 else if (a.EndTime != null) {
1632 if (b.InstanceStartTime != null) {
1633 btime = b.InstanceStartTime;
1635 else if (b.StartTime != null) {
1636 btime = b.StartTime;
1638 else if (b.InstanceEndTime != null) {
1639 btime = b.InstanceEndTime;
1641 else if (b.EndTime != null) {
1645 if (atime && btime) {
1647 atime = parseDate(atime);
1648 btime = parseDate(btime);
1650 // sort by date & time
1651 if (atime < btime) {
1654 else if (atime
> btime) {
1658 else if (a.Type != b.Type) {
1659 if (a.Type < b.Type) {
1662 else if (a.Type
> b.Type) {
1666 // sort by description
1667 else if (a.Summary && b.Summary && a.Summary != b.Summary) {
1668 if (a.Summary < b.Summary) {
1671 else if (a.Summary
> b.Summary) {
1676 // NOTE: events my have no date information at all. In that case, we list events without date first
1677 else if (atime && !btime) {
1680 else if (!atime && btime) {
1683 else if (!atime && !btime) {
1685 if (a.Type != b.Type) {
1686 if (a.Type < b.Type) {
1689 else if (a.Type
> b.Type) {
1693 // sort by description
1694 else if (a.Summary && b.Summary && a.Summary != b.Summary) {
1695 if (a.Summary < b.Summary) {
1698 else if (a.Summary
> b.Summary) {
1707 function updateCalendarColors()
1710 calendarColors = [];
1711 if (calendarList.length
> maxColors) {
1712 log(
"updateCalendarColors(): Warning: more calendars than available indicator colors");
1714 for(var i=
0; i < calendarList.length; i++) {
1715 calendarColors[calendarList[i]] = (i % maxColors) +
1;
1719 function log(message)
1721 if (config['enableLogging'].Value) {
1722 console.info(message);
1728 <style type=
"text/css">
1730 table { margin:
0px; padding:
0px; border-spacing:
0px; border-collapse: collapse; }
1731 td { padding:
0px
5px
0px
0px; white-space:nowrap; overflow:hidden; margin:
0px; }
1732 hr { color:#ffffff; background-color:#ffffff; height:
1px; text-align:left; border-style:none; }
1733 .settingsInfo { display:none; font-style:italic; }
1734 .title { font-weight:bold; font-size:
14pt; }
1735 .textInput { width:
90%; }
1736 .credits { margin-left:
40px; text-indent: -
20px; margin-bottom:
0px; }
1737 #homescreenView { width:
312px; height:
82px; overflow:hidden; }
1738 #calendarList { position:absolute; left:
5px; top:
0px; width:
307px; height:
82px; overflow:hidden; }
1739 #name { text-align:center; }
1740 #appicon { display: block; margin-left: auto; margin-right: auto; margin-top:
10px; }
1741 #smallappicon { width:
22px; height:
22px; margin-right:
10px; float:left; }
1746 <body onload=
"javascript:setTimeout('init()', 10)" onresize=
"javascript:updateScreen()" id=
"body" class=
"background">
1747 <div id=
"homescreenView">
1748 <div id=
"calendarList">loading...
</div>
1750 <div id=
"fullscreenView" style=
"display:none;">
1751 <img src=
"Icon.png" id=
"smallappicon">
1752 <h1 class=
"title">Coming Next
</h1>
1754 <div id=
"fullscreenCalendarList">loading...
</div>
1756 <div id=
"settingsView" style=
"display:none">
1757 <img src=
"Icon.png" id=
"smallappicon">
1758 <h1 id=
"settingsTitle" class=
"title">Settings
</h1>
1760 <div id=
"settingsList"></div>
1762 <div id=
"aboutView" style=
"display:none">
1763 <img src=
"Icon.png" id=
"appicon">
1764 <h1 id=
"name">Coming Next
</h1>
1766 <p>Created by Dr. Cochambre and Michael Prager.
</p>
1767 <p>Contributions:
</p>
1768 <p class=
"credits">Paul Moore (bug fixes, new features and code cleanup)
</p>
1769 <p class=
"credits">Manfred Hanselmann (DST support)
</p>
1770 <p class=
"credits">Christophe Milsent (translation support & french translation)
</p>
1771 <p class=
"credits">Flavio Nathan (portuguese-brazilian translation)
</p>
1772 <p class=
"credits">Tokeda (russian translation)
</p>
1773 <p class=
"credits">Marcella Ferrari (italian translation)
</p>
1774 <p class=
"credits">Venos (italian translation)
</p>
1775 <p>This software is open source and licensed under the GPLv3.
</p>
1776 <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>
1779 <div id=
"updateView" style=
"display:none">
1780 <img src=
"Icon.png" id=
"smallappicon">
1781 <h1 class=
"title">Check for update
</h1>
1783 <div id=
"currentVersion">Coming Next ??
</div>
1784 <div id=
"updateDiv"></div>
1785 <div id=
"tmp" style=
"display:none;"></div>