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 function stringEndsWith(str, suffix)
380 return str.indexOf(suffix, str.length - suffix.length) !== -
1;
383 // detects the system's current time format by parsing a native calendar timestamp (this is the only reliable formating across all devices and firmwares)
384 function detectTimeFormat(localeTimeString)
386 localeTimeString = localeTimeString.toLowerCase();
387 use12hoursTimeFormat = stringEndsWith(localeTimeString,
"am") || stringEndsWith(localeTimeString,
"pm");
388 timeFormatSeparator = localeTimeString.indexOf(
":") != -
1 ?
":" :
".";
391 function requestNotification()
393 var criteria = new Object();
394 criteria.Type =
"CalendarEntry";
395 criteria.Filter = new Object();
396 for(var i=
0; i < calendarList.length; i++) {
397 criteria.Filter.CalendarName = calendarList[i];
399 var notificationRequest = calendarService.IDataSource.RequestNotification(criteria, callback);
400 if (notificationRequest.ErrorCode)
401 error('requestNotification failed with error code ' + notificationRequest.ErrorCode);
402 notificationRequests.push(notificationRequest);
404 error(
"requestNotification: " + e + ', line ' + e.line);
408 var criteria2 = new Object();
409 criteria2.Type =
"CalendarEntry";
410 criteria2.Filter = new Object();
411 criteria2.Filter.LocalIdList = new Array();
412 criteria2.Filter.LocalIdList[
0] = settingsCalEntryId;
414 var notificationRequest = calendarService.IDataSource.RequestNotification(criteria2, settingsCallback);
415 if (notificationRequest.ErrorCode)
416 error('requestNotification failed with error code ' + notificationRequest.ErrorCode);
417 notificationRequests.push(notificationRequest);
419 error(
"requestNotification: " + e + ', line ' + e.line);
423 function cancelNotification()
425 for(var i=
0; i < notificationRequests.length; i++) {
427 var result = calendarService.IDataSource.Cancel(notificationRequests[i]);
428 if (result.ErrorCode)
429 error('cancelNotification failed with error code ' + result.ErrorCode);
431 error(
"cancelNotification: " + e + ', line ' + e.line);
436 function callback(transId, eventCode, result)
438 log(
"callback(): panelNum: " + panelNum +
" transId: " + transId +
" eventCode: " + eventCode +
" result.ErrorCode: " + result.ErrorCode);
439 lastReloadTime = null; // force calendar data reload on next update
443 function settingsCallback(transId, eventCode, result)
445 log(
"settingsCallback(): panelNum: " + panelNum +
" transId: " + transId +
" eventCode: " + eventCode +
" result.ErrorCode: " + result.ErrorCode);
449 function parseDate(dateString)
452 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:
453 Wednesday,
26 August,
2009 24:
00:
00
454 Wednesday,
26 August,
2009 12:
00:
00 am
455 Wednesday, August
26,
2009 12:
00:
00 am
456 Wednesday,
2009 August,
26 12:
00:
00 am
457 Wednesday,
2009 August,
28 8.00.00 pm
458 Wednesday,
2009 August,
28 08:
00:
00 PM
462 if (dateString ==
"" || dateString == null || dateString == undefined)
464 if (dateString instanceof Date) {
465 // we already have a date object, no need to parse string here
469 var dateArr = (dateString + '').replace(/,/g, '').replace(/\./g, ':').replace(/ /g, ' ').split(' ');
470 if (dateArr.length !=
5 && dateArr.length !=
6)
474 var weekDay = dateArr[
0];
475 var day = dateArr[
1];
476 var month = dateArr[
2];
477 var year = dateArr[
3];
478 // make sure month is set properly
479 if (isNaN(parseInt(day))) {
485 if (isNaN(parseInt(year))) {
490 // make sure day and year are set properly
491 if (Number(day)
> Number(year)) {
496 month = months_translated[month];
499 var timeArr = dateArr[
4].split(':');
500 if (timeArr.length !=
3)
502 var hours = Number(timeArr[
0]);
503 var minutes = Number(timeArr[
1]);
504 var seconds = Number(timeArr[
2]);
505 if (dateArr.length ==
6 && dateArr[
5].toLowerCase() == 'pm' && hours <
12)
507 if (dateArr.length ==
6 && dateArr[
5].toLowerCase() == 'am' && hours ==
12)
510 result = new Date(year, month -
1, day, hours, minutes, seconds);
513 // take care of daylight saving time
514 if (config['enableDaylightSaving'].Value) {
516 // determine if date is in summer or winter time
517 var dateSummerTime = isSummertime(result);
519 // work around bug in Nokias calendar api resulting in dates within a different DST to be off by
1 hour
520 if (summertime && !dateSummerTime) {
521 result = new Date(result.getTime() -
1000 *
60 *
60 * config['daylightSavingOffset'].Value); // -
1 hour
522 log('parseDate(): fixing time -
1h: ' + result);
524 else if (!summertime && dateSummerTime) {
525 result = new Date(result.getTime() +
1000 *
60 *
60 * config['daylightSavingOffset'].Value); // +
1 hour
526 log('parseDate(): fixing time +
1h: ' + result);
533 function getWeekdayLocalized(date) {
534 var localizedString = date.toLocaleDateString();
535 if (localizedString.indexOf(
",") == -
1) {
536 return weekdays_translated[date.getDay()];
538 return localizedString.split(',')[
0];
541 // 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"
542 function formatDate(date, format)
544 var day = date.getDate().toString();
545 var month = (date.getMonth() +
1).toString();
546 while (day.length <
2) { day = '
0' + day; }
547 while (month.length <
2) { month = '
0' + month; }
549 if (config['showTodayAsText'].Value && isToday(date))
550 return '
<span class=
"today">' + config['todayText'].Value + '
</span>';
551 if (config['showTodayAsText'].Value && isTomorrow(date))
552 return '
<span class=
"tomorrow">' + config['tomorrowText'].Value + '
</span>';
554 if (format instanceof Date) {
555 // we don't know how to format this
556 if (config['dateFormat'].Value == 'auto' || config['dateFormat'].Value == 'DDMM')
557 return day + config['dateSeparator'].Value + month;
559 return month + config['dateSeparator'].Value + day;
561 var dateArr = format.replace(/,/g,'').replace(/\./g,':').replace(/ /g,' ').split(' ');
562 if (dateArr.length !=
5 && dateArr.length !=
6) {
563 // we don't know how to format this
564 if (config['dateFormat'].Value == 'auto' || config['dateFormat'].Value == 'DDMM')
565 return day + config['dateSeparator'].Value + month;
567 return month + config['dateSeparator'].Value + day;
571 if (config['dateFormat'].Value == 'MMDD')
573 else if (config['dateFormat'].Value == 'DDMM')
576 // config['dateFormat'].Value == 'auto', try to detect system setting
578 var day_ = dateArr[
1];
579 var month_ = dateArr[
2];
580 var year_ = dateArr[
3];
581 // make sure month is set properly
582 if (isNaN(parseInt(day_))) {
587 } else if (isNaN(parseInt(year_))) {
593 // make sure day and year are set properly
594 if (Number(day_)
> Number(year_))
599 return day + config['dateSeparator'].Value + month;
601 return month + config['dateSeparator'].Value + day;
604 function formatTime(date)
606 // date is a Date() object
607 var hour = date.getHours();
608 var minute = date.getMinutes();
610 // don't use Date().toLocaleTimeString() as it is utterly broken on newer firmwares
611 if (use12hoursTimeFormat) {
622 minute =
"0" + minute;
623 time = hour + timeFormatSeparator + minute +
" " + ap;
629 minute =
"0" + minute;
630 time = hour + timeFormatSeparator + minute;
633 if (config['showNowAsText'].Value && date.getTime() == now.getTime())
634 time = '
<span class=
"now">' + config['nowText'].Value + '
</span>';
635 log(
"formatTime(): " + time +
", use12hoursTimeFormat=" + use12hoursTimeFormat +
", timeFormatSeparator=" + timeFormatSeparator +
", date.toLocateTimeString(): " + date.toLocaleTimeString());
639 function updateData()
646 // check if we got additional or less calendars since our last update
647 var newCalendarList = listCalendars();
648 if (newCalendarList == null) {
649 // Something went wrong fetching the calendars list.
650 // This usually happens when a backup is being made.
651 // Retry the next time updateData() is called by
652 // resetting errorOccured
653 log('updateData(): listCalendars() failed, trying again later...');
654 cacheEntriesHtml = ''; // make sure we replace the currently shown error message on the next update
655 errorOccured = false;
658 if (newCalendarList.length != calendarList.length) {
659 calendarList = newCalendarList;
660 updateCalendarColors();
661 cancelNotification();
662 requestNotification();
663 lastReloadTime = null; // force calendar data reload on this update
668 // only reload calendar data every
6 hours, visual updates occure more often
669 if (!lastReloadTime || now.getTime() - lastReloadTime.getTime()
> reloadInterval) {
670 log('updateData(): reloading calendar data');
672 // meetings have time
673 // 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
674 summertime = isSummertime(now); // cache summer time info for today
675 var meetingList = [];
676 for(var i=
0; i < calendarList.length; i++) {
677 // ignore excluded calendars
678 if (config['excludedCalendars'].Value.indexOf(calendarList[i]) != -
1)
680 var meetingListFiltering = {
681 Type:'CalendarEntry',
683 CalendarName: calendarList[i],
684 StartRange: (new Date(now.getFullYear(), now.getMonth(), now.getDate(),
0,
0,
0)),
685 EndRange: (new Date(now.getFullYear(), now.getMonth() + config['monthRange'].Value, now.getDate(),
0,
0,
0))
688 var meetingResult = calendarService.IDataSource.GetList(meetingListFiltering);
689 if (meetingResult.ErrorCode !=
0)
690 throw(
"Error fetching calendar data: " + meetingResult.ErrorCode + ': ' + meetingResult.ErrorMessage);
691 var list = meetingResult.ReturnValue;
692 meetingList = meetingList.concat(listToArray(list, calendarList[i]));
694 log(
"updateData(): meetingList.sort()");
695 meetingList.sort(sortCalendarEntries);
697 // todos don't, they start on
00:
00 hrs., but should be visible anyway
698 // this will generate a list of passed todos. We have to check if they have been marked as
"done" yet
699 if (config['includeTodos'].Value) {
700 var todayTodoList = [];
701 for(var i=
0; i < calendarList.length; i++) {
702 // ignore excluded calendars
703 if (config['excludedCalendars'].Value.indexOf(calendarList[i]) != -
1)
705 var todayTodoListFiltering = {
706 Type:'CalendarEntry',
708 CalendarName: calendarList[i],
710 StartRange: (new Date(now.getFullYear() -
1, now.getMonth(), now.getDate(),
0,
0,
0)),
711 EndRange: (new Date(now.getFullYear(), now.getMonth(), now.getDate(),
0,
0,
1))
714 var todayTodoResult = calendarService.IDataSource.GetList(todayTodoListFiltering);
715 var list = todayTodoResult.ReturnValue;
716 todayTodoList = todayTodoList.concat(listToArray(list, calendarList[i]));
718 log(
"updateData(): todayTodoList.sort()");
719 todayTodoList.sort(sortCalendarEntries);
720 entryLists = [todayTodoList, meetingList];
722 entryLists = [meetingList];
724 lastReloadTime = new Date();
726 error('loading Calendar items list:' + e + ', line ' + e.line);
736 var fontsize = 'normal';
737 var lineheight = 'normal';
739 fontsize = parseInt(
72 / config['eventsPerWidget'].Value) + 'px';
740 lineheight = parseInt(
82 / config['eventsPerWidget'].Value) + 'px';
742 if (config['eventsPerWidget'].Value ==
3) {
743 changeCssClass('.icon', 'width:
20px; height:
20px');
745 else if (config['eventsPerWidget'].Value ==
5) {
746 changeCssClass('.icon', 'width:
10px; height:
10px');
748 else if (config['eventsPerWidget'].Value ==
6) {
749 changeCssClass('.icon', 'width:
8px; height:
8px');
753 changeCssClass('.icon', config['cssStyle_icon'].Value);
754 var entriesHtml = '
<table style=
"font-size:' + fontsize + '; line-height:' + lineheight + ';">';
756 entriesHtml = '
<table width=
"307" height=
"82"><tr><td>' + entriesHtml; // this is needed to center the actual content vertically
760 max = (panelNum +
1) * config['eventsPerWidget'].Value;
762 max =
30; // we can display a lot more events in fullscreen mode
764 if (config['enableLogging'].Value) {
766 for (var i=
0; i < entryLists.length; i++) {
767 listinfo = listinfo +
" " + entryLists[i].length;
768 var entrieslist =
"";
769 for (var j=
0; j < entryLists[i].length; j++) {
770 entrieslist += entryLists[i][j].Summary +
", ";
772 log(
"updateData(): entrieslist: " + entrieslist);
774 log(
"updateData(): inner loop, " + entryLists.length +
" lists, [" + listinfo +
"] entries");
777 // the first outer loop iteration is for passed ToDos, the second loop is for all upcomming events (may also include ToDos)
778 for (var i=
0; counter < max && i < entryLists.length; i++) {
779 for (var j=
0; (counter < max) && (j < entryLists[i].length); j++) {
780 entry = entryLists[i][j];
783 // output event info for debugging
784 var entryInfo =
"event: ";
785 for(var k=
0; k < entryFields.length; ++k) {
786 if (entry[entryFields[k]] != undefined) {
787 entryInfo += entryFields[k] +
"=" + entry[entryFields[k]] +
",";
792 // we don't want ToDos when includeTodos == false or when they are completed
793 if (entry.Type == 'ToDo' && (entry.Status ==
"TodoCompleted" || !config['includeTodos'].Value)) {
794 log('skipping ' + entry.id );
799 // make sure that we don't include an event twice (useful for ToDos that might come up twice)
800 if (eventIds[entry.id] ==
1 && entry.Type == 'ToDo') {
801 log('skipped (already included) ' + entry.id);
805 eventIds[entry.id] =
1;
807 // summary can be undefined!
808 var Summary = ((entry.Summary == null) ? '' : entry.Summary);
809 if (entry.Location != '' && entry.Location != undefined && config['showLocation'].Value)
810 Summary += ', ' + entry.Location;
812 // fix by yves: determine start and end dates/times
813 entryStartTime = ((entry.InstanceStartTime == null) ? entry.StartTime : entry.InstanceStartTime);
814 entryEndTime = ((entry.InstanceEndTime == null) ? entry.EndTime : entry.InstanceEndTime);
816 // there can be ToDos that have no date at all!
817 if (entry.Type == 'ToDo' && entry.EndTime == null)
818 entryDate =
""; // this will cause parseDate(entryDate) to return null;
820 entryDate = ((entry.Type == 'ToDo') ? entryEndTime : entryStartTime); // ToDo's use their EndTime, the rest use StartTime
822 // Convert date/time string to Date object
823 var date = parseDate(entryDate);
824 log('date: ' + date);
825 var endDate = ((entryEndTime == null) ? null : parseDate(entryEndTime));
826 log('endDate: ' + endDate);
828 // check if Meeting is actually a DayEvent. Bug introduced by
"Anna" updates to various Symbian^
3 devices.
829 // Note that this workaround is not
100% save! It might missinterpret some meetings as dayevents of starting and ending on
00:
00
830 if (entry.Type == 'Meeting' && date.getHours() ==
0 && date.getMinutes() ==
0 &&
831 endDate != null && endDate.getHours() ==
0 && endDate.getMinutes() ==
0) {
832 log('fixing event type: changed from
"Meeting" to
"DayEvent".');
833 entry.Type = 'DayEvent';
836 // check if meeting event has already passed
837 if (entry.Type == 'Meeting') {
838 var compareTime = ((endDate == null) ? date.getTime() : endDate.getTime());
839 if (now.getTime()
> compareTime) {
840 log('skipping Meeting (already passed) ' + entry.id);
842 eventIds[entry.id] =
0;
847 // check if anniversary passed (not sure why they are in the list, the query was only for today - nokia?)
848 if (entry.Type == 'Anniversary') {
849 var tmp = new Date(now.getFullYear(), now.getMonth(), now.getDate(),
0,
0,
0);
850 if (date.getTime() < tmp.getTime()) {
851 log('skipping Anniversary (already passed) ' + entry.id);
853 eventIds[entry.id] =
0;
858 // fix DayEvents end time. End times are off by
1 Second. It's possible that the event has already passed
859 if (entry.Type == 'DayEvent' && endDate != null) {
860 endDate.setMinutes(endDate.getMinutes() -
1);
861 log('fixing DayEvent endDate: ' + endDate);
862 if (now.getTime()
> endDate.getTime()) {
863 log('event already passed ' + entry.id);
865 eventIds[entry.id] =
0;
870 // check if the event is currently taking place
871 if (entryStartTime != null && entryEndTime != null && date != null && endDate != null) {
872 // check if we are between start and endtime
873 if ((date.getTime() < now.getTime()) && (now.getTime() < endDate.getTime())) {
874 date = now; // change appointment date/time to now
875 log('event is currently taking place: ' + date);
879 // skip events for the first panel in case this is the second one and we're not in fullscreen mode
880 if (mode ==
0 && panelNum
> 0 && counter < panelNum * config['eventsPerWidget'].Value +
1) {
881 log('skipping (already in first widget) ' + entry.id);
885 // mark overdue todos
887 if (entry.Type == 'ToDo' && date != null) {
888 var tmp1 = new Date(date.getFullYear(), date.getMonth(), date.getDate(),
0,
0,
0);
889 var tmp2 = new Date(now.getFullYear(), now.getMonth(), now.getDate(),
0,
0,
0);
890 if (tmp1.getTime() < tmp2.getTime()) {
895 // generate html output
896 entriesHtml += '
<tr>';
897 if (config['showCalendarIndicator'].Value && calendarList.length - config['excludedCalendars'].Value.length
> 1) {
898 entriesHtml += '
<td><span class=
"calendar' + calendarColors[entry.CalendarName] + '"> </span></td>';
900 entriesHtml += '
<td><img class=
"icon" src=
"' + entry.Type + '.png" /></td>';
902 // some languages have very strange locale date formats, can't parse all those. Also some todos don't have dates at all.
903 entriesHtml += '
<td colspan=
"4"><span class=
"date">' + entryDate + '
</span> ';
905 var weekDay = getWeekdayLocalized(date).substr(
0,config['weekDayLength'].Value);
906 log('date.toLocaleDateString(): ' + date.toLocaleDateString());
907 log('weekDay: ' + weekDay);
908 var time = formatTime(date);
909 var dateStr = formatDate(date, entryDate);
910 if (entry.Type == 'ToDo' && overdue && config['markOverdueTodos'].Value) {
911 dateStr = '
<span class=
"overdue">' + config['overdueText'].Value + '
</span>';
912 entriesHtml += '
<td colspan=
"4" width=
"1px"><span class=
"date">' + dateStr + '
</span> ';
913 } else if (entry.Type == 'ToDo' || entry.Type == 'Anniversary' || entry.Type == 'DayEvent' || entry.Type == 'Reminder') {
914 if ((isToday(date) || isTomorrow(date)) && config['showTodayAsText'].Value) // show weekday if the date string is not text. looks odd otherwise
915 entriesHtml += '
<td colspan=
"4" width=
"1px"><span class=
"date">' + dateStr + '
</span> ';
917 entriesHtml += '
<td class=
"weekDay" width=
"1px">' + weekDay + '
</td><td width=
"1px" class=
"date">' + dateStr + '
</td><td colspan=
"2">';
918 } else if (entry.Type == 'Meeting') {
919 if (config['showCombinedDateTime'].Value) {
921 entriesHtml += '
<td width=
"1px" colspan=
"4"><span class=
"today">' + time + '
</span> ';
922 else if (isTomorrow(date))
923 entriesHtml += '
<td width=
"1px" colspan=
"4"><span class=
"tomorrow">' + dateStr + '
</span> <span class=
"time">' + time + '
</span> ';
925 entriesHtml += '
<td width=
"1px" class=
"weekDay">' + weekDay + '
</td><td width=
"1px" class=
"date">' + dateStr + '
</td><td colspan=
"2">';
927 if ((isToday(date) || isTomorrow(date)) && config['showTodayAsText'].Value)
928 entriesHtml += '
<td colspan=
"4" width=
"1px"><span class=
"today">' + dateStr + '
</span> <span class=
"time">' + time + '
</span> ';
930 entriesHtml += '
<td width=
"1px" class=
"weekDay">' + weekDay + '
</td><td width=
"1px" class=
"date">' + dateStr + '
</td><td width=
"1px" class=
"time">' + time + '
</td><td>';
934 entriesHtml += '
<span class=
"description">' + Summary + '
</span></td></tr>';
937 entriesHtml += '
</table>';
939 entriesHtml = entriesHtml + '
</td></tr></table>';
940 if (config['showNothingText'].Value && entriesHtml == '
<table></table>') {
941 var text = config['nothingText'].Value.replace(/%d/, config['monthRange'].Value);
942 entriesHtml = '
<div style=
"width:295px; height:75px; text-align:center; line-height:75px; overflow:visible;">' + text + '
</div>';
944 log(
"output: " + entriesHtml);
945 if (cacheEntriesHtml != entriesHtml) {
947 document.getElementById('calendarList').innerHTML = entriesHtml;
949 document.getElementById('fullscreenCalendarList').innerHTML = entriesHtml;
950 cacheEntriesHtml = entriesHtml;
953 lastUpdateTime = new Date();
955 error('displaying list:' + e + ', line ' + e.line);
960 // called by handleOnShow() and onResize events
961 function updateScreen()
963 log('updateScreen()');
965 // check if opening fullscreen
966 if( window.innerHeight
> 91 && mode ==
0) {
968 cacheEntriesHtml = '';
969 document.getElementById('body').style.backgroundImage =
"";
972 else if (window.innerHeight <=
91 && mode !=
0) {
974 cacheEntriesHtml = '';
979 updateHomescreen(); // check for screen rotation
984 function handleOnShow()
988 var time = new Date();
989 if (time.getTime() - lastUpdateTime.getTime()
> config['updateDataInterval'].Value *
60 *
1000) {
990 log('updateScreen(): force updateData() because last update was too long ago (' + (time.getTime() - lastUpdateTime.getTime()) /
1000 + 's)');
993 setUpdateTimer(); // reinitialize update timer
997 function launchCalendar()
1000 widget.openApplication(config['calendarApp'].Value,
"");
1001 if (config['hideWidgetOnCalendarOpen'].Value)
1004 error('starting Calendar App');
1011 log('New widget instance starting up...');
1014 // call calendar service
1015 if (device !=
"undefined")
1016 calendarService = device.getServiceObject(
"Service.Calendar",
"IDataSource");
1018 throw('device object does not exist');
1020 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>');
1024 calendarList = listCalendars();
1026 updateCalendarColors();
1029 requestNotification();
1030 document.getElementById(
"settingsTitle").innerHTML = getLocalizedText('menu.settings');
1032 if (window.innerHeight
> 91) {
1033 mode =
0; // we're starting fullscreen, we set mode to homescreen in order to let updateScreen() do all the work for us
1038 log(
"init(): updateScreen()");
1040 if (config['useBackgroundImage'].Value)
1041 // check for screen rotation every
1 secs
1042 screenRotationTimer = window.setInterval('checkOrientation()',
1000 *
1);
1044 // call updateScreen() when widget changes from background to forground
1045 window.widget.onshow = handleOnShow;
1047 log(
"init(): finished...");
1049 statupSuccessful = true;
1052 function checkOrientation()
1056 updateHomescreen(); // check for screen rotation
1059 function setUpdateTimer()
1061 updateTimer = window.setInterval('updateTimerCallback()',
1000 *
60 * config['updateDataInterval'].Value);
1064 function clearUpdateTimer()
1066 window.clearInterval(updateTimer);
1069 function updateTimerCallback()
1071 log(
"updateTimerCallback()");
1075 function createMenu()
1077 window.menu.setLeftSoftkeyLabel(
"",null);
1078 window.menu.setRightSoftkeyLabel(
"",null);
1080 var menuSettings = new MenuItem(getLocalizedText('menu.settings'), id++);
1081 var menuCallApp = new MenuItem(getLocalizedText('menu.openCalendarApp'), id++);
1082 var menuHelp = new MenuItem(getLocalizedText('menu.help'), id++);
1083 var menuUpdate = new MenuItem(getLocalizedText('menu.update'), id++);
1084 var menuAbout = new MenuItem(getLocalizedText('menu.about'), id++);
1085 menuSettings.onSelect = showSettings;
1086 menuAbout.onSelect = showAbout;
1087 menuCallApp.onSelect = launchCalendar;
1088 menuUpdate.onSelect = showUpdate;
1089 menuHelp.onSelect = showHelp;
1090 window.menu.clear();
1091 window.menu.append(menuCallApp);
1092 window.menu.append(menuSettings);
1093 window.menu.append(menuHelp);
1094 window.menu.append(menuUpdate);
1095 window.menu.append(menuAbout);
1098 function showSettings()
1102 document.getElementById(
"settingsView").style.display =
"block";
1103 document.onclick = null;
1105 window.menu.setLeftSoftkeyLabel(getLocalizedText('settings.save'), function()
1107 for (var key in config) {
1108 if (config[key].Type == 'String')
1109 config[key].Value = document.forms[
0].elements[
"settings." + key].value;
1110 else if (config[key].Type == 'Int') {
1111 config[key].Value = parseInt(document.forms[
0].elements[
"settings." + key].value);
1112 if (config[key].Value <
0 || isNaN(config[key].Value))
1113 config[key].Value = config[key].Default;
1115 else if (config[key].Type == 'Bool')
1116 config[key].Value = document.forms[
0].elements[
"settings." + key].checked;
1117 else if (config[key].Type == 'UID') {
1118 config[key].Value = parseInt(document.forms[
0].elements[
"settings." + key].value);
1119 if (isNaN(config[key].Value))
1120 config[key].Value = config[key].Default;
1122 else if (config[key].Type == 'Enum') {
1123 config[key].Value = document.forms[
0].elements[
"settings." + key].value;
1124 if (config[key].ValidValues.indexOf(config[key].Value) == -
1)
1125 config[key].Value = config[key].Default;
1127 else if (config[key].Type == 'Array') {
1128 if (key == 'excludedCalendars') {
1129 config[key].Value = new Array();
1130 for(var i=
0; i < calendarList.length; i++) {
1131 var element = document.forms[
0].elements[
"settings." + key +
"." + calendarList[i]];
1132 if (element != null && element.checked == false)
1133 config[key].Value.push(calendarList[i]);
1146 window.menu.setRightSoftkeyLabel(getLocalizedText('settings.cancel'), function()
1152 var settingsHtml = '
<form>';
1153 for (var key in config) {
1154 if (config[key].Type == 'String') {
1156 if (key.substring(
0,
9) ==
"cssStyle_")
1157 prefix = getLocalizedText('settings.cssStyle_prefix');
1158 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 />';
1160 else if (config[key].Type == 'Int')
1161 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 />';
1162 else if (config[key].Type == 'Bool')
1163 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 />';
1164 else if (config[key].Type == 'UID')
1165 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 />';
1166 else if (config[key].Type == 'Enum') {
1167 settingsHtml += '
<table><tr><td>' + getLocalizedText('settings.name.' + key) + '
<br /><select name=
"settings.' + key + '" size=
"1">';
1168 for(var i =
0; i < config[key].ValidValues.length; i++)
1169 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>';
1170 settingsHtml += '
</select></div></td>' + printHintBox(getLocalizedText('settings.info.' + key)) + '
<hr />';
1172 else if (config[key].Type == 'Array') {
1173 settingsHtml += '
<table><tr><td>' + getLocalizedText('settings.name.' + key) + '
<br />';
1174 if (key == 'excludedCalendars') {
1175 for(var i=
0; i < calendarList.length; i++) {
1176 var checked = '
checked=
"checked"';
1177 if (config[key].Value.indexOf(calendarList[i]) != -
1)
1179 settingsHtml += '
<input name=
"settings.' + key + '.' + calendarList[i] + '" type=
"checkbox" value=
"' + calendarList[i] + '" ' + checked + '
/> ' + calendarList[i] + '
<br />';
1182 settingsHtml += '
</td>' + printHintBox(getLocalizedText('settings.info.' + key)) + '
<hr />';
1185 settingsHtml += '
<input name=
"reset" type=
"button" value=
"' + getLocalizedText('settings.restoreDefaults') + '" onclick=
"javascript:restoreDefaultSettings();showSettings();" />';
1186 settingsHtml += '
</form>';
1187 document.getElementById(
"settingsList").innerHTML = settingsHtml;
1190 function changeCssClass(classname, properties)
1192 for(var i =
0; i < document.styleSheets[
0]['cssRules'].length; i++)
1194 if (document.styleSheets[
0]['cssRules'][i].selectorText == classname) {
1195 document.styleSheets[
0].deleteRule(i);
1196 document.styleSheets[
0].insertRule(classname + ' { ' + properties + ' }', document.styleSheets[
0]['cssRules'].length);
1202 function updateCssClasses()
1204 for(var key in config) {
1205 changeCssClass(getLocalizedText('settings.name.' + key), config[key].Value);
1209 function getSettingsCalEntryId()
1211 if (settingsCalEntryId == null) {
1212 // check if entry already exists
1213 var listFiltering = {
1214 Type:'CalendarEntry',
1216 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!
1217 EndRange: new Date(
2000,
0,
2),
1218 SearchText: 'ComingNext Settings|',
1224 result = calendarService.IDataSource.GetList(listFiltering);
1225 if (result.ErrorCode)
1226 throw(result.ErrorMessage);
1229 error(
"getSettingsCalEntryId: GetList() failed: " + e + ', line ' + e.line);
1232 var list = result.ReturnValue;
1233 var entry = list.getNext();
1234 if (entry != undefined) {
1235 settingsCalEntryId = entry.LocalId;
1236 log(
"settingsCalEntryId=" + settingsCalEntryId);
1238 else { // create settings item
1239 var item = new Object();
1240 item.Type =
"DayEvent";
1241 item.StartTime = new Date(
2000,
0,
1);
1242 item.Summary =
"ComingNext Settings|";
1244 var criteria = new Object();
1245 criteria.Type =
"CalendarEntry";
1246 criteria.Item = item;
1249 var result = calendarService.IDataSource.Add(criteria);
1250 if (result.ErrorCode)
1251 throw(result.ErrorMessage);
1253 error(
"getSettingsCalEntryId: " + e + ', line ' + e.line);
1256 getSettingsCalEntryId();
1261 function restoreDefaultSettings()
1263 for (var key in config)
1264 config[key].Value = config[key].Default;
1267 function loadSettings()
1269 getSettingsCalEntryId();
1270 var listFiltering = {
1271 Type:'CalendarEntry',
1273 LocalId: settingsCalEntryId
1278 result = calendarService.IDataSource.GetList(listFiltering);
1279 if (result.ErrorCode)
1280 throw(result.ErrorMessage);
1283 error(
"loadSettings: GetList() failed: " + e + ', line ' + e.line);
1286 var entry = result.ReturnValue.getNext();
1287 if (entry != undefined) {
1288 log(
"Loading Settings...");
1289 // only reload settings if they chanced since the last reload
1290 if (settingsCache != entry.Summary)
1292 restoreDefaultSettings();
1293 var stringlist = entry.Summary.split(
"|");
1294 // skip the first two entries, those contain header and version info
1295 for(var i =
2; i < stringlist.length -
1; i++) {
1296 var pair = stringlist[i].split('=');
1298 var value = pair[
1];
1299 if (key == null || value == null || config[key] == null) {
1300 log('Warning: unknown or invalid setting: ' + stringlist[i]);
1303 log('stringlist[' + i + ']: ' + key + '=\'' + value + '\'');
1304 if (config[key].Type == 'Int') {
1305 config[key].Value = Number(value);
1306 if (isNaN(config[key].Value))
1307 config[key].Value = config[key].Default;
1309 else if (config[key].Type == 'String')
1310 config[key].Value = value;
1311 else if (config[key].Type == 'Bool')
1312 config[key].Value = (value == 'true')
1313 else if (config[key].Type == 'Enum')
1314 config[key].Value = value;
1315 else if (config[key].Type == 'UID') {
1316 config[key].Value = Number(value);
1317 if (isNaN(config[key].Value))
1318 config[key].Value = config[key].Default;
1320 else if (config[key].Type == 'Array') {
1321 config[key].Value = value.split(
"^");
1322 if (config[key].Value.length ==
1 && config[key].Value[
0] ==
"") {
1323 config[key].Value = [];
1327 settingsCache = entry.Summary;
1331 log(
"Settings already cached and did not change");
1335 error(
"Failed to load settings, calendar entry could not be found");
1339 function saveSettings()
1341 getSettingsCalEntryId();
1342 var item = new Object();
1343 item.Type =
"DayEvent";
1344 item.StartTime = new Date(
2000,
0,
1);
1345 item.LocalId = settingsCalEntryId;
1346 item.Summary =
"ComingNext Settings|" + version +
"|";
1348 for (var key in config) {
1349 if (config[key].Type == 'Int')
1350 item.Summary += key +
"=" + config[key].Value.toString() +
"|";
1351 else if (config[key].Type == 'String')
1352 item.Summary += key +
"=" + config[key].Value +
"|";
1353 else if (config[key].Type == 'Bool')
1354 item.Summary += key +
"=" + (config[key].Value ? 'true' : 'false') +
"|";
1355 else if (config[key].Type == 'Enum')
1356 item.Summary += key +
"=" + config[key].Value +
"|";
1357 else if (config[key].Type == 'UID')
1358 item.Summary += key +
"=" + config[key].Value.toString() +
"|";
1359 else if (config[key].Type == 'Array')
1360 item.Summary += key +
"=" + config[key].Value.join(
"^") +
"|";
1362 settingsCache = item.Summary;
1364 var criteria = new Object();
1365 criteria.Type =
"CalendarEntry";
1366 criteria.Item = item;
1368 log(
"Saving settings to calendar entry: " + item.Summary);
1370 var result = calendarService.IDataSource.Add(criteria);
1371 if (result.ErrorCode)
1372 throw(result.ErrorMessage);
1374 error(
"saveSettings: " + e + ', line ' + e.line);
1377 lastReloadTime = null; // force calendar data reload on next update
1382 function toggleVisibility(elementId)
1384 if (document.getElementById(elementId).style.display ==
"none")
1385 document.getElementById(elementId).style.display =
"block";
1387 document.getElementById(elementId).style.display =
"none";
1391 function printHintBox(text)
1394 return '
<td width=
"1%" align=
"right" onclick=
"javascript:toggleVisibility(\'info' + uniqueId + '\')">' + getLocalizedText('settings.help') + '
</td></tr></table>'+
1395 '
<div class=
"settingsInfo" id=
"info' + uniqueId + '">' + text + '
</div>';
1398 function showAbout()
1402 document.getElementById(
"aboutView").style.display =
"block";
1403 document.onclick = null;
1405 window.menu.setLeftSoftkeyLabel(
" ", function(){});
1406 window.menu.setRightSoftkeyLabel(getLocalizedText('softkey.back'), function()
1412 //document.getElementById(
"aboutView").innerHTML = 'aboutView';
1413 document.getElementById(
"name").innerHTML =
"Coming Next " + version;
1416 function showHelp() {
1417 widget.openURL('http://comingnext.sf.net/help');
1420 function updateFullscreen()
1424 function showFullscreen()
1426 log(
"showFullscreen()");
1428 document.getElementById(
"fullscreenView").style.display =
"block";
1429 document.getElementById('body').className =
"backgroundFullscreen";
1431 document.onclick = launchCalendar;
1436 function getBackgroundImage()
1441 if (config['backgroundImageLocation'].Value == config['backgroundImageLocation'].ValidValues[
0]) // internal
1442 bgImage = 'background_' + orientation + '.png';
1444 bgImage = 'C:/Data/background_' + panelNum + '_' + orientation + '.png';
1448 function updateHomescreen()
1450 if (config['useBackgroundImage'].Value) {
1451 // check if we have a completely unknown screen resolution
1452 var screenHeight = screen.height;
1453 var screenWidth = screen.width;
1454 if (screenHeight !=
640 && screenHeight !=
480 && screenHeight !=
360)
1455 screenHeight =
360; // we can only assume we're in portrait mode, so we set the screen dims as needed for the following code
1456 if (screenWidth !=
640 && screenWidth !=
480 && screenWidth !=
360)
1457 screenWidth =
640; // we can only assume we're in portrait mode, so we set the screen dims as needed for the following code
1459 // check for screen rotation
1460 if (orientation != 'portrait' && ((screenWidth ==
360 && screenHeight ==
640) || (screenWidth ==
640 && screenHeight ==
480))) {
1461 window.widget.prepareForTransition(
"fade");
1462 orientation = 'portrait';
1463 document.getElementById('body').style.backgroundImage = 'url(' + getBackgroundImage() + ')';
1464 document.getElementById('body').style.backgroundColor = 'none';
1465 window.widget.performTransition();
1466 } else if (orientation != 'landscape' && ((screenWidth ==
640 && screenHeight ==
360) || (screenWidth ==
480 && screenHeight ==
640))) {
1467 window.widget.prepareForTransition(
"fade");
1468 orientation = 'landscape';
1469 document.getElementById('body').style.backgroundImage = 'url(' + getBackgroundImage() + ')';
1470 document.getElementById('body').style.backgroundColor = 'none';
1471 window.widget.performTransition();
1473 else if (document.getElementById('body').style.backgroundImage ==
"")
1475 document.getElementById('body').style.backgroundImage = 'url(' + getBackgroundImage() + ')';
1480 function showHomescreen()
1482 log(
"showHomescreen()");
1484 document.getElementById(
"homescreenView").style.display =
"block";
1485 document.getElementById('body').className =
"background";
1486 document.onclick = null;
1490 function getLocalizedText(p_Txt)
1492 if (localizedText[p_Txt])
1493 return localizedText[p_Txt];
1495 return 'ERROR: missing translation for ' + p_Txt;
1498 function showUpdate()
1502 document.getElementById(
"updateView").style.display =
"block";
1503 document.onclick = null;
1505 window.menu.setLeftSoftkeyLabel(getLocalizedText('update.checknow'), function(){
1508 window.menu.setRightSoftkeyLabel(getLocalizedText('softkey.back'), function()
1514 document.getElementById(
"currentVersion").innerHTML = getLocalizedText(
"update.current") + version;
1518 function checkForUpdate()
1520 // asynch XHR to server url
1521 reqV = new XMLHttpRequest();
1522 reqV.onreadystatechange = checkForUpdateCallback;
1523 document.getElementById(
"updateDiv").innerHTML = getLocalizedText(
"update.checking");
1524 reqV.open(
"GET", versionURL, true);
1525 reqV.setRequestHeader(
"If-Modified-Since",
"Sat, 1 Jan 2000 00:00:00 GMT" ); // disable caching
1529 function checkForUpdateCallback()
1531 if (reqV.readyState ==
4) {
1532 if (reqV.status ==
200) {
1533 var resultXml = reqV.responseText;
1535 var div = document.getElementById(
"tmp");
1536 div.innerHTML = resultXml;
1537 var newVersion = div.getElementsByTagName('version')[
0].innerHTML;
1538 var newVersionURL = div.getElementsByTagName('url')[
0].innerHTML;
1540 if (version != newVersion) {
1541 document.getElementById(
"updateDiv").innerHTML = getLocalizedText(
"update.download").replace(/%
1/, newVersion).replace(/%
2/, newVersionURL);
1544 document.getElementById(
"updateDiv").innerHTML = getLocalizedText(
"update.nonewversion");
1549 document.getElementById(
"updateDiv").innerHTML = getLocalizedText(
"update.error") + reqV.status +
" " + reqV.responseText;
1554 function hideViews()
1556 document.getElementById(
"homescreenView").style.display =
"none";
1557 document.getElementById(
"fullscreenView").style.display =
"none";
1558 document.getElementById(
"aboutView").style.display =
"none";
1559 document.getElementById(
"settingsView").style.display =
"none";
1560 document.getElementById(
"updateView").style.display =
"none";
1563 function listCalendars()
1573 DefaultCalendar: false
1577 var calendarsResult = calendarService.IDataSource.GetList(criteria);
1578 if (calendarsResult.ErrorCode !=
0)
1579 throw(
"Error fetching list of calendars: " + calendarsResult.ErrorCode + ': ' + calendarsResult.ErrorMessage);
1580 var calendarListIterator = calendarsResult.ReturnValue;
1585 while (( item = calendarListIterator.getNext()) != undefined ) {
1586 calendars[count++] = item;
1588 log(
"Available Calendars: " + calendars.join(
", "));
1591 error('listing calendars:' + e + ', line ' + e.line);
1596 // Copies all objects and their properties to an array. Data is copied so nothing gets lost when the reference is removed
1597 // 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
1598 function listToArray(list, calendarName)
1600 var array = new Array();
1603 while (( item = list.getNext()) != undefined ) {
1604 var itemCopy = new Object();
1605 for(var i=
0; i < entryFields.length; i++) {
1606 itemCopy[entryFields[i]] = item[entryFields[i]];
1608 // for some reason, the CalendarName property is never correctly queried, so we assign it manually here
1609 if (!itemCopy['CalendarName']) {
1610 itemCopy['CalendarName'] = calendarName;
1612 array.push(itemCopy);
1613 txt += array[array.length -
1].Summary +
", ";
1615 log(
"listToArray(): " + txt);
1619 function sortCalendarEntries(a, b)
1622 log(
"sortCalendarEntries(" + a.Summary +
"," + b.Summary +
")");
1624 if (a.InstanceStartTime != null) {
1625 atime = a.InstanceStartTime;
1627 else if (a.StartTime != null) {
1628 atime = a.StartTime;
1630 else if (a.InstanceEndTime != null) {
1631 atime = a.InstanceEndTime;
1633 else if (a.EndTime != null) {
1637 if (b.InstanceStartTime != null) {
1638 btime = b.InstanceStartTime;
1640 else if (b.StartTime != null) {
1641 btime = b.StartTime;
1643 else if (b.InstanceEndTime != null) {
1644 btime = b.InstanceEndTime;
1646 else if (b.EndTime != null) {
1650 if (atime && btime) {
1652 atime = parseDate(atime);
1653 btime = parseDate(btime);
1655 // sort by date & time
1656 if (atime < btime) {
1659 else if (atime
> btime) {
1663 else if (a.Type != b.Type) {
1664 if (a.Type < b.Type) {
1667 else if (a.Type
> b.Type) {
1671 // sort by description
1672 else if (a.Summary && b.Summary && a.Summary != b.Summary) {
1673 if (a.Summary < b.Summary) {
1676 else if (a.Summary
> b.Summary) {
1681 // NOTE: events my have no date information at all. In that case, we list events without date first
1682 else if (atime && !btime) {
1685 else if (!atime && btime) {
1688 else if (!atime && !btime) {
1690 if (a.Type != b.Type) {
1691 if (a.Type < b.Type) {
1694 else if (a.Type
> b.Type) {
1698 // sort by description
1699 else if (a.Summary && b.Summary && a.Summary != b.Summary) {
1700 if (a.Summary < b.Summary) {
1703 else if (a.Summary
> b.Summary) {
1712 function updateCalendarColors()
1715 calendarColors = [];
1716 if (calendarList.length
> maxColors) {
1717 log(
"updateCalendarColors(): Warning: more calendars than available indicator colors");
1719 for(var i=
0; i < calendarList.length; i++) {
1720 calendarColors[calendarList[i]] = (i % maxColors) +
1;
1724 function log(message)
1726 if (config['enableLogging'].Value) {
1727 console.info(message);
1733 <style type=
"text/css">
1735 table { margin:
0px; padding:
0px; border-spacing:
0px; border-collapse: collapse; }
1736 td { padding:
0px
5px
0px
0px; white-space:nowrap; overflow:hidden; margin:
0px; }
1737 hr { color:#ffffff; background-color:#ffffff; height:
1px; text-align:left; border-style:none; }
1738 .settingsInfo { display:none; font-style:italic; }
1739 .title { font-weight:bold; font-size:
14pt; }
1740 .textInput { width:
90%; }
1741 .credits { margin-left:
40px; text-indent: -
20px; margin-bottom:
0px; }
1742 #homescreenView { width:
312px; height:
82px; overflow:hidden; }
1743 #calendarList { position:absolute; left:
5px; top:
0px; width:
307px; height:
82px; overflow:hidden; }
1744 #name { text-align:center; }
1745 #appicon { display: block; margin-left: auto; margin-right: auto; margin-top:
10px; }
1746 #smallappicon { width:
22px; height:
22px; margin-right:
10px; float:left; }
1751 <body onload=
"javascript:setTimeout('init()', 10)" onresize=
"javascript:updateScreen()" id=
"body" class=
"background">
1752 <div id=
"homescreenView">
1753 <div id=
"calendarList">loading...
</div>
1755 <div id=
"fullscreenView" style=
"display:none;">
1756 <img src=
"Icon.png" id=
"smallappicon">
1757 <h1 class=
"title">Coming Next
</h1>
1759 <div id=
"fullscreenCalendarList">loading...
</div>
1761 <div id=
"settingsView" style=
"display:none">
1762 <img src=
"Icon.png" id=
"smallappicon">
1763 <h1 id=
"settingsTitle" class=
"title">Settings
</h1>
1765 <div id=
"settingsList"></div>
1767 <div id=
"aboutView" style=
"display:none">
1768 <img src=
"Icon.png" id=
"appicon">
1769 <h1 id=
"name">Coming Next
</h1>
1771 <p>Created by Dr. Cochambre and Michael Prager.
</p>
1772 <p>Contributions:
</p>
1773 <p class=
"credits">Paul Moore (bug fixes, new features and code cleanup)
</p>
1774 <p class=
"credits">Manfred Hanselmann (DST support)
</p>
1775 <p class=
"credits">Christophe Milsent (translation support & French translation)
</p>
1776 <p class=
"credits">Flavio Nathan (Portuguese-Brazilian translation)
</p>
1777 <p class=
"credits">Tokeda (Russian translation)
</p>
1778 <p class=
"credits">Marcella Ferrari (Italian translation)
</p>
1779 <p class=
"credits">Venos (Italian translation)
</p>
1780 <p class=
"credits">Francisco Rodero (Catalan translation)
</p>
1781 <p class=
"credits">zbigzbig20 (Polish translation)
</p>
1782 <p class=
"credits">Streamkeskus (Finish translation)
</p>
1783 <p>This software is open source and licensed under the GPLv3.
</p>
1784 <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>
1787 <div id=
"updateView" style=
"display:none">
1788 <img src=
"Icon.png" id=
"smallappicon">
1789 <h1 class=
"title">Check for update
</h1>
1791 <div id=
"currentVersion">Coming Next ??
</div>
1792 <div id=
"updateDiv"></div>
1793 <div id=
"tmp" style=
"display:none;"></div>