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 fontsize: { Type: 'Enum', Default: 'auto', Value: 'auto', ValidValues: ['auto', '
8', '
9', '
10', '
11', '
12', '
13', '
14', '
15', '
16', '
17', '
18', '
19', '
20', '
21', '
22', '
23', '
24', '
25', '
26', '
27', '
28'],},
35 eventsPerWidget: { Type: 'Int', Default:
4, Value:
4,},
36 monthRange: { Type: 'Int', Default:
2, Value:
2,},
37 includeTodos: { Type: 'Bool', Default: true, Value: true,},
38 useBackgroundImage: { Type: 'Bool', Default: true, Value: true,},
39 backgroundImageLocation: { Type: 'Enum', Default: 'internal', Value: 'internal', ValidValues: ['internal', 'external']},
40 showCombinedDateTime: { Type: 'Bool', Default: false, Value: false,},
41 showLocation: { Type: 'Bool', Default: true, Value: true,},
42 showIcons: { Type: 'Bool', Default: true, Value: true,},
43 showTodayAsText: { Type: 'Bool', Default: true, Value: true,},
44 todayText: { Type: 'String', Default: getLocalizedText('settings.default.todayText'), Value: getLocalizedText('settings.default.todayText'),},
45 tomorrowText: { Type: 'String', Default: getLocalizedText('settings.default.tomorrowText'), Value: getLocalizedText('settings.default.tomorrowText'),},
46 showNowAsText: { Type: 'Bool', Default: true, Value: true,},
47 nowText: { Type: 'String', Default: getLocalizedText('settings.default.nowText'), Value: getLocalizedText('settings.default.nowText'),},
48 markOverdueTodos: { Type: 'Bool', Default: true, Value: true,},
49 overdueText: {Type: 'String', Default: getLocalizedText('settings.default.overdueText'), Value: getLocalizedText('settings.default.overdueText'),},
50 dateSeparator: { Type: 'String', Default: getLocalizedText('settings.default.dateSeparator'), Value: getLocalizedText('settings.default.dateSeparator'),},
51 dateFormat: { Type: 'Enum', Default: 'auto', Value: 'auto', ValidValues: ['auto', 'DDMM', 'MMDD'],},
52 weekDayLength: { Type: 'Int', Default:
2, Value:
2,},
53 updateDataInterval: { Type: 'Int', Default:
5, Value:
5,},
54 calendarApp: { Type: 'UID', Default:
0x10005901, Value:
0x10005901,},
55 showNothingText: { Type: 'Bool', Default: true, Value: true,},
56 nothingText: { Type: 'String', Default: getLocalizedText('settings.default.nothingText'), Value: getLocalizedText('settings.default.nothingText'),},
57 enableDaylightSaving: { Type: 'Bool', Default: true, Value: true,},
58 daylightSavingOffset: { Type: 'Int', Default:
1, Value:
1,},
59 hideWidgetOnCalendarOpen: { Type: 'Bool', Default: false, Value: false,},
60 showCalendarIndicator: { Type: 'Bool', Default: true, Value: true,},
61 excludedCalendars: { Type: 'Array', Default: [], Value: [],},
62 enableLogging: { Type: 'Bool', Default: false, Value: false,},
63 cssStyle_background: { Type: 'String', Default: 'color:#ffffff; background-color:#
000000', Value: 'color:#ffffff; background-color:#
000000',},
64 cssStyle_backgroundFullscreen: { Type: 'String', Default: 'color:#ffffff; background-color:#
000000', Value: 'color:#ffffff; background-color:#
000000',},
65 cssStyle_weekDay: { Type: 'String', Default: '', Value: '',},
66 cssStyle_date: { Type: 'String', Default: '', Value: '',},
67 cssStyle_today: { Type: 'String', Default: 'color:#ff0000', Value: 'color:#ff0000',},
68 cssStyle_tomorrow: { Type: 'String', Default: 'color:#
0000ff', Value: 'color:#
0000ff',},
69 cssStyle_time: { Type: 'String', Default: '', Value: '',},
70 cssStyle_now: { Type: 'String', Default: 'color:#ff00ff', Value: 'color:#ff00ff',},
71 cssStyle_description: { Type: 'String', Default: '', Value: '',},
72 cssStyle_icon: { Type: 'String', Default: '', Value: '',},
73 cssStyle_overdue: { Type: 'String', Default: 'color:#ffff00', Value: 'color:#ffff00',},
74 cssStyle_calendar1: { Type: 'String', Default: 'background-color:#
0757cf', Value: 'background-color:#
0757cf',},
75 cssStyle_calendar2: { Type: 'String', Default: 'background-color:#
579f37', Value: 'background-color:#
579f37',},
76 cssStyle_calendar3: { Type: 'String', Default: 'background-color:#ff9f07', Value: 'background-color:#ff9f07',},
77 cssStyle_calendar4: { Type: 'String', Default: 'background-color:#af8fef', Value: 'background-color:#af8fef',},
78 cssStyle_calendar5: { Type: 'String', Default: 'background-color:#
57afbf', Value: 'background-color:#
57afbf',},
79 cssStyle_calendar6: { Type: 'String', Default: 'background-color:#
9fdf57', Value: 'background-color:#
9fdf57',},
84 //-------------------------------------------------------
85 // Nothing of interest from here on...
86 //-------------------------------------------------------
87 var panelNum =
0; // use
1 for second panel
89 var versionURL =
"http://comingnext.sourceforge.net/version.xml";
90 var calendarService = null;
91 var cacheEntriesHtml = [];
92 var months_translated = [];
93 var weekdays_translated = [];
96 var mode =
0; //
0 = homescreen,
1 = fullscreen,
2 = settings,
3 = about,
4 = check for update
98 var settingsCalEntryId = null;
99 var settingsCache = null;
100 var notificationRequests = new Array();
101 var calendarList = [];
102 var calendarColors = [];
103 var updateTimer = null;
104 var screenRotationTimer = null;
105 var lastUpdateTime = now; // last time we updated the display
106 var lastReloadTime = null; // last time we fetched calendar data
107 var reloadInterval =
6 *
60 *
60 *
1000; // =
6 hours; time interval for reloading calendar data
108 var errorOccured = false;
109 var entryLists = null; // stores all fetched calendar entries until data is refreshed
110 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.
111 var use12hoursTimeFormat = false; // defines how time should be formated:
19:
00 or
07:
00 pm
112 var timeFormatSeparator =
":"; // format time
19:
00 or
19.00 depending on system setting
113 var defaultFontSize = null; // default browser font size will be set by init
115 // vars for daylight saving time
116 var summertime = false; // true, if current date is in summer, false if in winter
117 var daylightSavingDates = new Object(); // caches calculated DST winter and summer time shift dates
119 // this is a list of data fields a calendar event can have
133 function isLeapYear( year ) {
134 if (( year %
4 ==
0 && year %
100 !=
0 ) || year %
400 ==
0 )
140 function calcLeapYear(year, days)
142 if (isLeapYear(year))
148 function subToSunday(myDate, year, days, prevMonthDays)
150 for (i = myDate.getDay(); i
> 0 ;i--)
152 days -= prevMonthDays;
153 days = isLeapYear(year) ? --days : days;
157 function isSummertime(curDate)
162 // if we already calculated DST summer and winter time dates for this year, use cached values
163 var dst = daylightSavingDates[curDate.getFullYear()];
165 var thisYearS = new Date(curDate.getFullYear(),
3,
0,
0,
0,
0 );
166 var thisYearW = new Date(curDate.getFullYear(),
10,
0,
0,
0,
0 );
167 var nextYearS = new Date(curDate.getFullYear() +
1,
3,
0,
0,
0,
0 );
168 var nextYearW = new Date(curDate.getFullYear() +
1,
10,
0,
0,
0,
0 );
170 thisYearSDays = nextYearSDays =
90;
171 thisYearWDays = nextYearWDays =
304;
173 thisYearSDays = calcLeapYear(curDate.getFullYear(), thisYearSDays);
174 thisYearWDays = calcLeapYear(curDate.getFullYear(), thisYearWDays);
175 nextYearSDays = calcLeapYear(curDate.getFullYear() +
1, nextYearSDays);
176 nextYearWDays = calcLeapYear(curDate.getFullYear() +
1, nextYearWDays);
178 thisYearSDays = subToSunday(thisYearS, curDate.getFullYear(), thisYearSDays,
59);
179 thisYearWDays = subToSunday(thisYearW, curDate.getFullYear(), thisYearWDays,
273);
180 nextYearSDays = subToSunday(nextYearS, curDate.getFullYear() +
1, nextYearSDays,
59);
181 nextYearWDays = subToSunday(nextYearW, curDate.getFullYear() +
1, nextYearWDays,
273);
184 Summer: new Date (curDate.getFullYear(),
03-
1, thisYearSDays,
2,
0,
0),
185 Winter: new Date (curDate.getFullYear(),
10-
1, thisYearWDays,
2,
0,
0),
187 daylightSavingDates[curDate.getFullYear()] = dst;
190 if (dst.Summer < curDate)
192 if (dst.Winter < curDate)
194 if (summer && !winter)
200 function error(message)
202 console.info('Error: ' + message);
203 document.getElementById(
"calendarList").innerHTML = 'Error: ' + message;
204 document.getElementById(
"fullscreenCalendarList").innerHTML = 'Error: ' + message;
206 document.onclick = null;
209 function areDatesEqual(date1, date2)
211 return (date1.getFullYear() == date2.getFullYear() &&
212 date1.getMonth() == date2.getMonth() &&
213 date1.getDate() == date2.getDate());
216 function isTomorrow(date)
218 // tommorow = now +
1 day
219 // ToDo: some days can be shorter as
24 hours(daylight saving change day)
220 return areDatesEqual(date, new Date (now.getTime() +
24*
60*
60*
1000));
223 function isToday(date)
225 return areDatesEqual(date, now);
228 function collectLocales()
230 var tmpyear =
2000 + panelNum;
233 if (months_translated.length
> 0)
235 for (month =
0; month <
12; month++) {
236 var startDate = new Date(tmpyear, month,
15);
238 var item = new Object();
239 item.Type =
"DayEvent";
240 item.StartTime = startDate;
241 item.Summary =
"__temp" + month;
243 var criteria = new Object();
244 criteria.Type =
"CalendarEntry";
245 criteria.Item = item;
248 var result = calendarService.IDataSource.Add(criteria);
249 if (result.ErrorCode)
250 throw(result.ErrorMessage);
252 error(
"collectLocales: " + e + ', line ' + e.line);
255 for (weekday =
0; weekday <
7; weekday++) {
256 var startDate = new Date(
2000,
0,
2 + weekday); // date that we know for sure is a sunday
258 var item = new Object();
259 item.Type =
"DayEvent";
260 item.StartTime = startDate;
261 item.Summary =
"__weekday_temp" + weekday;
263 var criteria = new Object();
264 criteria.Type =
"CalendarEntry";
265 criteria.Item = item;
268 var result = calendarService.IDataSource.Add(criteria);
269 if (result.ErrorCode)
270 throw(result.ErrorMessage);
272 error(
"collectLocales: " + e + ', line ' + e.line);
276 var startTime = new Date(tmpyear,
0,
1);
277 var endTime = new Date(tmpyear,
11,
31);
278 var listFiltering = {
279 Type:'CalendarEntry',
281 StartRange: startTime,
283 SearchText: '__temp',
287 var result = calendarService.IDataSource.GetList(listFiltering);
288 if (result.ErrorCode)
289 throw(result.ErrorMessage);
290 var list = result.ReturnValue;
292 error(
"collectLocales: " + e + ', line ' + e.line);
295 var ids = new Array();
301 while (list && (entry = list.getNext()) != undefined) {
302 dateArr = (entry.StartTime + '').replace(/,/g,'').replace(/\./g,':').replace(/ /g,' ').split(' ');
303 var day = dateArr[
1];
304 var month = dateArr[
2];
305 var year = dateArr[
3];
307 // make sure month is set properly
308 if (isNaN(parseInt(day))) {
312 } else if (isNaN(parseInt(year))) {
318 log(entry.StartTime + ' -
> ' + month + ' ' + counter);
319 ids[counter] = entry.id;
320 months_translated[month] = counter +
1;
324 error(
"collectLocales: " + e + ', line ' + e.line);
328 var startTime = new Date(
2000,
0,
2);
329 var endTime = new Date(
2000,
0,
9);
330 var listFiltering = {
331 Type:'CalendarEntry',
333 StartRange: startTime,
335 SearchText: '__weekday_temp',
339 var result = calendarService.IDataSource.GetList(listFiltering);
340 if (result.ErrorCode)
341 throw(result.ErrorMessage);
342 var weekdaylist = result.ReturnValue;
344 error(
"collectLocales: " + e + ', line ' + e.line);
352 while (weekdaylist && (entry = weekdaylist.getNext()) != undefined) {
353 detectTimeFormat(entry.StartTime + '');
354 curWeekday = (entry.StartTime + '').split(',')[
0];
355 log(entry.StartTime + ' -
> ' + curWeekday + ' ' + counter2);
356 ids[counter + counter2] = entry.id;
357 weekdays_translated[counter2] = curWeekday;
361 error(
"collectLocales: " + e + ', line ' + e.line);
366 var criteria = new Object();
367 criteria.Type =
"CalendarEntry";
372 var result = calendarService.IDataSource.Delete(criteria);
373 if (result.ErrorCode)
374 throw(result.ErrorMessage);
376 error('deleting temp calendar entries:' + e + ', line ' + e.line);
381 function stringEndsWith(str, suffix)
383 return str.indexOf(suffix, str.length - suffix.length) !== -
1;
386 // detects the system's current time format by parsing a native calendar timestamp (this is the only reliable formating across all devices and firmwares)
387 function detectTimeFormat(localeTimeString)
389 localeTimeString = localeTimeString.toLowerCase();
390 use12hoursTimeFormat = stringEndsWith(localeTimeString,
"am") || stringEndsWith(localeTimeString,
"pm");
391 timeFormatSeparator = localeTimeString.indexOf(
":") != -
1 ?
":" :
".";
394 function requestNotification()
396 var criteria = new Object();
397 criteria.Type =
"CalendarEntry";
398 criteria.Filter = new Object();
399 for(var i=
0; i < calendarList.length; i++) {
400 criteria.Filter.CalendarName = calendarList[i];
402 var notificationRequest = calendarService.IDataSource.RequestNotification(criteria, callback);
403 if (notificationRequest.ErrorCode)
404 error('requestNotification failed with error code ' + notificationRequest.ErrorCode);
405 notificationRequests.push(notificationRequest);
407 error(
"requestNotification: " + e + ', line ' + e.line);
411 var criteria2 = new Object();
412 criteria2.Type =
"CalendarEntry";
413 criteria2.Filter = new Object();
414 criteria2.Filter.LocalIdList = new Array();
415 criteria2.Filter.LocalIdList[
0] = settingsCalEntryId;
417 var notificationRequest = calendarService.IDataSource.RequestNotification(criteria2, settingsCallback);
418 if (notificationRequest.ErrorCode)
419 error('requestNotification failed with error code ' + notificationRequest.ErrorCode);
420 notificationRequests.push(notificationRequest);
422 error(
"requestNotification: " + e + ', line ' + e.line);
426 function cancelNotification()
428 for(var i=
0; i < notificationRequests.length; i++) {
430 var result = calendarService.IDataSource.Cancel(notificationRequests[i]);
431 if (result.ErrorCode)
432 error('cancelNotification failed with error code ' + result.ErrorCode);
434 error(
"cancelNotification: " + e + ', line ' + e.line);
439 function callback(transId, eventCode, result)
441 log(
"callback(): panelNum: " + panelNum +
" transId: " + transId +
" eventCode: " + eventCode +
" result.ErrorCode: " + result.ErrorCode);
442 lastReloadTime = null; // force calendar data reload on next update
446 function settingsCallback(transId, eventCode, result)
448 log(
"settingsCallback(): panelNum: " + panelNum +
" transId: " + transId +
" eventCode: " + eventCode +
" result.ErrorCode: " + result.ErrorCode);
452 function parseDate(dateString)
455 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:
456 Wednesday,
26 August,
2009 24:
00:
00
457 Wednesday,
26 August,
2009 12:
00:
00 am
458 Wednesday, August
26,
2009 12:
00:
00 am
459 Wednesday,
2009 August,
26 12:
00:
00 am
460 Wednesday,
2009 August,
28 8.00.00 pm
461 Wednesday,
2009 August,
28 08:
00:
00 PM
465 if (dateString ==
"" || dateString == null || dateString == undefined)
467 if (dateString instanceof Date) {
468 // we already have a date object, no need to parse string here
472 var dateArr = (dateString + '').replace(/,/g, '').replace(/\./g, ':').replace(/ /g, ' ').split(' ');
473 if (dateArr.length !=
5 && dateArr.length !=
6)
477 var weekDay = dateArr[
0];
478 var day = dateArr[
1];
479 var month = dateArr[
2];
480 var year = dateArr[
3];
481 // make sure month is set properly
482 if (isNaN(parseInt(day))) {
488 if (isNaN(parseInt(year))) {
493 // make sure day and year are set properly
494 if (Number(day)
> Number(year)) {
499 month = months_translated[month];
502 var timeArr = dateArr[
4].split(':');
503 if (timeArr.length !=
3)
505 var hours = Number(timeArr[
0]);
506 var minutes = Number(timeArr[
1]);
507 var seconds = Number(timeArr[
2]);
508 if (dateArr.length ==
6 && dateArr[
5].toLowerCase() == 'pm' && hours <
12)
510 if (dateArr.length ==
6 && dateArr[
5].toLowerCase() == 'am' && hours ==
12)
513 result = new Date(year, month -
1, day, hours, minutes, seconds);
516 // take care of daylight saving time
517 if (config['enableDaylightSaving'].Value) {
519 // determine if date is in summer or winter time
520 var dateSummerTime = isSummertime(result);
522 // work around bug in Nokias calendar api resulting in dates within a different DST to be off by
1 hour
523 if (summertime && !dateSummerTime) {
524 result = new Date(result.getTime() -
1000 *
60 *
60 * config['daylightSavingOffset'].Value); // -
1 hour
525 log('parseDate(): fixing time -
1h: ' + result);
527 else if (!summertime && dateSummerTime) {
528 result = new Date(result.getTime() +
1000 *
60 *
60 * config['daylightSavingOffset'].Value); // +
1 hour
529 log('parseDate(): fixing time +
1h: ' + result);
536 function getWeekdayLocalized(date) {
537 var localizedString = date.toLocaleDateString();
538 if (localizedString.indexOf(
",") == -
1) {
539 return weekdays_translated[date.getDay()];
541 return localizedString.split(',')[
0];
544 // 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"
545 function formatDate(date, format)
547 var day = date.getDate().toString();
548 var month = (date.getMonth() +
1).toString();
549 while (day.length <
2) { day = '
0' + day; }
550 while (month.length <
2) { month = '
0' + month; }
552 if (config['showTodayAsText'].Value && isToday(date))
553 return '
<span class=
"today">' + config['todayText'].Value + '
</span>';
554 if (config['showTodayAsText'].Value && isTomorrow(date))
555 return '
<span class=
"tomorrow">' + config['tomorrowText'].Value + '
</span>';
557 if (format instanceof Date) {
558 // we don't know how to format this
559 if (config['dateFormat'].Value == 'auto' || config['dateFormat'].Value == 'DDMM')
560 return day + config['dateSeparator'].Value + month;
562 return month + config['dateSeparator'].Value + day;
564 var dateArr = format.replace(/,/g,'').replace(/\./g,':').replace(/ /g,' ').split(' ');
565 if (dateArr.length !=
5 && dateArr.length !=
6) {
566 // we don't know how to format this
567 if (config['dateFormat'].Value == 'auto' || config['dateFormat'].Value == 'DDMM')
568 return day + config['dateSeparator'].Value + month;
570 return month + config['dateSeparator'].Value + day;
574 if (config['dateFormat'].Value == 'MMDD')
576 else if (config['dateFormat'].Value == 'DDMM')
579 // config['dateFormat'].Value == 'auto', try to detect system setting
581 var day_ = dateArr[
1];
582 var month_ = dateArr[
2];
583 var year_ = dateArr[
3];
584 // make sure month is set properly
585 if (isNaN(parseInt(day_))) {
590 } else if (isNaN(parseInt(year_))) {
596 // make sure day and year are set properly
597 if (Number(day_)
> Number(year_))
602 return day + config['dateSeparator'].Value + month;
604 return month + config['dateSeparator'].Value + day;
607 function formatTime(date)
609 // date is a Date() object
610 var hour = date.getHours();
611 var minute = date.getMinutes();
613 // don't use Date().toLocaleTimeString() as it is utterly broken on newer firmwares
614 if (use12hoursTimeFormat) {
625 minute =
"0" + minute;
626 time = hour + timeFormatSeparator + minute +
" " + ap;
632 minute =
"0" + minute;
633 time = hour + timeFormatSeparator + minute;
636 if (config['showNowAsText'].Value && date.getTime() == now.getTime())
637 time = '
<span class=
"now">' + config['nowText'].Value + '
</span>';
638 log(
"formatTime(): " + time +
", use12hoursTimeFormat=" + use12hoursTimeFormat +
", timeFormatSeparator=" + timeFormatSeparator +
", date.toLocateTimeString(): " + date.toLocaleTimeString());
642 function updateData()
649 // check if we got additional or less calendars since our last update
650 var newCalendarList = listCalendars();
651 if (newCalendarList == null) {
652 // Something went wrong fetching the calendars list.
653 // This usually happens when a backup is being made.
654 // Retry the next time updateData() is called by
655 // resetting errorOccured
656 log('updateData(): listCalendars() failed, trying again later...');
657 cacheEntriesHtml = ''; // make sure we replace the currently shown error message on the next update
658 errorOccured = false;
661 if (newCalendarList.length != calendarList.length) {
662 calendarList = newCalendarList;
663 updateCalendarColors();
664 cancelNotification();
665 requestNotification();
666 lastReloadTime = null; // force calendar data reload on this update
671 // only reload calendar data every
6 hours, visual updates occure more often
672 if (!lastReloadTime || now.getTime() - lastReloadTime.getTime()
> reloadInterval) {
673 log('updateData(): reloading calendar data');
675 // meetings have time
676 // 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
677 summertime = isSummertime(now); // cache summer time info for today
678 var meetingList = [];
679 for(var i=
0; i < calendarList.length; i++) {
680 // ignore excluded calendars
681 if (config['excludedCalendars'].Value.indexOf(calendarList[i]) != -
1)
683 var meetingListFiltering = {
684 Type:'CalendarEntry',
686 CalendarName: calendarList[i],
687 StartRange: (new Date(now.getFullYear(), now.getMonth(), now.getDate(),
0,
0,
0)),
688 EndRange: (new Date(now.getFullYear(), now.getMonth() + config['monthRange'].Value, now.getDate(),
0,
0,
0))
691 var meetingResult = calendarService.IDataSource.GetList(meetingListFiltering);
692 if (meetingResult.ErrorCode !=
0)
693 throw(
"Error fetching calendar data: " + meetingResult.ErrorCode + ': ' + meetingResult.ErrorMessage);
694 var list = meetingResult.ReturnValue;
695 meetingList = meetingList.concat(listToArray(list, calendarList[i]));
697 log(
"updateData(): meetingList.sort()");
698 meetingList.sort(sortCalendarEntries);
700 // todos don't, they start on
00:
00 hrs., but should be visible anyway
701 // this will generate a list of passed todos. We have to check if they have been marked as
"done" yet
702 if (config['includeTodos'].Value) {
703 var todayTodoList = [];
704 for(var i=
0; i < calendarList.length; i++) {
705 // ignore excluded calendars
706 if (config['excludedCalendars'].Value.indexOf(calendarList[i]) != -
1)
708 var todayTodoListFiltering = {
709 Type:'CalendarEntry',
711 CalendarName: calendarList[i],
713 StartRange: (new Date(now.getFullYear() -
1, now.getMonth(), now.getDate(),
0,
0,
0)),
714 EndRange: (new Date(now.getFullYear(), now.getMonth(), now.getDate(),
0,
0,
1))
717 var todayTodoResult = calendarService.IDataSource.GetList(todayTodoListFiltering);
718 var list = todayTodoResult.ReturnValue;
719 todayTodoList = todayTodoList.concat(listToArray(list, calendarList[i]));
721 log(
"updateData(): todayTodoList.sort()");
722 todayTodoList.sort(sortCalendarEntries);
723 entryLists = [todayTodoList, meetingList];
725 entryLists = [meetingList];
727 lastReloadTime = new Date();
729 error('loading Calendar items list:' + e + ', line ' + e.line);
739 var fontsize = getDefaultFontSize()[
1] + 'px';
740 var lineheight = fontsize;
742 if (config['fontsize'].Value == config['fontsize'].ValidValues[
0]) {
743 fontsize = parseInt(
72 / config['eventsPerWidget'].Value) + 'px';
744 lineheight = parseInt(
82 / config['eventsPerWidget'].Value) + 'px';
747 if (config['fontsize'].Value != config['fontsize'].ValidValues[
0]) {
748 fontsize = config['fontsize'].Value + 'px';
749 lineheight = fontsize;
751 changeCssClass('.icon', config['cssStyle_icon'].Value + '; width:' + lineheight + '; height:' + lineheight + ';');
752 var entriesHtml = '
<table style=
"font-size:' + fontsize + '; line-height:' + lineheight + ';">';
754 entriesHtml = '
<table width=
"307" height=
"82"><tr><td>' + entriesHtml; // this is needed to center the actual content vertically
758 max = (panelNum +
1) * config['eventsPerWidget'].Value;
760 max =
30; // we can display a lot more events in fullscreen mode
762 if (config['enableLogging'].Value) {
764 for (var i=
0; i < entryLists.length; i++) {
765 listinfo = listinfo +
" " + entryLists[i].length;
766 var entrieslist =
"";
767 for (var j=
0; j < entryLists[i].length; j++) {
768 entrieslist += entryLists[i][j].Summary +
", ";
770 log(
"updateData(): entrieslist: " + entrieslist);
772 log(
"updateData(): inner loop, " + entryLists.length +
" lists, [" + listinfo +
"] entries");
775 // the first outer loop iteration is for passed ToDos, the second loop is for all upcomming events (may also include ToDos)
776 for (var i=
0; counter < max && i < entryLists.length; i++) {
777 for (var j=
0; (counter < max) && (j < entryLists[i].length); j++) {
778 entry = entryLists[i][j];
781 // output event info for debugging
782 var entryInfo =
"event: ";
783 for(var k=
0; k < entryFields.length; ++k) {
784 if (entry[entryFields[k]] != undefined) {
785 entryInfo += entryFields[k] +
"=" + entry[entryFields[k]] +
",";
790 // we don't want ToDos when includeTodos == false or when they are completed
791 if (entry.Type == 'ToDo' && (entry.Status ==
"TodoCompleted" || !config['includeTodos'].Value)) {
792 log('skipping ' + entry.id );
797 // make sure that we don't include an event twice (useful for ToDos that might come up twice)
798 if (eventIds[entry.id] ==
1 && entry.Type == 'ToDo') {
799 log('skipped (already included) ' + entry.id);
803 eventIds[entry.id] =
1;
805 // summary can be undefined!
806 var Summary = ((entry.Summary == null) ? '' : entry.Summary);
807 if (entry.Location != '' && entry.Location != undefined && config['showLocation'].Value)
808 Summary += ', ' + entry.Location;
810 // fix by yves: determine start and end dates/times
811 entryStartTime = ((entry.InstanceStartTime == null) ? entry.StartTime : entry.InstanceStartTime);
812 entryEndTime = ((entry.InstanceEndTime == null) ? entry.EndTime : entry.InstanceEndTime);
814 // there can be ToDos that have no date at all!
815 if (entry.Type == 'ToDo' && entry.EndTime == null)
816 entryDate =
""; // this will cause parseDate(entryDate) to return null;
818 entryDate = ((entry.Type == 'ToDo') ? entryEndTime : entryStartTime); // ToDo's use their EndTime, the rest use StartTime
820 // Convert date/time string to Date object
821 var date = parseDate(entryDate);
822 log('date: ' + date);
823 var endDate = ((entryEndTime == null) ? null : parseDate(entryEndTime));
824 log('endDate: ' + endDate);
826 // check if Meeting is actually a DayEvent. Bug introduced by
"Anna" updates to various Symbian^
3 devices.
827 // Note that this workaround is not
100% save! It might missinterpret some meetings as dayevents of starting and ending on
00:
00
828 if (entry.Type == 'Meeting' && date.getHours() ==
0 && date.getMinutes() ==
0 &&
829 endDate != null && endDate.getHours() ==
0 && endDate.getMinutes() ==
0) {
830 log('fixing event type: changed from
"Meeting" to
"DayEvent".');
831 entry.Type = 'DayEvent';
834 // check if meeting event has already passed
835 if (entry.Type == 'Meeting') {
836 var compareTime = ((endDate == null) ? date.getTime() : endDate.getTime());
837 if (now.getTime()
> compareTime) {
838 log('skipping Meeting (already passed) ' + entry.id);
840 eventIds[entry.id] =
0;
845 // check if anniversary passed (not sure why they are in the list, the query was only for today - nokia?)
846 if (entry.Type == 'Anniversary') {
847 var tmp = new Date(now.getFullYear(), now.getMonth(), now.getDate(),
0,
0,
0);
848 if (date.getTime() < tmp.getTime()) {
849 log('skipping Anniversary (already passed) ' + entry.id);
851 eventIds[entry.id] =
0;
856 // fix DayEvents end time. End times are off by
1 Second. It's possible that the event has already passed
857 if (entry.Type == 'DayEvent' && endDate != null) {
858 endDate.setMinutes(endDate.getMinutes() -
1);
859 log('fixing DayEvent endDate: ' + endDate);
860 if (now.getTime()
> endDate.getTime()) {
861 log('event already passed ' + entry.id);
863 eventIds[entry.id] =
0;
868 // check if the event is currently taking place
869 if (entryStartTime != null && entryEndTime != null && date != null && endDate != null) {
870 // check if we are between start and endtime
871 if ((date.getTime() < now.getTime()) && (now.getTime() < endDate.getTime())) {
872 date = now; // change appointment date/time to now
873 log('event is currently taking place: ' + date);
877 // skip events for the first panel in case this is the second one and we're not in fullscreen mode
878 if (mode ==
0 && panelNum
> 0 && counter < panelNum * config['eventsPerWidget'].Value +
1) {
879 log('skipping (already in first widget) ' + entry.id);
883 // mark overdue todos
885 if (entry.Type == 'ToDo' && date != null) {
886 var tmp1 = new Date(date.getFullYear(), date.getMonth(), date.getDate(),
0,
0,
0);
887 var tmp2 = new Date(now.getFullYear(), now.getMonth(), now.getDate(),
0,
0,
0);
888 if (tmp1.getTime() < tmp2.getTime()) {
893 // generate html output
894 entriesHtml += '
<tr>';
895 if (config['showCalendarIndicator'].Value && calendarList.length - config['excludedCalendars'].Value.length
> 1) {
896 entriesHtml += '
<td><span class=
"calendar' + calendarColors[entry.CalendarName] + '"> </span></td>';
898 if (config['showIcons'].Value)
899 entriesHtml += '
<td><img class=
"icon" align=
"top" src=
"' + entry.Type + '.png" /></td>';
901 entriesHtml += '
<td style=
"padding:0px;"></td>';
903 // some languages have very strange locale date formats, can't parse all those. Also some todos don't have dates at all.
904 entriesHtml += '
<td colspan=
"4"><span class=
"date">' + entryDate + '
</span> ';
906 var weekDay = getWeekdayLocalized(date).substr(
0,config['weekDayLength'].Value);
907 log('date.toLocaleDateString(): ' + date.toLocaleDateString());
908 log('weekDay: ' + weekDay);
909 var time = formatTime(date);
910 var dateStr = formatDate(date, entryDate);
911 if (entry.Type == 'ToDo' && overdue && config['markOverdueTodos'].Value) {
912 dateStr = '
<span class=
"overdue">' + config['overdueText'].Value + '
</span>';
913 entriesHtml += '
<td colspan=
"4" width=
"1px"><span class=
"date">' + dateStr + '
</span> ';
914 } else if (entry.Type == 'ToDo' || entry.Type == 'Anniversary' || entry.Type == 'DayEvent' || entry.Type == 'Reminder') {
915 if ((isToday(date) || isTomorrow(date)) && config['showTodayAsText'].Value) // show weekday if the date string is not text. looks odd otherwise
916 entriesHtml += '
<td colspan=
"4" width=
"1px"><span class=
"date">' + dateStr + '
</span> ';
918 entriesHtml += '
<td class=
"weekDay" width=
"1px">' + weekDay + '
</td><td width=
"1px" class=
"date">' + dateStr + '
</td><td colspan=
"2">';
919 } else if (entry.Type == 'Meeting') {
920 if (config['showCombinedDateTime'].Value) {
922 entriesHtml += '
<td width=
"1px" colspan=
"4"><span class=
"today">' + time + '
</span> ';
923 else if (isTomorrow(date))
924 entriesHtml += '
<td width=
"1px" colspan=
"4"><span class=
"tomorrow">' + dateStr + '
</span> <span class=
"time">' + time + '
</span> ';
926 entriesHtml += '
<td width=
"1px" class=
"weekDay">' + weekDay + '
</td><td width=
"1px" class=
"date">' + dateStr + '
</td><td colspan=
"2">';
928 if ((isToday(date) || isTomorrow(date)) && config['showTodayAsText'].Value)
929 entriesHtml += '
<td colspan=
"4" width=
"1px"><span class=
"today">' + dateStr + '
</span> <span class=
"time">' + time + '
</span> ';
931 entriesHtml += '
<td width=
"1px" class=
"weekDay">' + weekDay + '
</td><td width=
"1px" class=
"date">' + dateStr + '
</td><td width=
"1px" class=
"time">' + time + '
</td><td>';
935 entriesHtml += '
<span class=
"description">' + Summary + '
</span></td></tr>';
938 entriesHtml += '
</table>';
940 entriesHtml = entriesHtml + '
</td></tr></table>';
941 if (config['showNothingText'].Value && entriesHtml == '
<table></table>') {
942 var text = config['nothingText'].Value.replace(/%d/, config['monthRange'].Value);
943 entriesHtml = '
<div style=
"width:295px; height:75px; text-align:center; line-height:75px; overflow:visible;">' + text + '
</div>';
945 log(
"output: " + entriesHtml);
946 if (cacheEntriesHtml != entriesHtml) {
948 document.getElementById('calendarList').innerHTML = entriesHtml;
950 document.getElementById('fullscreenCalendarList').innerHTML = entriesHtml;
951 cacheEntriesHtml = entriesHtml;
954 lastUpdateTime = new Date();
956 error('displaying list:' + e + ', line ' + e.line);
961 // called by handleOnShow() and onResize events
962 function updateScreen()
964 log('updateScreen(): mode=' + mode + ', window.innerHeight=' + window.innerHeight);
966 // check if opening fullscreen
968 // Note: according to Nokia's documentation, an innerHeight of
>91 is an indicator for fullscreen view.
969 // However a bug in E6's firmware causes different window widths and heights (disabled compatibility scaling).
970 // So far, values of
104 and
115 for window.innerHeight were reported, we use a safty margin here and check
972 if( window.innerHeight
> 150 && mode ==
0) {
974 cacheEntriesHtml = '';
975 document.getElementById('body').style.backgroundImage =
"";
978 else if (window.innerHeight <=
150 && mode !=
0) {
980 cacheEntriesHtml = '';
985 updateHomescreen(); // check for screen rotation
990 function handleOnShow()
994 var time = new Date();
995 if (time.getTime() - lastUpdateTime.getTime()
> config['updateDataInterval'].Value *
60 *
1000) {
996 log('updateScreen(): force updateData() because last update was too long ago (' + (time.getTime() - lastUpdateTime.getTime()) /
1000 + 's)');
999 setUpdateTimer(); // reinitialize update timer
1003 function launchCalendar()
1006 widget.openApplication(config['calendarApp'].Value,
"");
1007 if (config['hideWidgetOnCalendarOpen'].Value)
1010 error('starting Calendar App');
1017 log('New widget instance starting up...');
1020 // call calendar service
1021 if (device !=
"undefined")
1022 calendarService = device.getServiceObject(
"Service.Calendar",
"IDataSource");
1024 throw('device object does not exist');
1026 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>');
1030 calendarList = listCalendars();
1032 updateCalendarColors();
1035 requestNotification();
1036 document.getElementById(
"settingsTitle").innerHTML = getLocalizedText('menu.settings');
1038 if (window.innerHeight
> 91) {
1039 mode =
0; // we're starting fullscreen, we set mode to homescreen in order to let updateScreen() do all the work for us
1044 log(
"init(): updateScreen()");
1046 if (config['useBackgroundImage'].Value)
1047 // check for screen rotation every
1 secs
1048 screenRotationTimer = window.setInterval('checkOrientation()',
1000 *
1);
1050 // call updateScreen() when widget changes from background to forground
1051 window.widget.onshow = handleOnShow;
1053 log(
"init(): finished...");
1055 statupSuccessful = true;
1058 function checkOrientation()
1062 updateHomescreen(); // check for screen rotation
1065 function setUpdateTimer()
1067 updateTimer = window.setInterval('updateTimerCallback()',
1000 *
60 * config['updateDataInterval'].Value);
1070 function clearUpdateTimer()
1072 window.clearInterval(updateTimer);
1075 function updateTimerCallback()
1077 log(
"updateTimerCallback()");
1081 function createMenu()
1083 window.menu.setLeftSoftkeyLabel(
"",null);
1084 window.menu.setRightSoftkeyLabel(
"",null);
1086 var menuSettings = new MenuItem(getLocalizedText('menu.settings'), id++);
1087 var menuCallApp = new MenuItem(getLocalizedText('menu.openCalendarApp'), id++);
1088 var menuHelp = new MenuItem(getLocalizedText('menu.help'), id++);
1089 var menuUpdate = new MenuItem(getLocalizedText('menu.update'), id++);
1090 var menuAbout = new MenuItem(getLocalizedText('menu.about'), id++);
1091 menuSettings.onSelect = showSettings;
1092 menuAbout.onSelect = showAbout;
1093 menuCallApp.onSelect = launchCalendar;
1094 menuUpdate.onSelect = showUpdate;
1095 menuHelp.onSelect = showHelp;
1096 window.menu.clear();
1097 window.menu.append(menuCallApp);
1098 window.menu.append(menuSettings);
1099 window.menu.append(menuHelp);
1100 window.menu.append(menuUpdate);
1101 window.menu.append(menuAbout);
1104 function showSettings()
1108 document.getElementById(
"settingsView").style.display =
"block";
1109 document.onclick = null;
1111 window.menu.setLeftSoftkeyLabel(getLocalizedText('settings.save'), function()
1113 for (var key in config) {
1114 if (config[key].Type == 'String')
1115 config[key].Value = document.forms[
0].elements[
"settings." + key].value;
1116 else if (config[key].Type == 'Int') {
1117 config[key].Value = parseInt(document.forms[
0].elements[
"settings." + key].value);
1118 if (config[key].Value <
0 || isNaN(config[key].Value))
1119 config[key].Value = config[key].Default;
1121 else if (config[key].Type == 'Bool')
1122 config[key].Value = document.forms[
0].elements[
"settings." + key].checked;
1123 else if (config[key].Type == 'UID') {
1124 config[key].Value = parseInt(document.forms[
0].elements[
"settings." + key].value);
1125 if (isNaN(config[key].Value))
1126 config[key].Value = config[key].Default;
1128 else if (config[key].Type == 'Enum') {
1129 config[key].Value = document.forms[
0].elements[
"settings." + key].value;
1130 if (config[key].ValidValues.indexOf(config[key].Value) == -
1)
1131 config[key].Value = config[key].Default;
1133 else if (config[key].Type == 'Array') {
1134 if (key == 'excludedCalendars') {
1135 config[key].Value = new Array();
1136 for(var i=
0; i < calendarList.length; i++) {
1137 var element = document.forms[
0].elements[
"settings." + key +
"." + calendarList[i]];
1138 if (element != null && element.checked == false)
1139 config[key].Value.push(calendarList[i]);
1152 window.menu.setRightSoftkeyLabel(getLocalizedText('settings.cancel'), function()
1158 var settingsHtml = '
<form>';
1159 for (var key in config) {
1160 if (config[key].Type == 'String') {
1162 if (key.substring(
0,
9) ==
"cssStyle_")
1163 prefix = getLocalizedText('settings.cssStyle_prefix');
1164 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 />';
1166 else if (config[key].Type == 'Int')
1167 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 />';
1168 else if (config[key].Type == 'Bool')
1169 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 />';
1170 else if (config[key].Type == 'UID')
1171 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 />';
1172 else if (config[key].Type == 'Enum') {
1173 settingsHtml += '
<table><tr><td>' + getLocalizedText('settings.name.' + key) + '
<br /><select name=
"settings.' + key + '" size=
"1">';
1174 for(var i =
0; i < config[key].ValidValues.length; i++) {
1175 var text = getLocalizedText('settings.validValues.' + key + '.' + config[key].ValidValues[i]);
1176 if (text.indexOf('ERROR') ==
0)
1177 text = config[key].ValidValues[i];
1178 settingsHtml += '
<option value=
"' + config[key].ValidValues[i] + '"' + (config[key].Value == config[key].ValidValues[i] ? '
selected=
"selected"' : '') + '
>' + text + '
</option>';
1180 settingsHtml += '
</select></div></td>' + printHintBox(getLocalizedText('settings.info.' + key)) + '
<hr />';
1182 else if (config[key].Type == 'Array') {
1183 settingsHtml += '
<table><tr><td>' + getLocalizedText('settings.name.' + key) + '
<br />';
1184 if (key == 'excludedCalendars') {
1185 for(var i=
0; i < calendarList.length; i++) {
1186 var checked = '
checked=
"checked"';
1187 if (config[key].Value.indexOf(calendarList[i]) != -
1)
1189 settingsHtml += '
<input name=
"settings.' + key + '.' + calendarList[i] + '" type=
"checkbox" value=
"' + calendarList[i] + '" ' + checked + '
/> ' + calendarList[i] + '
<br />';
1192 settingsHtml += '
</td>' + printHintBox(getLocalizedText('settings.info.' + key)) + '
<hr />';
1195 settingsHtml += '
<input name=
"reset" type=
"button" value=
"' + getLocalizedText('settings.restoreDefaults') + '" onclick=
"javascript:restoreDefaultSettings();showSettings();" />';
1196 settingsHtml += '
</form>';
1197 document.getElementById(
"settingsList").innerHTML = settingsHtml;
1200 function changeCssClass(classname, properties)
1202 for(var i =
0; i < document.styleSheets[
0]['cssRules'].length; i++)
1204 if (document.styleSheets[
0]['cssRules'][i].selectorText == classname) {
1205 document.styleSheets[
0].deleteRule(i);
1206 document.styleSheets[
0].insertRule(classname + ' { ' + properties + ' }', document.styleSheets[
0]['cssRules'].length);
1212 function updateCssClasses()
1214 for(var key in config) {
1215 changeCssClass(getLocalizedText('settings.name.' + key), config[key].Value);
1219 function getSettingsCalEntryId()
1221 if (settingsCalEntryId == null) {
1222 // check if entry already exists
1223 var listFiltering = {
1224 Type:'CalendarEntry',
1226 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!
1227 EndRange: new Date(
2000,
0,
2),
1228 SearchText: 'ComingNext Settings|',
1234 result = calendarService.IDataSource.GetList(listFiltering);
1235 if (result.ErrorCode)
1236 throw(result.ErrorMessage);
1239 error(
"getSettingsCalEntryId: GetList() failed: " + e + ', line ' + e.line);
1242 var list = result.ReturnValue;
1243 var entry = list.getNext();
1244 if (entry != undefined) {
1245 settingsCalEntryId = entry.LocalId;
1246 log(
"settingsCalEntryId=" + settingsCalEntryId);
1248 else { // create settings item
1249 var item = new Object();
1250 item.Type =
"DayEvent";
1251 item.StartTime = new Date(
2000,
0,
1);
1252 item.Summary =
"ComingNext Settings|";
1254 var criteria = new Object();
1255 criteria.Type =
"CalendarEntry";
1256 criteria.Item = item;
1259 var result = calendarService.IDataSource.Add(criteria);
1260 if (result.ErrorCode)
1261 throw(result.ErrorMessage);
1263 error(
"getSettingsCalEntryId: " + e + ', line ' + e.line);
1266 getSettingsCalEntryId();
1271 function restoreDefaultSettings()
1273 for (var key in config)
1274 config[key].Value = config[key].Default;
1277 function loadSettings()
1279 getSettingsCalEntryId();
1280 var listFiltering = {
1281 Type:'CalendarEntry',
1283 LocalId: settingsCalEntryId
1288 result = calendarService.IDataSource.GetList(listFiltering);
1289 if (result.ErrorCode)
1290 throw(result.ErrorMessage);
1293 error(
"loadSettings: GetList() failed: " + e + ', line ' + e.line);
1296 var entry = result.ReturnValue.getNext();
1297 if (entry != undefined) {
1298 log(
"Loading Settings...");
1299 // only reload settings if they chanced since the last reload
1300 if (settingsCache != entry.Summary)
1302 restoreDefaultSettings();
1303 var stringlist = entry.Summary.split(
"|");
1304 // skip the first two entries, those contain header and version info
1305 for(var i =
2; i < stringlist.length -
1; i++) {
1306 var pair = stringlist[i].split('=');
1308 var value = pair[
1];
1309 if (key == null || value == null || config[key] == null) {
1310 log('Warning: unknown or invalid setting: ' + stringlist[i]);
1313 log('stringlist[' + i + ']: ' + key + '=\'' + value + '\'');
1314 if (config[key].Type == 'Int') {
1315 config[key].Value = Number(value);
1316 if (isNaN(config[key].Value))
1317 config[key].Value = config[key].Default;
1319 else if (config[key].Type == 'String')
1320 config[key].Value = value;
1321 else if (config[key].Type == 'Bool')
1322 config[key].Value = (value == 'true')
1323 else if (config[key].Type == 'Enum')
1324 config[key].Value = value;
1325 else if (config[key].Type == 'UID') {
1326 config[key].Value = Number(value);
1327 if (isNaN(config[key].Value))
1328 config[key].Value = config[key].Default;
1330 else if (config[key].Type == 'Array') {
1331 config[key].Value = value.split(
"^");
1332 if (config[key].Value.length ==
1 && config[key].Value[
0] ==
"") {
1333 config[key].Value = [];
1337 settingsCache = entry.Summary;
1341 log(
"Settings already cached and did not change");
1345 error(
"Failed to load settings, calendar entry could not be found");
1349 function saveSettings()
1351 getSettingsCalEntryId();
1352 var item = new Object();
1353 item.Type =
"DayEvent";
1354 item.StartTime = new Date(
2000,
0,
1);
1355 item.LocalId = settingsCalEntryId;
1356 item.Summary =
"ComingNext Settings|" + version +
"|";
1358 for (var key in config) {
1359 if (config[key].Type == 'Int')
1360 item.Summary += key +
"=" + config[key].Value.toString() +
"|";
1361 else if (config[key].Type == 'String')
1362 item.Summary += key +
"=" + config[key].Value +
"|";
1363 else if (config[key].Type == 'Bool')
1364 item.Summary += key +
"=" + (config[key].Value ? 'true' : 'false') +
"|";
1365 else if (config[key].Type == 'Enum')
1366 item.Summary += key +
"=" + config[key].Value +
"|";
1367 else if (config[key].Type == 'UID')
1368 item.Summary += key +
"=" + config[key].Value.toString() +
"|";
1369 else if (config[key].Type == 'Array')
1370 item.Summary += key +
"=" + config[key].Value.join(
"^") +
"|";
1372 settingsCache = item.Summary;
1374 var criteria = new Object();
1375 criteria.Type =
"CalendarEntry";
1376 criteria.Item = item;
1378 log(
"Saving settings to calendar entry: " + item.Summary);
1380 var result = calendarService.IDataSource.Add(criteria);
1381 if (result.ErrorCode)
1382 throw(result.ErrorMessage);
1384 error(
"saveSettings: " + e + ', line ' + e.line);
1387 lastReloadTime = null; // force calendar data reload on next update
1392 function toggleVisibility(elementId)
1394 if (document.getElementById(elementId).style.display ==
"none")
1395 document.getElementById(elementId).style.display =
"block";
1397 document.getElementById(elementId).style.display =
"none";
1401 function printHintBox(text)
1404 return '
<td width=
"1%" align=
"right" onclick=
"javascript:toggleVisibility(\'info' + uniqueId + '\')">' + getLocalizedText('settings.help') + '
</td></tr></table>'+
1405 '
<div class=
"settingsInfo" id=
"info' + uniqueId + '" style=
"display:none">' + text + '
</div>';
1408 function showAbout()
1412 document.getElementById(
"aboutView").style.display =
"block";
1413 document.onclick = null;
1415 window.menu.setLeftSoftkeyLabel(
" ", function(){});
1416 window.menu.setRightSoftkeyLabel(getLocalizedText('softkey.back'), function()
1422 //document.getElementById(
"aboutView").innerHTML = 'aboutView';
1423 document.getElementById(
"name").innerHTML =
"Coming Next " + version;
1426 function showHelp() {
1427 widget.openURL('http://comingnext.sf.net/help');
1430 function updateFullscreen()
1434 function showFullscreen()
1436 log(
"showFullscreen()");
1438 document.getElementById(
"fullscreenView").style.display =
"block";
1439 document.getElementById('body').className =
"backgroundFullscreen";
1441 document.onclick = launchCalendar;
1446 function getBackgroundImage()
1451 if (config['backgroundImageLocation'].Value == config['backgroundImageLocation'].ValidValues[
0]) // internal
1452 bgImage = 'background_' + orientation + '.png';
1454 bgImage = 'C:/Data/background_' + panelNum + '_' + orientation + '.png';
1458 function updateHomescreen()
1460 if (config['useBackgroundImage'].Value) {
1461 // check if we have a completely unknown screen resolution
1462 var screenHeight = screen.height;
1463 var screenWidth = screen.width;
1464 if (screenHeight !=
640 && screenHeight !=
480 && screenHeight !=
360)
1465 screenHeight =
360; // we can only assume we're in portrait mode, so we set the screen dims as needed for the following code
1466 if (screenWidth !=
640 && screenWidth !=
480 && screenWidth !=
360)
1467 screenWidth =
640; // we can only assume we're in portrait mode, so we set the screen dims as needed for the following code
1469 // check for screen rotation
1470 if (orientation != 'portrait' && ((screenWidth ==
360 && screenHeight ==
640) || (screenWidth ==
640 && screenHeight ==
480))) {
1471 window.widget.prepareForTransition(
"fade");
1472 orientation = 'portrait';
1473 document.getElementById('body').style.backgroundImage = 'url(' + getBackgroundImage() + ')';
1474 document.getElementById('body').style.backgroundColor = 'none';
1475 window.widget.performTransition();
1476 } else if (orientation != 'landscape' && ((screenWidth ==
640 && screenHeight ==
360) || (screenWidth ==
480 && screenHeight ==
640))) {
1477 window.widget.prepareForTransition(
"fade");
1478 orientation = 'landscape';
1479 document.getElementById('body').style.backgroundImage = 'url(' + getBackgroundImage() + ')';
1480 document.getElementById('body').style.backgroundColor = 'none';
1481 window.widget.performTransition();
1483 else if (document.getElementById('body').style.backgroundImage ==
"")
1485 document.getElementById('body').style.backgroundImage = 'url(' + getBackgroundImage() + ')';
1490 function showHomescreen()
1492 log(
"showHomescreen()");
1494 document.getElementById(
"homescreenView").style.display =
"block";
1495 document.getElementById('body').className =
"background";
1496 document.onclick = null;
1500 function getLocalizedText(p_Txt)
1502 if (localizedText[p_Txt])
1503 return localizedText[p_Txt];
1505 return 'ERROR: missing translation for ' + p_Txt;
1508 function showUpdate()
1512 document.getElementById(
"updateView").style.display =
"block";
1513 document.onclick = null;
1515 window.menu.setLeftSoftkeyLabel(getLocalizedText('update.checknow'), function(){
1518 window.menu.setRightSoftkeyLabel(getLocalizedText('softkey.back'), function()
1524 document.getElementById(
"currentVersion").innerHTML = getLocalizedText(
"update.current") + version;
1528 function checkForUpdate()
1530 // asynch XHR to server url
1531 reqV = new XMLHttpRequest();
1532 reqV.onreadystatechange = checkForUpdateCallback;
1533 document.getElementById(
"updateDiv").innerHTML = getLocalizedText(
"update.checking");
1534 reqV.open(
"GET", versionURL, true);
1538 function checkForUpdateCallback()
1540 if (reqV.readyState ==
4) {
1541 if (reqV.status ==
200) {
1542 var resultXml = reqV.responseText;
1544 var div = document.getElementById(
"tmp");
1545 div.innerHTML = resultXml;
1546 var newVersion = div.getElementsByTagName('version')[
0].innerHTML;
1547 var newVersionURL = div.getElementsByTagName('url')[
0].innerHTML;
1549 if (version != newVersion) {
1550 document.getElementById(
"updateDiv").innerHTML = getLocalizedText(
"update.download").replace(/%
1/, newVersion).replace(/%
2/, newVersionURL);
1553 document.getElementById(
"updateDiv").innerHTML = getLocalizedText(
"update.nonewversion");
1558 document.getElementById(
"updateDiv").innerHTML = getLocalizedText(
"update.error") + reqV.status +
" " + reqV.responseText;
1563 function hideViews()
1565 document.getElementById(
"homescreenView").style.display =
"none";
1566 document.getElementById(
"fullscreenView").style.display =
"none";
1567 document.getElementById(
"aboutView").style.display =
"none";
1568 document.getElementById(
"settingsView").style.display =
"none";
1569 document.getElementById(
"updateView").style.display =
"none";
1572 function listCalendars()
1582 DefaultCalendar: false
1586 var calendarsResult = calendarService.IDataSource.GetList(criteria);
1587 if (calendarsResult.ErrorCode !=
0)
1588 throw(
"Error fetching list of calendars: " + calendarsResult.ErrorCode + ': ' + calendarsResult.ErrorMessage);
1589 var calendarListIterator = calendarsResult.ReturnValue;
1594 while (( item = calendarListIterator.getNext()) != undefined ) {
1595 calendars[count++] = item;
1597 log(
"Available Calendars: " + calendars.join(
", "));
1600 error('listing calendars:' + e + ', line ' + e.line);
1605 // Copies all objects and their properties to an array. Data is copied so nothing gets lost when the reference is removed
1606 // 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
1607 function listToArray(list, calendarName)
1609 var array = new Array();
1612 while (( item = list.getNext()) != undefined ) {
1613 var itemCopy = new Object();
1614 for(var i=
0; i < entryFields.length; i++) {
1615 itemCopy[entryFields[i]] = item[entryFields[i]];
1617 // for some reason, the CalendarName property is never correctly queried, so we assign it manually here
1618 if (!itemCopy['CalendarName']) {
1619 itemCopy['CalendarName'] = calendarName;
1621 array.push(itemCopy);
1622 txt += array[array.length -
1].Summary +
", ";
1624 log(
"listToArray(): " + txt);
1628 function sortCalendarEntries(a, b)
1631 log(
"sortCalendarEntries(" + a.Summary +
"," + b.Summary +
")");
1633 if (a.InstanceStartTime != null) {
1634 atime = a.InstanceStartTime;
1636 else if (a.StartTime != null) {
1637 atime = a.StartTime;
1639 else if (a.InstanceEndTime != null) {
1640 atime = a.InstanceEndTime;
1642 else if (a.EndTime != null) {
1646 if (b.InstanceStartTime != null) {
1647 btime = b.InstanceStartTime;
1649 else if (b.StartTime != null) {
1650 btime = b.StartTime;
1652 else if (b.InstanceEndTime != null) {
1653 btime = b.InstanceEndTime;
1655 else if (b.EndTime != null) {
1659 if (atime && btime) {
1661 atime = parseDate(atime);
1662 btime = parseDate(btime);
1664 // sort by date & time
1665 if (atime < btime) {
1668 else if (atime
> btime) {
1672 else if (a.Type != b.Type) {
1673 if (a.Type < b.Type) {
1676 else if (a.Type
> b.Type) {
1680 // sort by description
1681 else if (a.Summary && b.Summary && a.Summary != b.Summary) {
1682 if (a.Summary < b.Summary) {
1685 else if (a.Summary
> b.Summary) {
1690 // NOTE: events my have no date information at all. In that case, we list events without date first
1691 else if (atime && !btime) {
1694 else if (!atime && btime) {
1697 else if (!atime && !btime) {
1699 if (a.Type != b.Type) {
1700 if (a.Type < b.Type) {
1703 else if (a.Type
> b.Type) {
1707 // sort by description
1708 else if (a.Summary && b.Summary && a.Summary != b.Summary) {
1709 if (a.Summary < b.Summary) {
1712 else if (a.Summary
> b.Summary) {
1721 function updateCalendarColors()
1724 calendarColors = [];
1725 if (calendarList.length
> maxColors) {
1726 log(
"updateCalendarColors(): Warning: more calendars than available indicator colors");
1728 for(var i=
0; i < calendarList.length; i++) {
1729 calendarColors[calendarList[i]] = (i % maxColors) +
1;
1733 function log(message)
1735 if (config['enableLogging'].Value) {
1736 console.info(message);
1740 function getDefaultFontSize()
1742 if (defaultFontSize == null) {
1743 var pa = document.body;
1744 var who = document.createElement('div');
1745 who.className = 'defaultEm';
1746 who.appendChild(document.createTextNode('M'));
1747 pa.appendChild(who);
1748 var fs = [who.offsetWidth, who.offsetHeight];
1749 pa.removeChild(who);
1750 defaultFontSize = fs;
1752 return defaultFontSize;
1757 <style type=
"text/css">
1759 table { margin:
0px; padding:
0px; border-spacing:
0px; border-collapse: collapse; }
1760 td { padding:
0px
5px
0px
0px; white-space:nowrap; overflow:visible; margin:
0px; }
1761 hr { color:#ffffff; background-color:#ffffff; height:
1px; text-align:left; border-style:none; }
1762 .settingsInfo { display:none; font-style:italic; }
1763 .title { font-weight:bold; font-size:
14pt; }
1764 .textInput { width:
90%; }
1765 .credits { margin-left:
40px; text-indent: -
20px; margin-bottom:
0px; }
1766 #homescreenView { width:
312px; height:
82px; overflow:hidden; }
1767 #calendarList { position:absolute; left:
5px; top:
0px; width:
307px; height:
82px; overflow:hidden; }
1768 #name { text-align:center; }
1769 #appicon { display: block; margin-left: auto; margin-right: auto; margin-top:
10px; }
1770 #smallappicon { width:
22px; height:
22px; margin-right:
10px; float:left; }
1771 .defaultEm { font-size:
1em; position:absolute; line-height:
1; padding:
0; visibility:hidden; }
1776 <body onload=
"javascript:setTimeout('init()', 10)" onresize=
"javascript:updateScreen()" id=
"body" class=
"background">
1777 <div id=
"homescreenView">
1778 <div id=
"calendarList">loading...
</div>
1780 <div id=
"fullscreenView" style=
"display:none;">
1781 <img src=
"Icon.png" id=
"smallappicon">
1782 <h1 class=
"title">Coming Next
</h1>
1784 <div id=
"fullscreenCalendarList">loading...
</div>
1786 <div id=
"settingsView" style=
"display:none">
1787 <img src=
"Icon.png" id=
"smallappicon">
1788 <h1 id=
"settingsTitle" class=
"title">Settings
</h1>
1790 <div id=
"settingsList"></div>
1792 <div id=
"aboutView" style=
"display:none">
1793 <img src=
"Icon.png" id=
"appicon">
1794 <h1 id=
"name">Coming Next
</h1>
1796 <p>Created by Dr. Cochambre and Michael Prager.
</p>
1797 <p>Contributions:
</p>
1798 <p class=
"credits">Paul Moore (bug fixes, new features and code cleanup)
</p>
1799 <p class=
"credits">Manfred Hanselmann (DST support)
</p>
1800 <p class=
"credits">Christophe Milsent (translation support & French translation)
</p>
1801 <p class=
"credits">Flavio Nathan (Portuguese-Brazilian translation)
</p>
1802 <p class=
"credits">Tokeda (Russian translation)
</p>
1803 <p class=
"credits">Marcella Ferrari (Italian translation)
</p>
1804 <p class=
"credits">Venos (Italian translation)
</p>
1805 <p class=
"credits">Francisco Rodero (Catalan translation)
</p>
1806 <p class=
"credits">zbigzbig20 (Polish translation)
</p>
1807 <p class=
"credits">Streamkeskus (Finnish translation)
</p>
1808 <p class=
"credits">renek (Czech translation)
</p>
1809 <p>This software is open source and licensed under the GPLv3.
</p>
1810 <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>
1813 <div id=
"updateView" style=
"display:none">
1814 <img src=
"Icon.png" id=
"smallappicon">
1815 <h1 class=
"title">Check for update
</h1>
1817 <div id=
"currentVersion">Coming Next ??
</div>
1818 <div id=
"updateDiv"></div>
1819 <div id=
"tmp" style=
"display:none;"></div>