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"></script>
30 <script type=
"text/javascript" src=
"../debug.js" charset=
"utf-8"></script>
31 <script type=
"text/javascript">
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(): mode=' + mode + ', window.innerHeight=' + window.innerHeight);
965 // check if opening fullscreen
967 // Note: according to Nokia's documentation, an innerHeight of
>91 is an indicator for fullscreen view.
968 // However a bug in E6's firmware causes different window widths and heights (disabled compatibility scaling).
969 // So far, values of
104 and
115 for window.innerHeight were reported, we use a safty margin here and check
971 if( window.innerHeight
> 150 && mode ==
0) {
973 cacheEntriesHtml = '';
974 document.getElementById('body').style.backgroundImage =
"";
977 else if (window.innerHeight <=
150 && mode !=
0) {
979 cacheEntriesHtml = '';
984 updateHomescreen(); // check for screen rotation
989 function handleOnShow()
993 var time = new Date();
994 if (time.getTime() - lastUpdateTime.getTime()
> config['updateDataInterval'].Value *
60 *
1000) {
995 log('updateScreen(): force updateData() because last update was too long ago (' + (time.getTime() - lastUpdateTime.getTime()) /
1000 + 's)');
998 setUpdateTimer(); // reinitialize update timer
1002 function launchCalendar()
1005 widget.openApplication(config['calendarApp'].Value,
"");
1006 if (config['hideWidgetOnCalendarOpen'].Value)
1009 error('starting Calendar App');
1016 log('New widget instance starting up...');
1019 // call calendar service
1020 if (device !=
"undefined")
1021 calendarService = device.getServiceObject(
"Service.Calendar",
"IDataSource");
1023 throw('device object does not exist');
1025 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>');
1029 calendarList = listCalendars();
1031 updateCalendarColors();
1034 requestNotification();
1035 document.getElementById(
"settingsTitle").innerHTML = getLocalizedText('menu.settings');
1037 if (window.innerHeight
> 91) {
1038 mode =
0; // we're starting fullscreen, we set mode to homescreen in order to let updateScreen() do all the work for us
1043 log(
"init(): updateScreen()");
1045 if (config['useBackgroundImage'].Value)
1046 // check for screen rotation every
1 secs
1047 screenRotationTimer = window.setInterval('checkOrientation()',
1000 *
1);
1049 // call updateScreen() when widget changes from background to forground
1050 window.widget.onshow = handleOnShow;
1052 log(
"init(): finished...");
1054 statupSuccessful = true;
1057 function checkOrientation()
1061 updateHomescreen(); // check for screen rotation
1064 function setUpdateTimer()
1066 updateTimer = window.setInterval('updateTimerCallback()',
1000 *
60 * config['updateDataInterval'].Value);
1069 function clearUpdateTimer()
1071 window.clearInterval(updateTimer);
1074 function updateTimerCallback()
1076 log(
"updateTimerCallback()");
1080 function createMenu()
1082 window.menu.setLeftSoftkeyLabel(
"",null);
1083 window.menu.setRightSoftkeyLabel(
"",null);
1085 var menuSettings = new MenuItem(getLocalizedText('menu.settings'), id++);
1086 var menuCallApp = new MenuItem(getLocalizedText('menu.openCalendarApp'), id++);
1087 var menuHelp = new MenuItem(getLocalizedText('menu.help'), id++);
1088 var menuUpdate = new MenuItem(getLocalizedText('menu.update'), id++);
1089 var menuAbout = new MenuItem(getLocalizedText('menu.about'), id++);
1090 menuSettings.onSelect = showSettings;
1091 menuAbout.onSelect = showAbout;
1092 menuCallApp.onSelect = launchCalendar;
1093 menuUpdate.onSelect = showUpdate;
1094 menuHelp.onSelect = showHelp;
1095 window.menu.clear();
1096 window.menu.append(menuCallApp);
1097 window.menu.append(menuSettings);
1098 window.menu.append(menuHelp);
1099 window.menu.append(menuUpdate);
1100 window.menu.append(menuAbout);
1103 function showSettings()
1107 document.getElementById(
"settingsView").style.display =
"block";
1108 document.onclick = null;
1110 window.menu.setLeftSoftkeyLabel(getLocalizedText('settings.save'), function()
1112 for (var key in config) {
1113 if (config[key].Type == 'String')
1114 config[key].Value = document.forms[
0].elements[
"settings." + key].value;
1115 else if (config[key].Type == 'Int') {
1116 config[key].Value = parseInt(document.forms[
0].elements[
"settings." + key].value);
1117 if (config[key].Value <
0 || isNaN(config[key].Value))
1118 config[key].Value = config[key].Default;
1120 else if (config[key].Type == 'Bool')
1121 config[key].Value = document.forms[
0].elements[
"settings." + key].checked;
1122 else if (config[key].Type == 'UID') {
1123 config[key].Value = parseInt(document.forms[
0].elements[
"settings." + key].value);
1124 if (isNaN(config[key].Value))
1125 config[key].Value = config[key].Default;
1127 else if (config[key].Type == 'Enum') {
1128 config[key].Value = document.forms[
0].elements[
"settings." + key].value;
1129 if (config[key].ValidValues.indexOf(config[key].Value) == -
1)
1130 config[key].Value = config[key].Default;
1132 else if (config[key].Type == 'Array') {
1133 if (key == 'excludedCalendars') {
1134 config[key].Value = new Array();
1135 for(var i=
0; i < calendarList.length; i++) {
1136 var element = document.forms[
0].elements[
"settings." + key +
"." + calendarList[i]];
1137 if (element != null && element.checked == false)
1138 config[key].Value.push(calendarList[i]);
1151 window.menu.setRightSoftkeyLabel(getLocalizedText('settings.cancel'), function()
1157 var settingsHtml = '
<form>';
1158 for (var key in config) {
1159 if (config[key].Type == 'String') {
1161 if (key.substring(
0,
9) ==
"cssStyle_")
1162 prefix = getLocalizedText('settings.cssStyle_prefix');
1163 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 />';
1165 else if (config[key].Type == 'Int')
1166 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 />';
1167 else if (config[key].Type == 'Bool')
1168 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 />';
1169 else if (config[key].Type == 'UID')
1170 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 />';
1171 else if (config[key].Type == 'Enum') {
1172 settingsHtml += '
<table><tr><td>' + getLocalizedText('settings.name.' + key) + '
<br /><select name=
"settings.' + key + '" size=
"1">';
1173 for(var i =
0; i < config[key].ValidValues.length; i++)
1174 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>';
1175 settingsHtml += '
</select></div></td>' + printHintBox(getLocalizedText('settings.info.' + key)) + '
<hr />';
1177 else if (config[key].Type == 'Array') {
1178 settingsHtml += '
<table><tr><td>' + getLocalizedText('settings.name.' + key) + '
<br />';
1179 if (key == 'excludedCalendars') {
1180 for(var i=
0; i < calendarList.length; i++) {
1181 var checked = '
checked=
"checked"';
1182 if (config[key].Value.indexOf(calendarList[i]) != -
1)
1184 settingsHtml += '
<input name=
"settings.' + key + '.' + calendarList[i] + '" type=
"checkbox" value=
"' + calendarList[i] + '" ' + checked + '
/> ' + calendarList[i] + '
<br />';
1187 settingsHtml += '
</td>' + printHintBox(getLocalizedText('settings.info.' + key)) + '
<hr />';
1190 settingsHtml += '
<input name=
"reset" type=
"button" value=
"' + getLocalizedText('settings.restoreDefaults') + '" onclick=
"javascript:restoreDefaultSettings();showSettings();" />';
1191 settingsHtml += '
</form>';
1192 document.getElementById(
"settingsList").innerHTML = settingsHtml;
1195 function changeCssClass(classname, properties)
1197 for(var i =
0; i < document.styleSheets[
0]['cssRules'].length; i++)
1199 if (document.styleSheets[
0]['cssRules'][i].selectorText == classname) {
1200 document.styleSheets[
0].deleteRule(i);
1201 document.styleSheets[
0].insertRule(classname + ' { ' + properties + ' }', document.styleSheets[
0]['cssRules'].length);
1207 function updateCssClasses()
1209 for(var key in config) {
1210 changeCssClass(getLocalizedText('settings.name.' + key), config[key].Value);
1214 function getSettingsCalEntryId()
1216 if (settingsCalEntryId == null) {
1217 // check if entry already exists
1218 var listFiltering = {
1219 Type:'CalendarEntry',
1221 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!
1222 EndRange: new Date(
2000,
0,
2),
1223 SearchText: 'ComingNext Settings|',
1229 result = calendarService.IDataSource.GetList(listFiltering);
1230 if (result.ErrorCode)
1231 throw(result.ErrorMessage);
1234 error(
"getSettingsCalEntryId: GetList() failed: " + e + ', line ' + e.line);
1237 var list = result.ReturnValue;
1238 var entry = list.getNext();
1239 if (entry != undefined) {
1240 settingsCalEntryId = entry.LocalId;
1241 log(
"settingsCalEntryId=" + settingsCalEntryId);
1243 else { // create settings item
1244 var item = new Object();
1245 item.Type =
"DayEvent";
1246 item.StartTime = new Date(
2000,
0,
1);
1247 item.Summary =
"ComingNext Settings|";
1249 var criteria = new Object();
1250 criteria.Type =
"CalendarEntry";
1251 criteria.Item = item;
1254 var result = calendarService.IDataSource.Add(criteria);
1255 if (result.ErrorCode)
1256 throw(result.ErrorMessage);
1258 error(
"getSettingsCalEntryId: " + e + ', line ' + e.line);
1261 getSettingsCalEntryId();
1266 function restoreDefaultSettings()
1268 for (var key in config)
1269 config[key].Value = config[key].Default;
1272 function loadSettings()
1274 getSettingsCalEntryId();
1275 var listFiltering = {
1276 Type:'CalendarEntry',
1278 LocalId: settingsCalEntryId
1283 result = calendarService.IDataSource.GetList(listFiltering);
1284 if (result.ErrorCode)
1285 throw(result.ErrorMessage);
1288 error(
"loadSettings: GetList() failed: " + e + ', line ' + e.line);
1291 var entry = result.ReturnValue.getNext();
1292 if (entry != undefined) {
1293 log(
"Loading Settings...");
1294 // only reload settings if they chanced since the last reload
1295 if (settingsCache != entry.Summary)
1297 restoreDefaultSettings();
1298 var stringlist = entry.Summary.split(
"|");
1299 // skip the first two entries, those contain header and version info
1300 for(var i =
2; i < stringlist.length -
1; i++) {
1301 var pair = stringlist[i].split('=');
1303 var value = pair[
1];
1304 if (key == null || value == null || config[key] == null) {
1305 log('Warning: unknown or invalid setting: ' + stringlist[i]);
1308 log('stringlist[' + i + ']: ' + key + '=\'' + value + '\'');
1309 if (config[key].Type == 'Int') {
1310 config[key].Value = Number(value);
1311 if (isNaN(config[key].Value))
1312 config[key].Value = config[key].Default;
1314 else if (config[key].Type == 'String')
1315 config[key].Value = value;
1316 else if (config[key].Type == 'Bool')
1317 config[key].Value = (value == 'true')
1318 else if (config[key].Type == 'Enum')
1319 config[key].Value = value;
1320 else if (config[key].Type == 'UID') {
1321 config[key].Value = Number(value);
1322 if (isNaN(config[key].Value))
1323 config[key].Value = config[key].Default;
1325 else if (config[key].Type == 'Array') {
1326 config[key].Value = value.split(
"^");
1327 if (config[key].Value.length ==
1 && config[key].Value[
0] ==
"") {
1328 config[key].Value = [];
1332 settingsCache = entry.Summary;
1336 log(
"Settings already cached and did not change");
1340 error(
"Failed to load settings, calendar entry could not be found");
1344 function saveSettings()
1346 getSettingsCalEntryId();
1347 var item = new Object();
1348 item.Type =
"DayEvent";
1349 item.StartTime = new Date(
2000,
0,
1);
1350 item.LocalId = settingsCalEntryId;
1351 item.Summary =
"ComingNext Settings|" + version +
"|";
1353 for (var key in config) {
1354 if (config[key].Type == 'Int')
1355 item.Summary += key +
"=" + config[key].Value.toString() +
"|";
1356 else if (config[key].Type == 'String')
1357 item.Summary += key +
"=" + config[key].Value +
"|";
1358 else if (config[key].Type == 'Bool')
1359 item.Summary += key +
"=" + (config[key].Value ? 'true' : 'false') +
"|";
1360 else if (config[key].Type == 'Enum')
1361 item.Summary += key +
"=" + config[key].Value +
"|";
1362 else if (config[key].Type == 'UID')
1363 item.Summary += key +
"=" + config[key].Value.toString() +
"|";
1364 else if (config[key].Type == 'Array')
1365 item.Summary += key +
"=" + config[key].Value.join(
"^") +
"|";
1367 settingsCache = item.Summary;
1369 var criteria = new Object();
1370 criteria.Type =
"CalendarEntry";
1371 criteria.Item = item;
1373 log(
"Saving settings to calendar entry: " + item.Summary);
1375 var result = calendarService.IDataSource.Add(criteria);
1376 if (result.ErrorCode)
1377 throw(result.ErrorMessage);
1379 error(
"saveSettings: " + e + ', line ' + e.line);
1382 lastReloadTime = null; // force calendar data reload on next update
1387 function toggleVisibility(elementId)
1389 if (document.getElementById(elementId).style.display ==
"none")
1390 document.getElementById(elementId).style.display =
"block";
1392 document.getElementById(elementId).style.display =
"none";
1396 function printHintBox(text)
1399 return '
<td width=
"1%" align=
"right" onclick=
"javascript:toggleVisibility(\'info' + uniqueId + '\')">' + getLocalizedText('settings.help') + '
</td></tr></table>'+
1400 '
<div class=
"settingsInfo" id=
"info' + uniqueId + '" style=
"display:none">' + text + '
</div>';
1403 function showAbout()
1407 document.getElementById(
"aboutView").style.display =
"block";
1408 document.onclick = null;
1410 window.menu.setLeftSoftkeyLabel(
" ", function(){});
1411 window.menu.setRightSoftkeyLabel(getLocalizedText('softkey.back'), function()
1417 //document.getElementById(
"aboutView").innerHTML = 'aboutView';
1418 document.getElementById(
"name").innerHTML =
"Coming Next " + version;
1421 function showHelp() {
1422 widget.openURL('http://comingnext.sf.net/help');
1425 function updateFullscreen()
1429 function showFullscreen()
1431 log(
"showFullscreen()");
1433 document.getElementById(
"fullscreenView").style.display =
"block";
1434 document.getElementById('body').className =
"backgroundFullscreen";
1436 document.onclick = launchCalendar;
1441 function getBackgroundImage()
1446 if (config['backgroundImageLocation'].Value == config['backgroundImageLocation'].ValidValues[
0]) // internal
1447 bgImage = 'background_' + orientation + '.png';
1449 bgImage = 'C:/Data/background_' + panelNum + '_' + orientation + '.png';
1453 function updateHomescreen()
1455 if (config['useBackgroundImage'].Value) {
1456 // check if we have a completely unknown screen resolution
1457 var screenHeight = screen.height;
1458 var screenWidth = screen.width;
1459 if (screenHeight !=
640 && screenHeight !=
480 && screenHeight !=
360)
1460 screenHeight =
360; // we can only assume we're in portrait mode, so we set the screen dims as needed for the following code
1461 if (screenWidth !=
640 && screenWidth !=
480 && screenWidth !=
360)
1462 screenWidth =
640; // we can only assume we're in portrait mode, so we set the screen dims as needed for the following code
1464 // check for screen rotation
1465 if (orientation != 'portrait' && ((screenWidth ==
360 && screenHeight ==
640) || (screenWidth ==
640 && screenHeight ==
480))) {
1466 window.widget.prepareForTransition(
"fade");
1467 orientation = 'portrait';
1468 document.getElementById('body').style.backgroundImage = 'url(' + getBackgroundImage() + ')';
1469 document.getElementById('body').style.backgroundColor = 'none';
1470 window.widget.performTransition();
1471 } else if (orientation != 'landscape' && ((screenWidth ==
640 && screenHeight ==
360) || (screenWidth ==
480 && screenHeight ==
640))) {
1472 window.widget.prepareForTransition(
"fade");
1473 orientation = 'landscape';
1474 document.getElementById('body').style.backgroundImage = 'url(' + getBackgroundImage() + ')';
1475 document.getElementById('body').style.backgroundColor = 'none';
1476 window.widget.performTransition();
1478 else if (document.getElementById('body').style.backgroundImage ==
"")
1480 document.getElementById('body').style.backgroundImage = 'url(' + getBackgroundImage() + ')';
1485 function showHomescreen()
1487 log(
"showHomescreen()");
1489 document.getElementById(
"homescreenView").style.display =
"block";
1490 document.getElementById('body').className =
"background";
1491 document.onclick = null;
1495 function getLocalizedText(p_Txt)
1497 if (localizedText[p_Txt])
1498 return localizedText[p_Txt];
1500 return 'ERROR: missing translation for ' + p_Txt;
1503 function showUpdate()
1507 document.getElementById(
"updateView").style.display =
"block";
1508 document.onclick = null;
1510 window.menu.setLeftSoftkeyLabel(getLocalizedText('update.checknow'), function(){
1513 window.menu.setRightSoftkeyLabel(getLocalizedText('softkey.back'), function()
1519 document.getElementById(
"currentVersion").innerHTML = getLocalizedText(
"update.current") + version;
1523 function checkForUpdate()
1525 // asynch XHR to server url
1526 reqV = new XMLHttpRequest();
1527 reqV.onreadystatechange = checkForUpdateCallback;
1528 document.getElementById(
"updateDiv").innerHTML = getLocalizedText(
"update.checking");
1529 reqV.open(
"GET", versionURL, true);
1533 function checkForUpdateCallback()
1535 if (reqV.readyState ==
4) {
1536 if (reqV.status ==
200) {
1537 var resultXml = reqV.responseText;
1539 var div = document.getElementById(
"tmp");
1540 div.innerHTML = resultXml;
1541 var newVersion = div.getElementsByTagName('version')[
0].innerHTML;
1542 var newVersionURL = div.getElementsByTagName('url')[
0].innerHTML;
1544 if (version != newVersion) {
1545 document.getElementById(
"updateDiv").innerHTML = getLocalizedText(
"update.download").replace(/%
1/, newVersion).replace(/%
2/, newVersionURL);
1548 document.getElementById(
"updateDiv").innerHTML = getLocalizedText(
"update.nonewversion");
1553 document.getElementById(
"updateDiv").innerHTML = getLocalizedText(
"update.error") + reqV.status +
" " + reqV.responseText;
1558 function hideViews()
1560 document.getElementById(
"homescreenView").style.display =
"none";
1561 document.getElementById(
"fullscreenView").style.display =
"none";
1562 document.getElementById(
"aboutView").style.display =
"none";
1563 document.getElementById(
"settingsView").style.display =
"none";
1564 document.getElementById(
"updateView").style.display =
"none";
1567 function listCalendars()
1577 DefaultCalendar: false
1581 var calendarsResult = calendarService.IDataSource.GetList(criteria);
1582 if (calendarsResult.ErrorCode !=
0)
1583 throw(
"Error fetching list of calendars: " + calendarsResult.ErrorCode + ': ' + calendarsResult.ErrorMessage);
1584 var calendarListIterator = calendarsResult.ReturnValue;
1589 while (( item = calendarListIterator.getNext()) != undefined ) {
1590 calendars[count++] = item;
1592 log(
"Available Calendars: " + calendars.join(
", "));
1595 error('listing calendars:' + e + ', line ' + e.line);
1600 // Copies all objects and their properties to an array. Data is copied so nothing gets lost when the reference is removed
1601 // 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
1602 function listToArray(list, calendarName)
1604 var array = new Array();
1607 while (( item = list.getNext()) != undefined ) {
1608 var itemCopy = new Object();
1609 for(var i=
0; i < entryFields.length; i++) {
1610 itemCopy[entryFields[i]] = item[entryFields[i]];
1612 // for some reason, the CalendarName property is never correctly queried, so we assign it manually here
1613 if (!itemCopy['CalendarName']) {
1614 itemCopy['CalendarName'] = calendarName;
1616 array.push(itemCopy);
1617 txt += array[array.length -
1].Summary +
", ";
1619 log(
"listToArray(): " + txt);
1623 function sortCalendarEntries(a, b)
1626 log(
"sortCalendarEntries(" + a.Summary +
"," + b.Summary +
")");
1628 if (a.InstanceStartTime != null) {
1629 atime = a.InstanceStartTime;
1631 else if (a.StartTime != null) {
1632 atime = a.StartTime;
1634 else if (a.InstanceEndTime != null) {
1635 atime = a.InstanceEndTime;
1637 else if (a.EndTime != null) {
1641 if (b.InstanceStartTime != null) {
1642 btime = b.InstanceStartTime;
1644 else if (b.StartTime != null) {
1645 btime = b.StartTime;
1647 else if (b.InstanceEndTime != null) {
1648 btime = b.InstanceEndTime;
1650 else if (b.EndTime != null) {
1654 if (atime && btime) {
1656 atime = parseDate(atime);
1657 btime = parseDate(btime);
1659 // sort by date & time
1660 if (atime < btime) {
1663 else if (atime
> btime) {
1667 else if (a.Type != b.Type) {
1668 if (a.Type < b.Type) {
1671 else if (a.Type
> b.Type) {
1675 // sort by description
1676 else if (a.Summary && b.Summary && a.Summary != b.Summary) {
1677 if (a.Summary < b.Summary) {
1680 else if (a.Summary
> b.Summary) {
1685 // NOTE: events my have no date information at all. In that case, we list events without date first
1686 else if (atime && !btime) {
1689 else if (!atime && btime) {
1692 else if (!atime && !btime) {
1694 if (a.Type != b.Type) {
1695 if (a.Type < b.Type) {
1698 else if (a.Type
> b.Type) {
1702 // sort by description
1703 else if (a.Summary && b.Summary && a.Summary != b.Summary) {
1704 if (a.Summary < b.Summary) {
1707 else if (a.Summary
> b.Summary) {
1716 function updateCalendarColors()
1719 calendarColors = [];
1720 if (calendarList.length
> maxColors) {
1721 log(
"updateCalendarColors(): Warning: more calendars than available indicator colors");
1723 for(var i=
0; i < calendarList.length; i++) {
1724 calendarColors[calendarList[i]] = (i % maxColors) +
1;
1728 function log(message)
1730 if (config['enableLogging'].Value) {
1731 console.info(message);
1737 <style type=
"text/css">
1739 table { margin:
0px; padding:
0px; border-spacing:
0px; border-collapse: collapse; }
1740 td { padding:
0px
5px
0px
0px; white-space:nowrap; overflow:hidden; margin:
0px; }
1741 hr { color:#ffffff; background-color:#ffffff; height:
1px; text-align:left; border-style:none; }
1742 .settingsInfo { display:none; font-style:italic; }
1743 .title { font-weight:bold; font-size:
14pt; }
1744 .textInput { width:
90%; }
1745 .credits { margin-left:
40px; text-indent: -
20px; margin-bottom:
0px; }
1746 #homescreenView { width:
312px; height:
82px; overflow:hidden; }
1747 #calendarList { position:absolute; left:
5px; top:
0px; width:
307px; height:
82px; overflow:hidden; }
1748 #name { text-align:center; }
1749 #appicon { display: block; margin-left: auto; margin-right: auto; margin-top:
10px; }
1750 #smallappicon { width:
22px; height:
22px; margin-right:
10px; float:left; }
1755 <body onload=
"javascript:setTimeout('init()', 10)" onresize=
"javascript:updateScreen()" id=
"body" class=
"background">
1756 <div id=
"homescreenView">
1757 <div id=
"calendarList">loading...
</div>
1759 <div id=
"fullscreenView" style=
"display:none;">
1760 <img src=
"Icon.png" id=
"smallappicon">
1761 <h1 class=
"title">Coming Next
</h1>
1763 <div id=
"fullscreenCalendarList">loading...
</div>
1765 <div id=
"settingsView" style=
"display:none">
1766 <img src=
"Icon.png" id=
"smallappicon">
1767 <h1 id=
"settingsTitle" class=
"title">Settings
</h1>
1769 <div id=
"settingsList"></div>
1771 <div id=
"aboutView" style=
"display:none">
1772 <img src=
"Icon.png" id=
"appicon">
1773 <h1 id=
"name">Coming Next
</h1>
1775 <p>Created by Dr. Cochambre and Michael Prager.
</p>
1776 <p>Contributions:
</p>
1777 <p class=
"credits">Paul Moore (bug fixes, new features and code cleanup)
</p>
1778 <p class=
"credits">Manfred Hanselmann (DST support)
</p>
1779 <p class=
"credits">Christophe Milsent (translation support & French translation)
</p>
1780 <p class=
"credits">Flavio Nathan (Portuguese-Brazilian translation)
</p>
1781 <p class=
"credits">Tokeda (Russian translation)
</p>
1782 <p class=
"credits">Marcella Ferrari (Italian translation)
</p>
1783 <p class=
"credits">Venos (Italian translation)
</p>
1784 <p class=
"credits">Francisco Rodero (Catalan translation)
</p>
1785 <p class=
"credits">zbigzbig20 (Polish translation)
</p>
1786 <p class=
"credits">Streamkeskus (Finnish translation)
</p>
1787 <p class=
"credits">renek (Czech translation)
</p>
1788 <p>This software is open source and licensed under the GPLv3.
</p>
1789 <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>
1792 <div id=
"updateView" style=
"display:none">
1793 <img src=
"Icon.png" id=
"smallappicon">
1794 <h1 class=
"title">Check for update
</h1>
1796 <div id=
"currentVersion">Coming Next ??
</div>
1797 <div id=
"updateDiv"></div>
1798 <div id=
"tmp" style=
"display:none;"></div>