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
111 // vars for daylight saving time
112 var summertime = false; // true, if current date is in summer, false if in winter
113 var daylightSavingDates = new Object(); // caches calculated DST winter and summer time shift dates
115 // this is a list of data fields a calendar event can have
129 function isLeapYear( year ) {
130 if (( year %
4 ==
0 && year %
100 !=
0 ) || year %
400 ==
0 )
136 function calcLeapYear(year, days)
138 if (isLeapYear(year))
144 function subToSunday(myDate, year, days, prevMonthDays)
146 for (i = myDate.getDay(); i
> 0 ;i--)
148 days -= prevMonthDays;
149 days = isLeapYear(year) ? --days : days;
153 function isSummertime(curDate)
158 // if we already calculated DST summer and winter time dates for this year, use cached values
159 var dst = daylightSavingDates[curDate.getFullYear()];
161 var thisYearS = new Date(curDate.getFullYear(),
3,
0,
0,
0,
0 );
162 var thisYearW = new Date(curDate.getFullYear(),
10,
0,
0,
0,
0 );
163 var nextYearS = new Date(curDate.getFullYear() +
1,
3,
0,
0,
0,
0 );
164 var nextYearW = new Date(curDate.getFullYear() +
1,
10,
0,
0,
0,
0 );
166 thisYearSDays = nextYearSDays =
90;
167 thisYearWDays = nextYearWDays =
304;
169 thisYearSDays = calcLeapYear(curDate.getFullYear(), thisYearSDays);
170 thisYearWDays = calcLeapYear(curDate.getFullYear(), thisYearWDays);
171 nextYearSDays = calcLeapYear(curDate.getFullYear() +
1, nextYearSDays);
172 nextYearWDays = calcLeapYear(curDate.getFullYear() +
1, nextYearWDays);
174 thisYearSDays = subToSunday(thisYearS, curDate.getFullYear(), thisYearSDays,
59);
175 thisYearWDays = subToSunday(thisYearW, curDate.getFullYear(), thisYearWDays,
273);
176 nextYearSDays = subToSunday(nextYearS, curDate.getFullYear() +
1, nextYearSDays,
59);
177 nextYearWDays = subToSunday(nextYearW, curDate.getFullYear() +
1, nextYearWDays,
273);
180 Summer: new Date (curDate.getFullYear(),
03-
1, thisYearSDays,
2,
0,
0),
181 Winter: new Date (curDate.getFullYear(),
10-
1, thisYearWDays,
2,
0,
0),
183 daylightSavingDates[curDate.getFullYear()] = dst;
186 if (dst.Summer < curDate)
188 if (dst.Winter < curDate)
190 if (summer && !winter)
196 function error(message)
198 console.info('Error: ' + message);
199 document.getElementById(
"calendarList").innerHTML = 'Error: ' + message;
200 document.getElementById(
"fullscreenCalendarList").innerHTML = 'Error: ' + message;
202 document.onclick = null;
205 function areDatesEqual(date1, date2)
207 return (date1.getFullYear() == date2.getFullYear() &&
208 date1.getMonth() == date2.getMonth() &&
209 date1.getDate() == date2.getDate());
212 function isTomorrow(date)
214 // tommorow = now +
1 day
215 // ToDo: some days can be shorter as
24 hours(daylight saving change day)
216 return areDatesEqual(date, new Date (now.getTime() +
24*
60*
60*
1000));
219 function isToday(date)
221 return areDatesEqual(date, now);
224 function collectLocales()
226 var tmpyear =
2000 + panelNum;
229 if (months_translated.length
> 0)
231 for (month =
0; month <
12; month++) {
232 var startDate = new Date(tmpyear, month,
15);
234 var item = new Object();
235 item.Type =
"DayEvent";
236 item.StartTime = startDate;
237 item.Summary =
"__temp" + month;
239 var criteria = new Object();
240 criteria.Type =
"CalendarEntry";
241 criteria.Item = item;
244 var result = calendarService.IDataSource.Add(criteria);
245 if (result.ErrorCode)
246 throw(result.ErrorMessage);
248 error(
"collectLocales: " + e + ', line ' + e.line);
251 for (weekday =
0; weekday <
7; weekday++) {
252 var startDate = new Date(
2000,
0,
2 + weekday); // date that we know for sure is a sunday
254 var item = new Object();
255 item.Type =
"DayEvent";
256 item.StartTime = startDate;
257 item.Summary =
"__weekday_temp" + weekday;
259 var criteria = new Object();
260 criteria.Type =
"CalendarEntry";
261 criteria.Item = item;
264 var result = calendarService.IDataSource.Add(criteria);
265 if (result.ErrorCode)
266 throw(result.ErrorMessage);
268 error(
"collectLocales: " + e + ', line ' + e.line);
272 var startTime = new Date(tmpyear,
0,
1);
273 var endTime = new Date(tmpyear,
11,
31);
274 var listFiltering = {
275 Type:'CalendarEntry',
277 StartRange: startTime,
279 SearchText: '__temp',
283 var result = calendarService.IDataSource.GetList(listFiltering);
284 if (result.ErrorCode)
285 throw(result.ErrorMessage);
286 var list = result.ReturnValue;
288 error(
"collectLocales: " + e + ', line ' + e.line);
291 var ids = new Array();
297 while (list && (entry = list.getNext()) != undefined) {
298 dateArr = (entry.StartTime + '').replace(/,/g,'').replace(/\./g,':').replace(/ /g,' ').split(' ');
299 var day = dateArr[
1];
300 var month = dateArr[
2];
301 var year = dateArr[
3];
303 // make sure month is set properly
304 if (isNaN(parseInt(day))) {
308 } else if (isNaN(parseInt(year))) {
314 log(entry.StartTime + ' -
> ' + month + ' ' + counter);
315 ids[counter] = entry.id;
316 months_translated[month] = counter +
1;
320 error(
"collectLocales: " + e + ', line ' + e.line);
324 var startTime = new Date(
2000,
0,
2);
325 var endTime = new Date(
2000,
0,
9);
326 var listFiltering = {
327 Type:'CalendarEntry',
329 StartRange: startTime,
331 SearchText: '__weekday_temp',
335 var result = calendarService.IDataSource.GetList(listFiltering);
336 if (result.ErrorCode)
337 throw(result.ErrorMessage);
338 var weekdaylist = result.ReturnValue;
340 error(
"collectLocales: " + e + ', line ' + e.line);
348 while (weekdaylist && (entry = weekdaylist.getNext()) != undefined) {
349 curWeekday = (entry.StartTime + '').split(',')[
0];
350 log(entry.StartTime + ' -
> ' + curWeekday + ' ' + counter2);
351 ids[counter + counter2] = entry.id;
352 weekdays_translated[counter2] = curWeekday;
356 error(
"collectLocales: " + e + ', line ' + e.line);
361 var criteria = new Object();
362 criteria.Type =
"CalendarEntry";
367 var result = calendarService.IDataSource.Delete(criteria);
368 if (result.ErrorCode)
369 throw(result.ErrorMessage);
371 error('deleting temp calendar entries:' + e + ', line ' + e.line);
376 function requestNotification()
378 var criteria = new Object();
379 criteria.Type =
"CalendarEntry";
380 criteria.Filter = new Object();
381 for(var i=
0; i < calendarList.length; i++) {
382 criteria.Filter.CalendarName = calendarList[i];
384 var notificationRequest = calendarService.IDataSource.RequestNotification(criteria, callback);
385 if (notificationRequest.ErrorCode)
386 error('requestNotification failed with error code ' + notificationRequest.ErrorCode);
387 notificationRequests.push(notificationRequest);
389 error(
"requestNotification: " + e + ', line ' + e.line);
393 var criteria2 = new Object();
394 criteria2.Type =
"CalendarEntry";
395 criteria2.Filter = new Object();
396 criteria2.Filter.LocalIdList = new Array();
397 criteria2.Filter.LocalIdList[
0] = settingsCalEntryId;
399 var notificationRequest = calendarService.IDataSource.RequestNotification(criteria2, settingsCallback);
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 function cancelNotification()
410 for(var i=
0; i < notificationRequests.length; i++) {
412 var result = calendarService.IDataSource.Cancel(notificationRequests[i]);
413 if (result.ErrorCode)
414 error('cancelNotification failed with error code ' + result.ErrorCode);
416 error(
"cancelNotification: " + e + ', line ' + e.line);
421 function callback(transId, eventCode, result)
423 log(
"callback(): panelNum: " + panelNum +
" transId: " + transId +
" eventCode: " + eventCode +
" result.ErrorCode: " + result.ErrorCode);
424 lastReloadTime = null; // force calendar data reload on next update
428 function settingsCallback(transId, eventCode, result)
430 log(
"settingsCallback(): panelNum: " + panelNum +
" transId: " + transId +
" eventCode: " + eventCode +
" result.ErrorCode: " + result.ErrorCode);
434 function parseDate(dateString)
437 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:
438 Wednesday,
26 August,
2009 24:
00:
00
439 Wednesday,
26 August,
2009 12:
00:
00 am
440 Wednesday, August
26,
2009 12:
00:
00 am
441 Wednesday,
2009 August,
26 12:
00:
00 am
442 Wednesday,
2009 August,
28 8.00.00 pm
443 Wednesday,
2009 August,
28 08:
00:
00 PM
447 if (dateString ==
"" || dateString == null || dateString == undefined)
449 if (dateString instanceof Date) {
450 // we already have a date object, no need to parse string here
454 var dateArr = (dateString + '').replace(/,/g, '').replace(/\./g, ':').replace(/ /g, ' ').split(' ');
455 if (dateArr.length !=
5 && dateArr.length !=
6)
459 var weekDay = dateArr[
0];
460 var day = dateArr[
1];
461 var month = dateArr[
2];
462 var year = dateArr[
3];
463 // make sure month is set properly
464 if (isNaN(parseInt(day))) {
470 if (isNaN(parseInt(year))) {
475 // make sure day and year are set properly
476 if (Number(day)
> Number(year)) {
481 month = months_translated[month];
484 var timeArr = dateArr[
4].split(':');
485 if (timeArr.length !=
3)
487 var hours = Number(timeArr[
0]);
488 var minutes = Number(timeArr[
1]);
489 var seconds = Number(timeArr[
2]);
490 if (dateArr.length ==
6 && dateArr[
5].toLowerCase() == 'pm' && hours <
12)
492 if (dateArr.length ==
6 && dateArr[
5].toLowerCase() == 'am' && hours ==
12)
495 // remember if date was formated using
12h time format, we need to use this information later when formating time
496 if (dateArr.length ==
6 && (dateArr[
5].toLowerCase() == 'am' || dateArr[
5].toLowerCase() == 'pm'))
497 use12hoursTimeFormat = true;
499 result = new Date(year, month -
1, day, hours, minutes, seconds);
502 // take care of daylight saving time
503 if (config['enableDaylightSaving'].Value) {
505 // determine if date is in summer or winter time
506 var dateSummerTime = isSummertime(result);
508 // work around bug in Nokias calendar api resulting in dates within a different DST to be off by
1 hour
509 if (summertime && !dateSummerTime) {
510 result = new Date(result.getTime() -
1000 *
60 *
60 * config['daylightSavingOffset'].Value); // -
1 hour
511 log('parseDate(): fixing time -
1h: ' + result);
513 else if (!summertime && dateSummerTime) {
514 result = new Date(result.getTime() +
1000 *
60 *
60 * config['daylightSavingOffset'].Value); // +
1 hour
515 log('parseDate(): fixing time +
1h: ' + result);
522 function getWeekdayLocalized(date) {
523 var localizedString = date.toLocaleDateString();
524 if (localizedString.match(/\d\d.\d\d.\d\d/)) {
525 return weekdays_translated[date.getDay()];
527 return localizedString.split(',')[
0];
530 // 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"
531 function formatDate(date, format)
533 var day = date.getDate().toString();
534 var month = (date.getMonth() +
1).toString();
535 while (day.length <
2) { day = '
0' + day; }
536 while (month.length <
2) { month = '
0' + month; }
538 if (config['showTodayAsText'].Value && isToday(date))
539 return '
<span class=
"today">' + config['todayText'].Value + '
</span>';
540 if (config['showTodayAsText'].Value && isTomorrow(date))
541 return '
<span class=
"tomorrow">' + config['tomorrowText'].Value + '
</span>';
543 if (format instanceof Date) {
544 // we don't know how to format this
545 if (config['dateFormat'].Value == 'auto' || config['dateFormat'].Value == 'DDMM')
546 return day + config['dateSeparator'].Value + month;
548 return month + config['dateSeparator'].Value + day;
550 var dateArr = format.replace(/,/g,'').replace(/\./g,':').replace(/ /g,' ').split(' ');
551 if (dateArr.length !=
5 && dateArr.length !=
6) {
552 // we don't know how to format this
553 if (config['dateFormat'].Value == 'auto' || config['dateFormat'].Value == 'DDMM')
554 return day + config['dateSeparator'].Value + month;
556 return month + config['dateSeparator'].Value + day;
560 if (config['dateFormat'].Value == 'MMDD')
562 else if (config['dateFormat'].Value == 'DDMM')
565 // config['dateFormat'].Value == 'auto', try to detect system setting
567 var day_ = dateArr[
1];
568 var month_ = dateArr[
2];
569 var year_ = dateArr[
3];
570 // make sure month is set properly
571 if (isNaN(parseInt(day_))) {
576 } else if (isNaN(parseInt(year_))) {
582 // make sure day and year are set properly
583 if (Number(day_)
> Number(year_))
588 return day + config['dateSeparator'].Value + month;
590 return month + config['dateSeparator'].Value + day;
593 function formatTime(date)
595 // date is a Date() object
596 date.setSeconds(
0); // we don't care about seconds
597 var time = date.toLocaleTimeString().replace(/[\.:]
00/, ''); // remove seconds from string
598 if (time.replace(/\./, ':').split(':')[
0].length <
2)
601 // workaround for bug introduced by Anna firmwares, which causes Date().toLocaleTimeString() to no longer return time in
12h format even though this has been defined in system settings
602 if (use12hoursTimeFormat && time.toLowerCase().indexOf('am') == -
1 && time.toLowerCase().indexOf('pm') == -
1) {
603 var hour = date.getHours();
604 var minute = date.getMinutes();
615 minute =
"0" + minute;
616 time = hour +
":" + minute +
" " + ap;
619 if (config['showNowAsText'].Value && date.getTime() == now.getTime())
620 time = '
<span class=
"now">' + config['nowText'].Value + '
</span>';
624 function updateData()
631 // check if we got additional or less calendars since our last update
632 var newCalendarList = listCalendars();
633 if (newCalendarList == null) {
634 // Something went wrong fetching the calendars list.
635 // This usually happens when a backup is being made.
636 // Retry the next time updateData() is called by
637 // resetting errorOccured
638 log('updateData(): listCalendars() failed, trying again later...');
639 cacheEntriesHtml = ''; // make sure we replace the currently shown error message on the next update
640 errorOccured = false;
643 if (newCalendarList.length != calendarList.length) {
644 calendarList = newCalendarList;
645 updateCalendarColors();
646 cancelNotification();
647 requestNotification();
648 lastReloadTime = null; // force calendar data reload on this update
653 // only reload calendar data every
6 hours, visual updates occure more often
654 if (!lastReloadTime || now.getTime() - lastReloadTime.getTime()
> reloadInterval) {
655 log('updateData(): reloading calendar data');
657 // meetings have time
658 // 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
659 summertime = isSummertime(now); // cache summer time info for today
660 var meetingList = [];
661 for(var i=
0; i < calendarList.length; i++) {
662 // ignore excluded calendars
663 if (config['excludedCalendars'].Value.indexOf(calendarList[i]) != -
1)
665 var meetingListFiltering = {
666 Type:'CalendarEntry',
668 CalendarName: calendarList[i],
669 StartRange: (new Date(now.getFullYear(), now.getMonth(), now.getDate(),
0,
0,
0)),
670 EndRange: (new Date(now.getFullYear(), now.getMonth() + config['monthRange'].Value, now.getDate(),
0,
0,
0))
673 var meetingResult = calendarService.IDataSource.GetList(meetingListFiltering);
674 if (meetingResult.ErrorCode !=
0)
675 throw(
"Error fetching calendar data: " + meetingResult.ErrorCode + ': ' + meetingResult.ErrorMessage);
676 var list = meetingResult.ReturnValue;
677 meetingList = meetingList.concat(listToArray(list, calendarList[i]));
679 log(
"updateData(): meetingList.sort()");
680 meetingList.sort(sortCalendarEntries);
682 // todos don't, they start on
00:
00 hrs., but should be visible anyway
683 // this will generate a list of passed todos. We have to check if they have been marked as
"done" yet
684 if (config['includeTodos'].Value) {
685 var todayTodoList = [];
686 for(var i=
0; i < calendarList.length; i++) {
687 // ignore excluded calendars
688 if (config['excludedCalendars'].Value.indexOf(calendarList[i]) != -
1)
690 var todayTodoListFiltering = {
691 Type:'CalendarEntry',
693 CalendarName: calendarList[i],
695 StartRange: (new Date(now.getFullYear() -
1, now.getMonth(), now.getDate(),
0,
0,
0)),
696 EndRange: (new Date(now.getFullYear(), now.getMonth(), now.getDate(),
0,
0,
1))
699 var todayTodoResult = calendarService.IDataSource.GetList(todayTodoListFiltering);
700 var list = todayTodoResult.ReturnValue;
701 todayTodoList = todayTodoList.concat(listToArray(list, calendarList[i]));
703 log(
"updateData(): todayTodoList.sort()");
704 todayTodoList.sort(sortCalendarEntries);
705 entryLists = [todayTodoList, meetingList];
707 entryLists = [meetingList];
709 lastReloadTime = new Date();
711 error('loading Calendar items list:' + e + ', line ' + e.line);
721 var fontsize = 'normal';
722 var lineheight = 'normal';
724 fontsize = parseInt(
72 / config['eventsPerWidget'].Value) + 'px';
725 lineheight = parseInt(
82 / config['eventsPerWidget'].Value) + 'px';
727 if (config['eventsPerWidget'].Value ==
3) {
728 changeCssClass('.icon', 'width:
20px; height:
20px');
730 else if (config['eventsPerWidget'].Value ==
5) {
731 changeCssClass('.icon', 'width:
10px; height:
10px');
733 else if (config['eventsPerWidget'].Value ==
6) {
734 changeCssClass('.icon', 'width:
8px; height:
8px');
738 changeCssClass('.icon', config['cssStyle_icon'].Value);
739 var entriesHtml = '
<table style=
"font-size:' + fontsize + '; line-height:' + lineheight + ';">';
741 entriesHtml = '
<table width=
"307" height=
"82"><tr><td>' + entriesHtml; // this is needed to center the actual content vertically
745 max = (panelNum +
1) * config['eventsPerWidget'].Value;
747 max =
30; // we can display a lot more events in fullscreen mode
749 if (config['enableLogging'].Value) {
751 for (var i=
0; i < entryLists.length; i++) {
752 listinfo = listinfo +
" " + entryLists[i].length;
753 var entrieslist =
"";
754 for (var j=
0; j < entryLists[i].length; j++) {
755 entrieslist += entryLists[i][j].Summary +
", ";
757 log(
"updateData(): entrieslist: " + entrieslist);
759 log(
"updateData(): inner loop, " + entryLists.length +
" lists, [" + listinfo +
"] entries");
762 // the first outer loop iteration is for passed ToDos, the second loop is for all upcomming events (may also include ToDos)
763 for (var i=
0; counter < max && i < entryLists.length; i++) {
764 for (var j=
0; (counter < max) && (j < entryLists[i].length); j++) {
765 entry = entryLists[i][j];
768 // output event info for debugging
769 var entryInfo =
"event: ";
770 for(var k=
0; k < entryFields.length; ++k) {
771 if (entry[entryFields[k]] != undefined) {
772 entryInfo += entryFields[k] +
"=" + entry[entryFields[k]] +
",";
777 // we don't want ToDos when includeTodos == false or when they are completed
778 if (entry.Type == 'ToDo' && (entry.Status ==
"TodoCompleted" || !config['includeTodos'].Value)) {
779 log('skipping ' + entry.id );
784 // make sure that we don't include an event twice (useful for ToDos that might come up twice)
785 if (eventIds[entry.id] ==
1 && entry.Type == 'ToDo') {
786 log('skipped (already included) ' + entry.id);
790 eventIds[entry.id] =
1;
792 // summary can be undefined!
793 var Summary = ((entry.Summary == null) ? '' : entry.Summary);
794 if (entry.Location != '' && entry.Location != undefined && config['showLocation'].Value)
795 Summary += ', ' + entry.Location;
797 // fix by yves: determine start and end dates/times
798 entryStartTime = ((entry.InstanceStartTime == null) ? entry.StartTime : entry.InstanceStartTime);
799 entryEndTime = ((entry.InstanceEndTime == null) ? entry.EndTime : entry.InstanceEndTime);
801 // there can be ToDos that have no date at all!
802 if (entry.Type == 'ToDo' && entry.EndTime == null)
803 entryDate =
""; // this will cause parseDate(entryDate) to return null;
805 entryDate = ((entry.Type == 'ToDo') ? entryEndTime : entryStartTime); // ToDo's use their EndTime, the rest use StartTime
807 // Convert date/time string to Date object
808 var date = parseDate(entryDate);
809 log('date: ' + date);
810 var endDate = ((entryEndTime == null) ? null : parseDate(entryEndTime));
811 log('endDate: ' + endDate);
813 // check if Meeting is actually a DayEvent. Bug introduced by
"Anna" updates to various Symbian^
3 devices.
814 // Note that this workaround is not
100% save! It might missinterpret some meetings as dayevents of starting and ending on
00:
00
815 if (entry.Type == 'Meeting' && date.getHours() ==
0 && date.getMinutes() ==
0 &&
816 endDate != null && endDate.getHours() ==
0 && endDate.getMinutes() ==
0) {
817 log('fixing event type: changed from
"Meeting" to
"DayEvent".');
818 entry.Type = 'DayEvent';
821 // check if meeting event has already passed
822 if (entry.Type == 'Meeting') {
823 var compareTime = ((endDate == null) ? date.getTime() : endDate.getTime());
824 if (now.getTime()
> compareTime) {
825 log('skipping Meeting (already passed) ' + entry.id);
827 eventIds[entry.id] =
0;
832 // check if anniversary passed (not sure why they are in the list, the query was only for today - nokia?)
833 if (entry.Type == 'Anniversary') {
834 var tmp = new Date(now.getFullYear(), now.getMonth(), now.getDate(),
0,
0,
0);
835 if (date.getTime() < tmp.getTime()) {
836 log('skipping Anniversary (already passed) ' + entry.id);
838 eventIds[entry.id] =
0;
843 // fix DayEvents end time. End times are off by
1 Second. It's possible that the event has already passed
844 if (entry.Type == 'DayEvent' && endDate != null) {
845 endDate.setMinutes(endDate.getMinutes() -
1);
846 log('fixing DayEvent endDate: ' + endDate);
847 if (now.getTime()
> endDate.getTime()) {
848 log('event already passed ' + entry.id);
850 eventIds[entry.id] =
0;
855 // check if the event is currently taking place
856 if (entryStartTime != null && entryEndTime != null && date != null && endDate != null) {
857 // check if we are between start and endtime
858 if ((date.getTime() < now.getTime()) && (now.getTime() < endDate.getTime())) {
859 date = now; // change appointment date/time to now
860 log('event is currently taking place: ' + date);
864 // skip events for the first panel in case this is the second one and we're not in fullscreen mode
865 if (mode ==
0 && panelNum
> 0 && counter < panelNum * config['eventsPerWidget'].Value +
1) {
866 log('skipping (already in first widget) ' + entry.id);
870 // mark overdue todos
872 if (entry.Type == 'ToDo' && date != null) {
873 var tmp1 = new Date(date.getFullYear(), date.getMonth(), date.getDate(),
0,
0,
0);
874 var tmp2 = new Date(now.getFullYear(), now.getMonth(), now.getDate(),
0,
0,
0);
875 if (tmp1.getTime() < tmp2.getTime()) {
880 // generate html output
881 entriesHtml += '
<tr>';
882 if (config['showCalendarIndicator'].Value && calendarList.length - config['excludedCalendars'].Value.length
> 1) {
883 entriesHtml += '
<td><span class=
"calendar' + calendarColors[entry.CalendarName] + '"> </span></td>';
885 entriesHtml += '
<td><img class=
"icon" src=
"' + entry.Type + '.png" /></td>';
887 // some languages have very strange locale date formats, can't parse all those. Also some todos don't have dates at all.
888 entriesHtml += '
<td colspan=
"4"><span class=
"date">' + entryDate + '
</span> ';
890 var weekDay = getWeekdayLocalized(date).substr(
0,config['weekDayLength'].Value);
891 log('date.toLocaleDateString(): ' + date.toLocaleDateString());
892 log('weekDay: ' + weekDay);
893 var time = formatTime(date);
894 var dateStr = formatDate(date, entryDate);
895 if (entry.Type == 'ToDo' && overdue && config['markOverdueTodos'].Value) {
896 dateStr = '
<span class=
"overdue">' + config['overdueText'].Value + '
</span>';
897 entriesHtml += '
<td colspan=
"4" width=
"1px"><span class=
"date">' + dateStr + '
</span> ';
898 } else if (entry.Type == 'ToDo' || entry.Type == 'Anniversary' || entry.Type == 'DayEvent' || entry.Type == 'Reminder') {
899 if ((isToday(date) || isTomorrow(date)) && config['showTodayAsText'].Value) // show weekday if the date string is not text. looks odd otherwise
900 entriesHtml += '
<td colspan=
"4" width=
"1px"><span class=
"date">' + dateStr + '
</span> ';
902 entriesHtml += '
<td class=
"weekDay" width=
"1px">' + weekDay + '
</td><td width=
"1px" class=
"date">' + dateStr + '
</td><td colspan=
"2">';
903 } else if (entry.Type == 'Meeting') {
904 if (config['showCombinedDateTime'].Value) {
906 entriesHtml += '
<td width=
"1px" colspan=
"4"><span class=
"today">' + time + '
</span> ';
907 else if (isTomorrow(date))
908 entriesHtml += '
<td width=
"1px" colspan=
"4"><span class=
"tomorrow">' + dateStr + '
</span> <span class=
"time">' + time + '
</span> ';
910 entriesHtml += '
<td width=
"1px" class=
"weekDay">' + weekDay + '
</td><td width=
"1px" class=
"date">' + dateStr + '
</td><td colspan=
"2">';
912 if ((isToday(date) || isTomorrow(date)) && config['showTodayAsText'].Value)
913 entriesHtml += '
<td colspan=
"4" width=
"1px"><span class=
"today">' + dateStr + '
</span> <span class=
"time">' + time + '
</span> ';
915 entriesHtml += '
<td width=
"1px" class=
"weekDay">' + weekDay + '
</td><td width=
"1px" class=
"date">' + dateStr + '
</td><td width=
"1px" class=
"time">' + time + '
</td><td>';
919 entriesHtml += '
<span class=
"description">' + Summary + '
</span></td></tr>';
922 entriesHtml += '
</table>';
924 entriesHtml = entriesHtml + '
</td></tr></table>';
925 if (config['showNothingText'].Value && entriesHtml == '
<table></table>') {
926 var text = config['nothingText'].Value.replace(/%d/, config['monthRange'].Value);
927 entriesHtml = '
<div style=
"width:295px; height:75px; text-align:center; line-height:75px; overflow:visible;">' + text + '
</div>';
929 if (cacheEntriesHtml != entriesHtml) {
931 document.getElementById('calendarList').innerHTML = entriesHtml;
933 document.getElementById('fullscreenCalendarList').innerHTML = entriesHtml;
934 cacheEntriesHtml = entriesHtml;
937 lastUpdateTime = new Date();
939 error('displaying list:' + e + ', line ' + e.line);
944 // called by handleOnShow() and onResize events
945 function updateScreen()
947 log('updateScreen()');
949 // check if opening fullscreen
950 if( window.innerHeight
> 91 && mode ==
0) {
952 cacheEntriesHtml = '';
953 document.getElementById('body').style.backgroundImage =
"";
956 else if (window.innerHeight <=
91 && mode !=
0) {
958 cacheEntriesHtml = '';
963 updateHomescreen(); // check for screen rotation
968 function handleOnShow()
972 var time = new Date();
973 if (time.getTime() - lastUpdateTime.getTime()
> config['updateDataInterval'].Value *
60 *
1000) {
974 log('updateScreen(): force updateData() because last update was too long ago (' + (time.getTime() - lastUpdateTime.getTime()) /
1000 + 's)');
977 setUpdateTimer(); // reinitialize update timer
981 function launchCalendar()
984 widget.openApplication(config['calendarApp'].Value,
"");
985 if (config['hideWidgetOnCalendarOpen'].Value)
988 error('starting Calendar App');
995 log('New widget instance starting up...');
998 // call calendar service
999 if (device !=
"undefined")
1000 calendarService = device.getServiceObject(
"Service.Calendar",
"IDataSource");
1002 throw('device object does not exist');
1004 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>');
1008 calendarList = listCalendars();
1010 updateCalendarColors();
1013 requestNotification();
1014 document.getElementById(
"settingsTitle").innerHTML = getLocalizedText('menu.settings');
1016 if (window.innerHeight
> 91) {
1017 mode =
0; // we're starting fullscreen, we set mode to homescreen in order to let updateScreen() do all the work for us
1022 log(
"init(): updateScreen()");
1024 if (config['useBackgroundImage'].Value)
1025 // check for screen rotation every
1 secs
1026 screenRotationTimer = window.setInterval('checkOrientation()',
1000 *
1);
1028 // call updateScreen() when widget changes from background to forground
1029 window.widget.onshow = handleOnShow;
1031 log(
"init(): finished...");
1033 statupSuccessful = true;
1036 function checkOrientation()
1040 updateHomescreen(); // check for screen rotation
1043 function setUpdateTimer()
1045 updateTimer = window.setInterval('updateTimerCallback()',
1000 *
60 * config['updateDataInterval'].Value);
1048 function clearUpdateTimer()
1050 window.clearInterval(updateTimer);
1053 function updateTimerCallback()
1055 log(
"updateTimerCallback()");
1059 function createMenu()
1061 window.menu.setLeftSoftkeyLabel(
"",null);
1062 window.menu.setRightSoftkeyLabel(
"",null);
1064 var menuSettings = new MenuItem(getLocalizedText('menu.settings'), id++);
1065 var menuCallApp = new MenuItem(getLocalizedText('menu.openCalendarApp'), id++);
1066 var menuHelp = new MenuItem(getLocalizedText('menu.help'), id++);
1067 var menuUpdate = new MenuItem(getLocalizedText('menu.update'), id++);
1068 var menuAbout = new MenuItem(getLocalizedText('menu.about'), id++);
1069 menuSettings.onSelect = showSettings;
1070 menuAbout.onSelect = showAbout;
1071 menuCallApp.onSelect = launchCalendar;
1072 menuUpdate.onSelect = showUpdate;
1073 menuHelp.onSelect = showHelp;
1074 window.menu.clear();
1075 window.menu.append(menuCallApp);
1076 window.menu.append(menuSettings);
1077 window.menu.append(menuHelp);
1078 window.menu.append(menuUpdate);
1079 window.menu.append(menuAbout);
1082 function showSettings()
1086 document.getElementById(
"settingsView").style.display =
"block";
1087 document.onclick = null;
1089 window.menu.setLeftSoftkeyLabel(getLocalizedText('settings.save'), function()
1091 for (var key in config) {
1092 if (config[key].Type == 'String')
1093 config[key].Value = document.forms[
0].elements[
"settings." + key].value;
1094 else if (config[key].Type == 'Int') {
1095 config[key].Value = parseInt(document.forms[
0].elements[
"settings." + key].value);
1096 if (config[key].Value <
0 || isNaN(config[key].Value))
1097 config[key].Value = config[key].Default;
1099 else if (config[key].Type == 'Bool')
1100 config[key].Value = document.forms[
0].elements[
"settings." + key].checked;
1101 else if (config[key].Type == 'UID') {
1102 config[key].Value = parseInt(document.forms[
0].elements[
"settings." + key].value);
1103 if (isNaN(config[key].Value))
1104 config[key].Value = config[key].Default;
1106 else if (config[key].Type == 'Enum') {
1107 config[key].Value = document.forms[
0].elements[
"settings." + key].value;
1108 if (config[key].ValidValues.indexOf(config[key].Value) == -
1)
1109 config[key].Value = config[key].Default;
1111 else if (config[key].Type == 'Array') {
1112 if (key == 'excludedCalendars') {
1113 config[key].Value = new Array();
1114 for(var i=
0; i < calendarList.length; i++) {
1115 var element = document.forms[
0].elements[
"settings." + key +
"." + calendarList[i]];
1116 if (element != null && element.checked == false)
1117 config[key].Value.push(calendarList[i]);
1130 window.menu.setRightSoftkeyLabel(getLocalizedText('settings.cancel'), function()
1136 var settingsHtml = '
<form>';
1137 for (var key in config) {
1138 if (config[key].Type == 'String') {
1140 if (key.substring(
0,
9) ==
"cssStyle_")
1141 prefix = getLocalizedText('settings.cssStyle_prefix');
1142 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 />';
1144 else if (config[key].Type == 'Int')
1145 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 />';
1146 else if (config[key].Type == 'Bool')
1147 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 />';
1148 else if (config[key].Type == 'UID')
1149 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 />';
1150 else if (config[key].Type == 'Enum') {
1151 settingsHtml += '
<table><tr><td>' + getLocalizedText('settings.name.' + key) + '
<br /><select name=
"settings.' + key + '" size=
"1">';
1152 for(var i =
0; i < config[key].ValidValues.length; i++)
1153 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>';
1154 settingsHtml += '
</select></div></td>' + printHintBox(getLocalizedText('settings.info.' + key)) + '
<hr />';
1156 else if (config[key].Type == 'Array') {
1157 settingsHtml += '
<table><tr><td>' + getLocalizedText('settings.name.' + key) + '
<br />';
1158 if (key == 'excludedCalendars') {
1159 for(var i=
0; i < calendarList.length; i++) {
1160 var checked = '
checked=
"checked"';
1161 if (config[key].Value.indexOf(calendarList[i]) != -
1)
1163 settingsHtml += '
<input name=
"settings.' + key + '.' + calendarList[i] + '" type=
"checkbox" value=
"' + calendarList[i] + '" ' + checked + '
/> ' + calendarList[i] + '
<br />';
1166 settingsHtml += '
</td>' + printHintBox(getLocalizedText('settings.info.' + key)) + '
<hr />';
1169 settingsHtml += '
<input name=
"reset" type=
"button" value=
"' + getLocalizedText('settings.restoreDefaults') + '" onclick=
"javascript:restoreDefaultSettings();showSettings();" />';
1170 settingsHtml += '
</form>';
1171 document.getElementById(
"settingsList").innerHTML = settingsHtml;
1174 function changeCssClass(classname, properties)
1176 for(var i =
0; i < document.styleSheets[
0]['cssRules'].length; i++)
1178 if (document.styleSheets[
0]['cssRules'][i].selectorText == classname) {
1179 document.styleSheets[
0].deleteRule(i);
1180 document.styleSheets[
0].insertRule(classname + ' { ' + properties + ' }', document.styleSheets[
0]['cssRules'].length);
1186 function updateCssClasses()
1188 for(var key in config) {
1189 changeCssClass(getLocalizedText('settings.name.' + key), config[key].Value);
1193 function getSettingsCalEntryId()
1195 if (settingsCalEntryId == null) {
1196 // check if entry already exists
1197 var listFiltering = {
1198 Type:'CalendarEntry',
1200 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!
1201 EndRange: new Date(
2000,
0,
2),
1202 SearchText: 'ComingNext Settings|',
1208 result = calendarService.IDataSource.GetList(listFiltering);
1209 if (result.ErrorCode)
1210 throw(result.ErrorMessage);
1213 error(
"getSettingsCalEntryId: GetList() failed: " + e + ', line ' + e.line);
1216 var list = result.ReturnValue;
1217 var entry = list.getNext();
1218 if (entry != undefined) {
1219 settingsCalEntryId = entry.LocalId;
1220 log(
"settingsCalEntryId=" + settingsCalEntryId);
1222 else { // create settings item
1223 var item = new Object();
1224 item.Type =
"DayEvent";
1225 item.StartTime = new Date(
2000,
0,
1);
1226 item.Summary =
"ComingNext Settings|";
1228 var criteria = new Object();
1229 criteria.Type =
"CalendarEntry";
1230 criteria.Item = item;
1233 var result = calendarService.IDataSource.Add(criteria);
1234 if (result.ErrorCode)
1235 throw(result.ErrorMessage);
1237 error(
"getSettingsCalEntryId: " + e + ', line ' + e.line);
1240 getSettingsCalEntryId();
1245 function restoreDefaultSettings()
1247 for (var key in config)
1248 config[key].Value = config[key].Default;
1251 function loadSettings()
1253 getSettingsCalEntryId();
1254 var listFiltering = {
1255 Type:'CalendarEntry',
1257 LocalId: settingsCalEntryId
1262 result = calendarService.IDataSource.GetList(listFiltering);
1263 if (result.ErrorCode)
1264 throw(result.ErrorMessage);
1267 error(
"loadSettings: GetList() failed: " + e + ', line ' + e.line);
1270 var entry = result.ReturnValue.getNext();
1271 if (entry != undefined) {
1272 log(
"Loading Settings...");
1273 // only reload settings if they chanced since the last reload
1274 if (settingsCache != entry.Summary)
1276 restoreDefaultSettings();
1277 var stringlist = entry.Summary.split(
"|");
1278 // skip the first two entries, those contain header and version info
1279 for(var i =
2; i < stringlist.length -
1; i++) {
1280 var pair = stringlist[i].split('=');
1282 var value = pair[
1];
1283 if (key == null || value == null || config[key] == null) {
1284 log('Warning: unknown or invalid setting: ' + stringlist[i]);
1287 log('stringlist: ' + key + '=\'' + value + '\'');
1288 if (config[key].Type == 'Int') {
1289 config[key].Value = Number(value);
1290 if (isNaN(config[key].Value))
1291 config[key].Value = config[key].Default;
1293 else if (config[key].Type == 'String')
1294 config[key].Value = value;
1295 else if (config[key].Type == 'Bool')
1296 config[key].Value = (value == 'true')
1297 else if (config[key].Type == 'Enum')
1298 config[key].Value = value;
1299 else if (config[key].Type == 'UID') {
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 == 'Array') {
1305 config[key].Value = value.split(
"^");
1306 if (config[key].Value.length ==
1 && config[key].Value[
0] ==
"") {
1307 config[key].Value = [];
1311 settingsCache = entry.Summary;
1315 log(
"Settings already cached and did not change");
1319 error(
"Failed to load settings, calendar entry could not be found");
1323 function saveSettings()
1325 getSettingsCalEntryId();
1326 var item = new Object();
1327 item.Type =
"DayEvent";
1328 item.StartTime = new Date(
2000,
0,
1);
1329 item.LocalId = settingsCalEntryId;
1330 item.Summary =
"ComingNext Settings|" + version +
"|";
1332 for (var key in config) {
1333 if (config[key].Type == 'Int')
1334 item.Summary += key +
"=" + config[key].Value.toString() +
"|";
1335 else if (config[key].Type == 'String')
1336 item.Summary += key +
"=" + config[key].Value +
"|";
1337 else if (config[key].Type == 'Bool')
1338 item.Summary += key +
"=" + (config[key].Value ? 'true' : 'false') +
"|";
1339 else if (config[key].Type == 'Enum')
1340 item.Summary += key +
"=" + config[key].Value +
"|";
1341 else if (config[key].Type == 'UID')
1342 item.Summary += key +
"=" + config[key].Value.toString() +
"|";
1343 else if (config[key].Type == 'Array')
1344 item.Summary += key +
"=" + config[key].Value.join(
"^") +
"|";
1346 settingsCache = item.Summary;
1348 var criteria = new Object();
1349 criteria.Type =
"CalendarEntry";
1350 criteria.Item = item;
1352 log(
"Saving settings to calendar entry: " + item.Summary);
1354 var result = calendarService.IDataSource.Add(criteria);
1355 if (result.ErrorCode)
1356 throw(result.ErrorMessage);
1358 error(
"saveSettings: " + e + ', line ' + e.line);
1361 lastReloadTime = null; // force calendar data reload on next update
1366 function toggleVisibility(elementId)
1368 if (document.getElementById(elementId).style.display ==
"none")
1369 document.getElementById(elementId).style.display =
"block";
1371 document.getElementById(elementId).style.display =
"none";
1375 function printHintBox(text)
1378 return '
<td width=
"1%" align=
"right" onclick=
"javascript:toggleVisibility(\'info' + uniqueId + '\')">' + getLocalizedText('settings.help') + '
</td></tr></table>'+
1379 '
<div class=
"settingsInfo" id=
"info' + uniqueId + '">' + text + '
</div>';
1382 function showAbout()
1386 document.getElementById(
"aboutView").style.display =
"block";
1387 document.onclick = null;
1389 window.menu.setLeftSoftkeyLabel(
" ", function(){});
1390 window.menu.setRightSoftkeyLabel(getLocalizedText('softkey.back'), function()
1396 //document.getElementById(
"aboutView").innerHTML = 'aboutView';
1397 document.getElementById(
"name").innerHTML =
"Coming Next " + version;
1400 function showHelp() {
1401 widget.openURL('http://comingnext.sf.net/help');
1404 function updateFullscreen()
1408 function showFullscreen()
1410 log(
"showFullscreen()");
1412 document.getElementById(
"fullscreenView").style.display =
"block";
1413 document.getElementById('body').className =
"backgroundFullscreen";
1415 document.onclick = launchCalendar;
1420 function getBackgroundImage()
1425 if (config['backgroundImageLocation'].Value == config['backgroundImageLocation'].ValidValues[
0]) // internal
1426 bgImage = 'background_' + orientation + '.png';
1428 bgImage = 'C:/Data/background_' + panelNum + '_' + orientation + '.png';
1432 function updateHomescreen()
1434 if (config['useBackgroundImage'].Value) {
1435 // check if we have a completely unknown screen resolution
1436 var screenHeight = screen.height;
1437 var screenWidth = screen.width;
1438 if (screenHeight !=
640 && screenHeight !=
480 && screenHeight !=
360)
1439 screenHeight =
360; // we can only assume we're in portrait mode, so we set the screen dims as needed for the following code
1440 if (screenWidth !=
640 && screenWidth !=
480 && screenWidth !=
360)
1441 screenWidth =
640; // we can only assume we're in portrait mode, so we set the screen dims as needed for the following code
1443 // check for screen rotation
1444 if (orientation != 'portrait' && ((screenWidth ==
360 && screenHeight ==
640) || (screenWidth ==
640 && screenHeight ==
480))) {
1445 window.widget.prepareForTransition(
"fade");
1446 orientation = 'portrait';
1447 document.getElementById('body').style.backgroundImage = 'url(' + getBackgroundImage() + ')';
1448 document.getElementById('body').style.backgroundColor = 'none';
1449 window.widget.performTransition();
1450 } else if (orientation != 'landscape' && ((screenWidth ==
640 && screenHeight ==
360) || (screenWidth ==
480 && screenHeight ==
640))) {
1451 window.widget.prepareForTransition(
"fade");
1452 orientation = 'landscape';
1453 document.getElementById('body').style.backgroundImage = 'url(' + getBackgroundImage() + ')';
1454 document.getElementById('body').style.backgroundColor = 'none';
1455 window.widget.performTransition();
1457 else if (document.getElementById('body').style.backgroundImage ==
"")
1459 document.getElementById('body').style.backgroundImage = 'url(' + getBackgroundImage() + ')';
1464 function showHomescreen()
1466 log(
"showHomescreen()");
1468 document.getElementById(
"homescreenView").style.display =
"block";
1469 document.getElementById('body').className =
"background";
1470 document.onclick = null;
1474 function getLocalizedText(p_Txt)
1476 if (localizedText[p_Txt])
1477 return localizedText[p_Txt];
1479 return 'ERROR: missing translation for ' + p_Txt;
1482 function showUpdate()
1486 document.getElementById(
"updateView").style.display =
"block";
1487 document.onclick = null;
1489 window.menu.setLeftSoftkeyLabel(getLocalizedText('update.checknow'), function(){
1492 window.menu.setRightSoftkeyLabel(getLocalizedText('softkey.back'), function()
1498 document.getElementById(
"currentVersion").innerHTML = getLocalizedText(
"update.current") + version;
1502 function checkForUpdate()
1504 // asynch XHR to server url
1505 reqV = new XMLHttpRequest();
1506 reqV.onreadystatechange = checkForUpdateCallback;
1507 document.getElementById(
"updateDiv").innerHTML = getLocalizedText(
"update.checking");
1508 reqV.open(
"GET", versionURL, true);
1509 reqV.setRequestHeader(
"If-Modified-Since",
"Sat, 1 Jan 2000 00:00:00 GMT" ); // disable caching
1513 function checkForUpdateCallback()
1515 if (reqV.readyState ==
4) {
1516 if (reqV.status ==
200) {
1517 var resultXml = reqV.responseText;
1519 var div = document.getElementById(
"tmp");
1520 div.innerHTML = resultXml;
1521 var newVersion = div.getElementsByTagName('version')[
0].innerHTML;
1522 var newVersionURL = div.getElementsByTagName('url')[
0].innerHTML;
1524 if (version != newVersion) {
1525 document.getElementById(
"updateDiv").innerHTML = getLocalizedText(
"update.download").replace(/%
1/, newVersion).replace(/%
2/, newVersionURL);
1528 document.getElementById(
"updateDiv").innerHTML = getLocalizedText(
"update.nonewversion");
1533 document.getElementById(
"updateDiv").innerHTML = getLocalizedText(
"update.error") + reqV.status +
" " + reqV.responseText;
1538 function hideViews()
1540 document.getElementById(
"homescreenView").style.display =
"none";
1541 document.getElementById(
"fullscreenView").style.display =
"none";
1542 document.getElementById(
"aboutView").style.display =
"none";
1543 document.getElementById(
"settingsView").style.display =
"none";
1544 document.getElementById(
"updateView").style.display =
"none";
1547 function listCalendars()
1557 DefaultCalendar: false
1561 var calendarsResult = calendarService.IDataSource.GetList(criteria);
1562 if (calendarsResult.ErrorCode !=
0)
1563 throw(
"Error fetching list of calendars: " + calendarsResult.ErrorCode + ': ' + calendarsResult.ErrorMessage);
1564 var calendarListIterator = calendarsResult.ReturnValue;
1569 while (( item = calendarListIterator.getNext()) != undefined ) {
1570 calendars[count++] = item;
1572 log(
"Available Calendars: " + calendars.join(
", "));
1575 error('listing calendars:' + e + ', line ' + e.line);
1580 // Copies all objects and their properties to an array. Data is copied so nothing gets lost when the reference is removed
1581 // 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
1582 function listToArray(list, calendarName)
1584 var array = new Array();
1587 while (( item = list.getNext()) != undefined ) {
1588 var itemCopy = new Object();
1589 for(var i=
0; i < entryFields.length; i++) {
1590 itemCopy[entryFields[i]] = item[entryFields[i]];
1592 // for some reason, the CalendarName property is never correctly queried, so we assign it manually here
1593 if (!itemCopy['CalendarName']) {
1594 itemCopy['CalendarName'] = calendarName;
1596 array.push(itemCopy);
1597 txt += array[array.length -
1].Summary +
", ";
1599 log(
"listToArray(): " + txt);
1603 function sortCalendarEntries(a, b)
1606 log(
"sortCalendarEntries(" + a.Summary +
"," + b.Summary +
")");
1608 if (a.InstanceStartTime != null) {
1609 atime = a.InstanceStartTime;
1611 else if (a.StartTime != null) {
1612 atime = a.StartTime;
1614 else if (a.InstanceEndTime != null) {
1615 atime = a.InstanceEndTime;
1617 else if (a.EndTime != null) {
1621 if (b.InstanceStartTime != null) {
1622 btime = b.InstanceStartTime;
1624 else if (b.StartTime != null) {
1625 btime = b.StartTime;
1627 else if (b.InstanceEndTime != null) {
1628 btime = b.InstanceEndTime;
1630 else if (b.EndTime != null) {
1634 if (atime && btime) {
1636 atime = parseDate(atime);
1637 btime = parseDate(btime);
1639 // sort by date & time
1640 if (atime < btime) {
1643 else if (atime
> btime) {
1647 else if (a.Type != b.Type) {
1648 if (a.Type < b.Type) {
1651 else if (a.Type
> b.Type) {
1655 // sort by description
1656 else if (a.Summary && b.Summary && a.Summary != b.Summary) {
1657 if (a.Summary < b.Summary) {
1660 else if (a.Summary
> b.Summary) {
1665 // NOTE: events my have no date information at all. In that case, we list events without date first
1666 else if (atime && !btime) {
1669 else if (!atime && btime) {
1672 else if (!atime && !btime) {
1674 if (a.Type != b.Type) {
1675 if (a.Type < b.Type) {
1678 else if (a.Type
> b.Type) {
1682 // sort by description
1683 else if (a.Summary && b.Summary && a.Summary != b.Summary) {
1684 if (a.Summary < b.Summary) {
1687 else if (a.Summary
> b.Summary) {
1696 function updateCalendarColors()
1699 calendarColors = [];
1700 if (calendarList.length
> maxColors) {
1701 log(
"updateCalendarColors(): Warning: more calendars than available indicator colors");
1703 for(var i=
0; i < calendarList.length; i++) {
1704 calendarColors[calendarList[i]] = (i % maxColors) +
1;
1708 function log(message)
1710 if (config['enableLogging'].Value) {
1711 console.info(message);
1717 <style type=
"text/css">
1719 table { margin:
0px; padding:
0px; border-spacing:
0px; border-collapse: collapse; }
1720 td { padding:
0px
5px
0px
0px; white-space:nowrap; overflow:hidden; margin:
0px; }
1721 hr { color:#ffffff; background-color:#ffffff; height:
1px; text-align:left; border-style:none; }
1722 .settingsInfo { display:none; font-style:italic; }
1723 .title { font-weight:bold; font-size:
14pt; }
1724 .textInput { width:
90%; }
1725 .credits { margin-left:
40px; text-indent: -
20px; margin-bottom:
0px; }
1726 #homescreenView { width:
312px; height:
82px; overflow:hidden; }
1727 #calendarList { position:absolute; left:
5px; top:
0px; width:
307px; height:
82px; overflow:hidden; }
1728 #name { text-align:center; }
1729 #appicon { display: block; margin-left: auto; margin-right: auto; margin-top:
10px; }
1730 #smallappicon { width:
22px; height:
22px; margin-right:
10px; float:left; }
1735 <body onload=
"javascript:setTimeout('init()', 10)" onresize=
"javascript:updateScreen()" id=
"body" class=
"background">
1736 <div id=
"homescreenView">
1737 <div id=
"calendarList">loading...
</div>
1739 <div id=
"fullscreenView" style=
"display:none;">
1740 <img src=
"Icon.png" id=
"smallappicon">
1741 <h1 class=
"title">Coming Next
</h1>
1743 <div id=
"fullscreenCalendarList">loading...
</div>
1745 <div id=
"settingsView" style=
"display:none">
1746 <img src=
"Icon.png" id=
"smallappicon">
1747 <h1 id=
"settingsTitle" class=
"title">Settings
</h1>
1749 <div id=
"settingsList"></div>
1751 <div id=
"aboutView" style=
"display:none">
1752 <img src=
"Icon.png" id=
"appicon">
1753 <h1 id=
"name">Coming Next
</h1>
1755 <p>Created by Dr. Cochambre and Michael Prager.
</p>
1756 <p>Contributions:
</p>
1757 <p class=
"credits">Paul Moore (bug fixes, new features and code cleanup)
</p>
1758 <p class=
"credits">Manfred Hanselmann (DST support)
</p>
1759 <p class=
"credits">Christophe Milsent (translation support & french translation)
</p>
1760 <p class=
"credits">Flavio Nathan (portuguese-brazilian translation)
</p>
1761 <p class=
"credits">Tokeda (russian translation)
</p>
1762 <p class=
"credits">Marcella Ferrari (italian translation)
</p>
1763 <p class=
"credits">Venos (italian translation)
</p>
1764 <p>This software is open source and licensed under the GPLv3.
</p>
1765 <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>
1768 <div id=
"updateView" style=
"display:none">
1769 <img src=
"Icon.png" id=
"smallappicon">
1770 <h1 class=
"title">Check for update
</h1>
1772 <div id=
"currentVersion">Coming Next ??
</div>
1773 <div id=
"updateDiv"></div>
1774 <div id=
"tmp" style=
"display:none;"></div>