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 maxNumberOfEventsOnFullscreen: { Type: 'Int', Default:
30, Value:
30,},
38 includeTodos: { Type: 'Bool', Default: true, Value: true,},
39 useBackgroundImage: { Type: 'Bool', Default: true, Value: true,},
40 backgroundImageLocation: { Type: 'Enum', Default: 'internal', Value: 'internal', ValidValues: ['internal', 'external']},
41 showCombinedDateTime: { Type: 'Bool', Default: false, Value: false,},
42 showLocation: { Type: 'Bool', Default: true, Value: true,},
43 showIcons: { Type: 'Bool', Default: true, Value: true,},
44 showTodayAsText: { Type: 'Bool', Default: true, Value: true,},
45 todayText: { Type: 'String', Default: getLocalizedText('settings.default.todayText'), Value: getLocalizedText('settings.default.todayText'),},
46 tomorrowText: { Type: 'String', Default: getLocalizedText('settings.default.tomorrowText'), Value: getLocalizedText('settings.default.tomorrowText'),},
47 showNowAsText: { Type: 'Bool', Default: true, Value: true,},
48 nowText: { Type: 'String', Default: getLocalizedText('settings.default.nowText'), Value: getLocalizedText('settings.default.nowText'),},
49 markOverdueTodos: { Type: 'Bool', Default: true, Value: true,},
50 overdueText: {Type: 'String', Default: getLocalizedText('settings.default.overdueText'), Value: getLocalizedText('settings.default.overdueText'),},
51 dateSeparator: { Type: 'String', Default: getLocalizedText('settings.default.dateSeparator'), Value: getLocalizedText('settings.default.dateSeparator'),},
52 dateFormat: { Type: 'Enum', Default: 'auto', Value: 'auto', ValidValues: ['auto', 'DDMM', 'MMDD'],},
53 weekDayLength: { Type: 'Int', Default:
2, Value:
2,},
54 updateDataInterval: { Type: 'Int', Default:
5, Value:
5,},
55 calendarApp: { Type: 'UID', Default:
0x10005901, Value:
0x10005901,},
56 showNothingText: { Type: 'Bool', Default: true, Value: true,},
57 nothingText: { Type: 'String', Default: getLocalizedText('settings.default.nothingText'), Value: getLocalizedText('settings.default.nothingText'),},
58 enableDaylightSaving: { Type: 'Bool', Default: true, Value: true,},
59 daylightSavingOffset: { Type: 'Int', Default:
1, Value:
1,},
60 hideWidgetOnCalendarOpen: { Type: 'Bool', Default: false, Value: false,},
61 showCalendarIndicator: { Type: 'Bool', Default: true, Value: true,},
62 excludedCalendars: { Type: 'Array', Default: [], Value: [],},
63 enableLogging: { Type: 'Bool', Default: false, Value: false,},
64 anonymizeLogging: { Type: 'Bool', Default: false, Value: false,},
65 cssStyle_background: { Type: 'String', Default: 'color:#ffffff; background-color:#
000000', Value: 'color:#ffffff; background-color:#
000000',},
66 cssStyle_backgroundFullscreen: { Type: 'String', Default: 'color:#ffffff; background-color:#
000000', Value: 'color:#ffffff; background-color:#
000000',},
67 cssStyle_weekDay: { Type: 'String', Default: '', Value: '',},
68 cssStyle_date: { Type: 'String', Default: '', Value: '',},
69 cssStyle_today: { Type: 'String', Default: 'color:#ff0000', Value: 'color:#ff0000',},
70 cssStyle_tomorrow: { Type: 'String', Default: 'color:#
0000ff', Value: 'color:#
0000ff',},
71 cssStyle_time: { Type: 'String', Default: '', Value: '',},
72 cssStyle_now: { Type: 'String', Default: 'color:#ff00ff', Value: 'color:#ff00ff',},
73 cssStyle_description: { Type: 'String', Default: '', Value: '',},
74 cssStyle_icon: { Type: 'String', Default: '', Value: '',},
75 cssStyle_overdue: { Type: 'String', Default: 'color:#ffff00', Value: 'color:#ffff00',},
76 cssStyle_calendar1: { Type: 'String', Default: 'background-color:#
0757cf', Value: 'background-color:#
0757cf',},
77 cssStyle_calendar2: { Type: 'String', Default: 'background-color:#
579f37', Value: 'background-color:#
579f37',},
78 cssStyle_calendar3: { Type: 'String', Default: 'background-color:#ff9f07', Value: 'background-color:#ff9f07',},
79 cssStyle_calendar4: { Type: 'String', Default: 'background-color:#af8fef', Value: 'background-color:#af8fef',},
80 cssStyle_calendar5: { Type: 'String', Default: 'background-color:#
57afbf', Value: 'background-color:#
57afbf',},
81 cssStyle_calendar6: { Type: 'String', Default: 'background-color:#
9fdf57', Value: 'background-color:#
9fdf57',},
86 //-------------------------------------------------------
87 // Nothing of interest from here on...
88 //-------------------------------------------------------
89 var panelNum =
0; // use
1 for second panel
91 var versionURL =
"http://comingnext.sourceforge.net/version.xml";
92 var calendarService = null;
93 var cacheEntriesHtml = [];
94 var months_translated = [];
95 var weekdays_translated = [];
98 var mode =
0; //
0 = homescreen,
1 = fullscreen,
2 = settings,
3 = about,
4 = check for update
100 var settingsCalEntryId = null;
101 var settingsCache = null;
102 var notificationRequests = new Array();
103 var calendarList = [];
104 var calendarColors = [];
105 var updateTimer = null;
106 var screenRotationTimer = null;
107 var lastUpdateTime = now; // last time we updated the display
108 var lastReloadTime = null; // last time we fetched calendar data
109 var reloadInterval =
6 *
60 *
60 *
1000; // =
6 hours; time interval for reloading calendar data
110 var errorOccured = false;
111 var entryLists = null; // stores all fetched calendar entries until data is refreshed
112 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.
113 var use12hoursTimeFormat = false; // defines how time should be formated:
19:
00 or
07:
00 pm
114 var timeFormatSeparator =
":"; // format time
19:
00 or
19.00 depending on system setting
115 var defaultFontSize = null; // default browser font size will be set by init
117 // vars for daylight saving time
118 var summertime = false; // true, if current date is in summer, false if in winter
119 var daylightSavingDates = new Object(); // caches calculated DST winter and summer time shift dates
121 // this is a list of data fields a calendar event can have
135 function isLeapYear( year ) {
136 if (( year %
4 ==
0 && year %
100 !=
0 ) || year %
400 ==
0 )
142 function calcLeapYear(year, days)
144 if (isLeapYear(year))
150 function subToSunday(myDate, year, days, prevMonthDays)
152 for (i = myDate.getDay(); i
> 0 ;i--)
154 days -= prevMonthDays;
155 days = isLeapYear(year) ? --days : days;
159 function isSummertime(curDate)
164 // if we already calculated DST summer and winter time dates for this year, use cached values
165 var dst = daylightSavingDates[curDate.getFullYear()];
167 var thisYearS = new Date(curDate.getFullYear(),
3,
0,
0,
0,
0 );
168 var thisYearW = new Date(curDate.getFullYear(),
10,
0,
0,
0,
0 );
169 var nextYearS = new Date(curDate.getFullYear() +
1,
3,
0,
0,
0,
0 );
170 var nextYearW = new Date(curDate.getFullYear() +
1,
10,
0,
0,
0,
0 );
172 thisYearSDays = nextYearSDays =
90;
173 thisYearWDays = nextYearWDays =
304;
175 thisYearSDays = calcLeapYear(curDate.getFullYear(), thisYearSDays);
176 thisYearWDays = calcLeapYear(curDate.getFullYear(), thisYearWDays);
177 nextYearSDays = calcLeapYear(curDate.getFullYear() +
1, nextYearSDays);
178 nextYearWDays = calcLeapYear(curDate.getFullYear() +
1, nextYearWDays);
180 thisYearSDays = subToSunday(thisYearS, curDate.getFullYear(), thisYearSDays,
59);
181 thisYearWDays = subToSunday(thisYearW, curDate.getFullYear(), thisYearWDays,
273);
182 nextYearSDays = subToSunday(nextYearS, curDate.getFullYear() +
1, nextYearSDays,
59);
183 nextYearWDays = subToSunday(nextYearW, curDate.getFullYear() +
1, nextYearWDays,
273);
186 Summer: new Date (curDate.getFullYear(),
03-
1, thisYearSDays,
2,
0,
0),
187 Winter: new Date (curDate.getFullYear(),
10-
1, thisYearWDays,
2,
0,
0),
189 daylightSavingDates[curDate.getFullYear()] = dst;
192 if (dst.Summer < curDate)
194 if (dst.Winter < curDate)
196 if (summer && !winter)
202 function error(message)
204 console.info('Error: ' + message);
205 document.getElementById(
"calendarList").innerHTML = 'Error: ' + message;
206 document.getElementById(
"fullscreenCalendarList").innerHTML = 'Error: ' + message;
208 document.onclick = null;
211 function areDatesEqual(date1, date2)
213 return (date1.getFullYear() == date2.getFullYear() &&
214 date1.getMonth() == date2.getMonth() &&
215 date1.getDate() == date2.getDate());
218 function isTomorrow(date)
220 // tommorow = now +
1 day
221 // ToDo: some days can be shorter as
24 hours(daylight saving change day)
222 return areDatesEqual(date, new Date (now.getTime() +
24*
60*
60*
1000));
225 function isToday(date)
227 return areDatesEqual(date, now);
230 function collectLocales()
232 var tmpyear =
2000 + panelNum;
235 if (months_translated.length
> 0)
237 for (month =
0; month <
12; month++) {
238 var startDate = new Date(tmpyear, month,
15);
240 var item = new Object();
241 item.Type =
"DayEvent";
242 item.StartTime = startDate;
243 item.Summary =
"__temp" + month;
245 var criteria = new Object();
246 criteria.Type =
"CalendarEntry";
247 criteria.Item = item;
250 var result = calendarService.IDataSource.Add(criteria);
251 if (result.ErrorCode)
252 throw(result.ErrorMessage);
254 error(
"collectLocales: " + e + ', line ' + e.line);
257 for (weekday =
0; weekday <
7; weekday++) {
258 var startDate = new Date(
2000,
0,
2 + weekday); // date that we know for sure is a sunday
260 var item = new Object();
261 item.Type =
"DayEvent";
262 item.StartTime = startDate;
263 item.Summary =
"__weekday_temp" + weekday;
265 var criteria = new Object();
266 criteria.Type =
"CalendarEntry";
267 criteria.Item = item;
270 var result = calendarService.IDataSource.Add(criteria);
271 if (result.ErrorCode)
272 throw(result.ErrorMessage);
274 error(
"collectLocales: " + e + ', line ' + e.line);
278 var startTime = new Date(tmpyear,
0,
1);
279 var endTime = new Date(tmpyear,
11,
31);
280 var listFiltering = {
281 Type:'CalendarEntry',
283 StartRange: startTime,
285 SearchText: '__temp',
289 var result = calendarService.IDataSource.GetList(listFiltering);
290 if (result.ErrorCode)
291 throw(result.ErrorMessage);
292 var list = result.ReturnValue;
294 error(
"collectLocales: " + e + ', line ' + e.line);
297 var ids = new Array();
303 while (list && (entry = list.getNext()) != undefined) {
304 dateArr = (entry.StartTime + '').replace(/,/g,'').replace(/\./g,':').replace(/ /g,' ').split(' ');
305 var day = dateArr[
1];
306 var month = dateArr[
2];
307 var year = dateArr[
3];
309 // make sure month is set properly
310 if (isNaN(parseInt(day))) {
314 } else if (isNaN(parseInt(year))) {
320 log(entry.StartTime + ' -
> ' + month + ' ' + counter);
321 ids[counter] = entry.id;
322 months_translated[month] = counter +
1;
326 error(
"collectLocales: " + e + ', line ' + e.line);
330 var startTime = new Date(
2000,
0,
2);
331 var endTime = new Date(
2000,
0,
9);
332 var listFiltering = {
333 Type:'CalendarEntry',
335 StartRange: startTime,
337 SearchText: '__weekday_temp',
341 var result = calendarService.IDataSource.GetList(listFiltering);
342 if (result.ErrorCode)
343 throw(result.ErrorMessage);
344 var weekdaylist = result.ReturnValue;
346 error(
"collectLocales: " + e + ', line ' + e.line);
354 while (weekdaylist && (entry = weekdaylist.getNext()) != undefined) {
355 detectTimeFormat(entry.StartTime + '');
356 curWeekday = (entry.StartTime + '').split(',')[
0];
357 log(entry.StartTime + ' -
> ' + curWeekday + ' ' + counter2);
358 ids[counter + counter2] = entry.id;
359 weekdays_translated[counter2] = curWeekday;
363 error(
"collectLocales: " + e + ', line ' + e.line);
368 var criteria = new Object();
369 criteria.Type =
"CalendarEntry";
374 var result = calendarService.IDataSource.Delete(criteria);
375 if (result.ErrorCode)
376 throw(result.ErrorMessage);
378 error('deleting temp calendar entries:' + e + ', line ' + e.line);
383 function stringEndsWith(str, suffix)
385 return str.indexOf(suffix, str.length - suffix.length) !== -
1;
388 // detects the system's current time format by parsing a native calendar timestamp (this is the only reliable formating across all devices and firmwares)
389 function detectTimeFormat(localeTimeString)
391 localeTimeString = localeTimeString.toLowerCase();
392 use12hoursTimeFormat = stringEndsWith(localeTimeString,
"am") || stringEndsWith(localeTimeString,
"pm");
393 timeFormatSeparator = localeTimeString.indexOf(
":") != -
1 ?
":" :
".";
396 function requestNotification()
398 var criteria = new Object();
399 criteria.Type =
"CalendarEntry";
400 criteria.Filter = new Object();
401 for(var i=
0; i < calendarList.length; i++) {
402 criteria.Filter.CalendarName = calendarList[i];
404 var notificationRequest = calendarService.IDataSource.RequestNotification(criteria, callback);
405 if (notificationRequest.ErrorCode)
406 error('requestNotification failed with error code ' + notificationRequest.ErrorCode);
407 notificationRequests.push(notificationRequest);
409 error(
"requestNotification: " + e + ', line ' + e.line);
413 var criteria2 = new Object();
414 criteria2.Type =
"CalendarEntry";
415 criteria2.Filter = new Object();
416 criteria2.Filter.LocalIdList = new Array();
417 criteria2.Filter.LocalIdList[
0] = settingsCalEntryId;
419 var notificationRequest = calendarService.IDataSource.RequestNotification(criteria2, settingsCallback);
420 if (notificationRequest.ErrorCode)
421 error('requestNotification failed with error code ' + notificationRequest.ErrorCode);
422 notificationRequests.push(notificationRequest);
424 error(
"requestNotification: " + e + ', line ' + e.line);
428 function cancelNotification()
430 for(var i=
0; i < notificationRequests.length; i++) {
432 var result = calendarService.IDataSource.Cancel(notificationRequests[i]);
433 if (result.ErrorCode)
434 error('cancelNotification failed with error code ' + result.ErrorCode);
436 error(
"cancelNotification: " + e + ', line ' + e.line);
441 function callback(transId, eventCode, result)
443 log(
"callback(): panelNum: " + panelNum +
" transId: " + transId +
" eventCode: " + eventCode +
" result.ErrorCode: " + result.ErrorCode);
444 lastReloadTime = null; // force calendar data reload on next update
448 function settingsCallback(transId, eventCode, result)
450 log(
"settingsCallback(): panelNum: " + panelNum +
" transId: " + transId +
" eventCode: " + eventCode +
" result.ErrorCode: " + result.ErrorCode);
454 function parseDate(dateString)
457 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:
458 Wednesday,
26 August,
2009 24:
00:
00
459 Wednesday,
26 August,
2009 12:
00:
00 am
460 Wednesday, August
26,
2009 12:
00:
00 am
461 Wednesday,
2009 August,
26 12:
00:
00 am
462 Wednesday,
2009 August,
28 8.00.00 pm
463 Wednesday,
2009 August,
28 08:
00:
00 PM
467 if (dateString ==
"" || dateString == null || dateString == undefined)
469 if (dateString instanceof Date) {
470 // we already have a date object, no need to parse string here
474 var dateArr = (dateString + '').replace(/,/g, '').replace(/\./g, ':').replace(/ /g, ' ').split(' ');
475 if (dateArr.length !=
5 && dateArr.length !=
6)
479 var weekDay = dateArr[
0];
480 var day = dateArr[
1];
481 var month = dateArr[
2];
482 var year = dateArr[
3];
483 // make sure month is set properly
484 if (isNaN(parseInt(day))) {
490 if (isNaN(parseInt(year))) {
495 // make sure day and year are set properly
496 if (Number(day)
> Number(year)) {
501 month = months_translated[month];
504 var timeArr = dateArr[
4].split(':');
505 if (timeArr.length !=
3)
507 var hours = Number(timeArr[
0]);
508 var minutes = Number(timeArr[
1]);
509 var seconds = Number(timeArr[
2]);
510 if (dateArr.length ==
6 && dateArr[
5].toLowerCase() == 'pm' && hours <
12)
512 if (dateArr.length ==
6 && dateArr[
5].toLowerCase() == 'am' && hours ==
12)
515 result = new Date(year, month -
1, day, hours, minutes, seconds);
518 // take care of daylight saving time
519 if (config['enableDaylightSaving'].Value) {
521 // determine if date is in summer or winter time
522 var dateSummerTime = isSummertime(result);
524 // work around bug in Nokias calendar api resulting in dates within a different DST to be off by
1 hour
525 if (summertime && !dateSummerTime) {
526 result = new Date(result.getTime() -
1000 *
60 *
60 * config['daylightSavingOffset'].Value); // -
1 hour
527 log('parseDate(): fixing time -
1h: ' + result);
529 else if (!summertime && dateSummerTime) {
530 result = new Date(result.getTime() +
1000 *
60 *
60 * config['daylightSavingOffset'].Value); // +
1 hour
531 log('parseDate(): fixing time +
1h: ' + result);
538 function getWeekdayLocalized(date) {
539 var localizedString = date.toLocaleDateString();
540 if (localizedString.indexOf(
",") == -
1) {
541 return weekdays_translated[date.getDay()];
543 return localizedString.split(',')[
0];
546 // 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"
547 function formatDate(date, format)
549 var day = date.getDate().toString();
550 var month = (date.getMonth() +
1).toString();
551 while (day.length <
2) { day = '
0' + day; }
552 while (month.length <
2) { month = '
0' + month; }
554 if (config['showTodayAsText'].Value && isToday(date))
555 return '
<span class=
"today">' + config['todayText'].Value + '
</span>';
556 if (config['showTodayAsText'].Value && isTomorrow(date))
557 return '
<span class=
"tomorrow">' + config['tomorrowText'].Value + '
</span>';
559 if (format instanceof Date) {
560 // we don't know how to format this
561 if (config['dateFormat'].Value == 'auto' || config['dateFormat'].Value == 'DDMM')
562 return day + config['dateSeparator'].Value + month;
564 return month + config['dateSeparator'].Value + day;
566 var dateArr = format.replace(/,/g,'').replace(/\./g,':').replace(/ /g,' ').split(' ');
567 if (dateArr.length !=
5 && dateArr.length !=
6) {
568 // we don't know how to format this
569 if (config['dateFormat'].Value == 'auto' || config['dateFormat'].Value == 'DDMM')
570 return day + config['dateSeparator'].Value + month;
572 return month + config['dateSeparator'].Value + day;
576 if (config['dateFormat'].Value == 'MMDD')
578 else if (config['dateFormat'].Value == 'DDMM')
581 // config['dateFormat'].Value == 'auto', try to detect system setting
583 var day_ = dateArr[
1];
584 var month_ = dateArr[
2];
585 var year_ = dateArr[
3];
586 // make sure month is set properly
587 if (isNaN(parseInt(day_))) {
592 } else if (isNaN(parseInt(year_))) {
598 // make sure day and year are set properly
599 if (Number(day_)
> Number(year_))
604 return day + config['dateSeparator'].Value + month;
606 return month + config['dateSeparator'].Value + day;
609 function formatTime(date)
611 // date is a Date() object
612 var hour = date.getHours();
613 var minute = date.getMinutes();
615 // don't use Date().toLocaleTimeString() as it is utterly broken on newer firmwares
616 if (use12hoursTimeFormat) {
627 minute =
"0" + minute;
628 time = hour + timeFormatSeparator + minute +
" " + ap;
634 minute =
"0" + minute;
635 time = hour + timeFormatSeparator + minute;
638 if (config['showNowAsText'].Value && date.getTime() == now.getTime())
639 time = '
<span class=
"now">' + config['nowText'].Value + '
</span>';
640 log(
"formatTime(): " + time +
", use12hoursTimeFormat=" + use12hoursTimeFormat +
", timeFormatSeparator=" + timeFormatSeparator +
", date.toLocateTimeString(): " + date.toLocaleTimeString());
644 function updateData()
651 // check if we got additional or less calendars since our last update
652 var newCalendarList = listCalendars();
653 if (newCalendarList == null) {
654 // Something went wrong fetching the calendars list.
655 // This usually happens when a backup is being made.
656 // Retry the next time updateData() is called by
657 // resetting errorOccured
658 log('updateData(): listCalendars() failed, trying again later...');
659 cacheEntriesHtml = ''; // make sure we replace the currently shown error message on the next update
660 errorOccured = false;
663 if (newCalendarList.length != calendarList.length) {
664 calendarList = newCalendarList;
665 updateCalendarColors();
666 cancelNotification();
667 requestNotification();
668 lastReloadTime = null; // force calendar data reload on this update
673 // only reload calendar data every
6 hours, visual updates occure more often
674 if (!lastReloadTime || now.getTime() - lastReloadTime.getTime()
> reloadInterval) {
675 log('updateData(): reloading calendar data');
677 // meetings have time
678 // 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
679 summertime = isSummertime(now); // cache summer time info for today
680 var meetingList = [];
681 for(var i=
0; i < calendarList.length; i++) {
682 // ignore excluded calendars
683 if (config['excludedCalendars'].Value.indexOf(calendarList[i]) != -
1)
685 var meetingListFiltering = {
686 Type:'CalendarEntry',
688 CalendarName: calendarList[i],
689 StartRange: (new Date(now.getFullYear(), now.getMonth(), now.getDate(),
0,
0,
0)),
690 EndRange: (new Date(now.getFullYear(), now.getMonth() + config['monthRange'].Value, now.getDate(),
0,
0,
0))
693 var meetingResult = calendarService.IDataSource.GetList(meetingListFiltering);
694 if (meetingResult.ErrorCode !=
0)
695 throw(
"Error fetching calendar data: " + meetingResult.ErrorCode + ': ' + meetingResult.ErrorMessage);
696 var list = meetingResult.ReturnValue;
697 meetingList = meetingList.concat(listToArray(list, calendarList[i]));
699 log(
"updateData(): meetingList.sort()");
700 meetingList.sort(sortCalendarEntries);
702 // todos don't, they start on
00:
00 hrs., but should be visible anyway
703 // this will generate a list of passed todos. We have to check if they have been marked as
"done" yet
704 if (config['includeTodos'].Value) {
705 var todayTodoList = [];
706 for(var i=
0; i < calendarList.length; i++) {
707 // ignore excluded calendars
708 if (config['excludedCalendars'].Value.indexOf(calendarList[i]) != -
1)
710 var todayTodoListFiltering = {
711 Type:'CalendarEntry',
713 CalendarName: calendarList[i],
715 StartRange: (new Date(now.getFullYear() -
1, now.getMonth(), now.getDate(),
0,
0,
0)),
716 EndRange: (new Date(now.getFullYear(), now.getMonth(), now.getDate(),
0,
0,
1))
719 var todayTodoResult = calendarService.IDataSource.GetList(todayTodoListFiltering);
720 var list = todayTodoResult.ReturnValue;
721 todayTodoList = todayTodoList.concat(listToArray(list, calendarList[i]));
723 log(
"updateData(): todayTodoList.sort()");
724 todayTodoList.sort(sortCalendarEntries);
725 entryLists = [todayTodoList, meetingList];
727 entryLists = [meetingList];
729 lastReloadTime = new Date();
731 error('loading Calendar items list:' + e + ', line ' + e.line);
741 var fontsize = getDefaultFontSize()[
1] + 'px';
742 var lineheight = fontsize;
744 if (config['fontsize'].Value == config['fontsize'].ValidValues[
0]) {
745 fontsize = parseInt(
72 / config['eventsPerWidget'].Value) + 'px';
746 lineheight = parseInt(
72 / config['eventsPerWidget'].Value) + 'px';
749 if (config['fontsize'].Value != config['fontsize'].ValidValues[
0]) {
750 fontsize = config['fontsize'].Value + 'px';
751 lineheight = fontsize;
753 changeCssClass('.icon', config['cssStyle_icon'].Value + '; width:' + fontsize + '; height:' + fontsize + ';');
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 = config[
"maxNumberOfEventsOnFullscreen"].Value; // 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><div class=
"calendar' + calendarColors[entry.CalendarName] + '" style=
"height:' + (lineheight.split("px
")[0] - 1) + 'px; width:4px;"></div></td>';
900 if (config['showIcons'].Value)
901 entriesHtml += '
<td><img class=
"icon" align=
"top" src=
"' + entry.Type + '.png" /></td>';
903 entriesHtml += '
<td style=
"padding:0px;"></td>';
905 // some languages have very strange locale date formats, can't parse all those. Also some todos don't have dates at all.
906 entriesHtml += '
<td colspan=
"4"><span class=
"date">' + entryDate + '
</span> ';
908 var weekDay = getWeekdayLocalized(date).substr(
0,config['weekDayLength'].Value);
909 log('date.toLocaleDateString(): ' + date.toLocaleDateString());
910 log('weekDay: ' + weekDay);
911 var time = formatTime(date);
912 var dateStr = formatDate(date, entryDate);
913 if (entry.Type == 'ToDo' && overdue && config['markOverdueTodos'].Value) {
914 dateStr = '
<span class=
"overdue">' + config['overdueText'].Value + '
</span>';
915 entriesHtml += '
<td colspan=
"4" width=
"1px"><span class=
"date">' + dateStr + '
</span> ';
916 } else if (entry.Type == 'ToDo' || entry.Type == 'Anniversary' || entry.Type == 'DayEvent' || entry.Type == 'Reminder') {
917 if ((isToday(date) || isTomorrow(date)) && config['showTodayAsText'].Value) // show weekday if the date string is not text. looks odd otherwise
918 entriesHtml += '
<td colspan=
"4" width=
"1px"><span class=
"date">' + dateStr + '
</span> ';
920 entriesHtml += '
<td class=
"weekDay" width=
"1px">' + weekDay + '
</td><td width=
"1px" class=
"date">' + dateStr + '
</td><td colspan=
"2">';
921 } else if (entry.Type == 'Meeting') {
922 if (config['showCombinedDateTime'].Value) {
924 entriesHtml += '
<td width=
"1px" colspan=
"4"><span class=
"today">' + time + '
</span> ';
925 else if (isTomorrow(date))
926 entriesHtml += '
<td width=
"1px" colspan=
"4"><span class=
"tomorrow">' + dateStr + '
</span> <span class=
"time">' + time + '
</span> ';
928 entriesHtml += '
<td width=
"1px" class=
"weekDay">' + weekDay + '
</td><td width=
"1px" class=
"date">' + dateStr + '
</td><td colspan=
"2">';
930 if ((isToday(date) || isTomorrow(date)) && config['showTodayAsText'].Value)
931 entriesHtml += '
<td colspan=
"4" width=
"1px"><span class=
"today">' + dateStr + '
</span> <span class=
"time">' + time + '
</span> ';
933 entriesHtml += '
<td width=
"1px" class=
"weekDay">' + weekDay + '
</td><td width=
"1px" class=
"date">' + dateStr + '
</td><td width=
"1px" class=
"time">' + time + '
</td><td>';
937 entriesHtml += '
<span class=
"description">' + Summary + '
</span></td></tr>';
940 entriesHtml += '
</table>';
942 entriesHtml = entriesHtml + '
</td></tr></table>';
943 if (config['showNothingText'].Value && entriesHtml == '
<table></table>') {
944 var text = config['nothingText'].Value.replace(/%d/, config['monthRange'].Value);
945 entriesHtml = '
<div style=
"width:295px; height:75px; text-align:center; line-height:75px; overflow:visible;">' + text + '
</div>';
947 log(
"output: " + entriesHtml);
948 if (cacheEntriesHtml != entriesHtml) {
950 document.getElementById('calendarList').innerHTML = entriesHtml;
952 document.getElementById('fullscreenCalendarList').innerHTML = entriesHtml;
953 cacheEntriesHtml = entriesHtml;
956 lastUpdateTime = new Date();
958 error('displaying list:' + e + ', line ' + e.line);
963 // called by handleOnShow() and onResize events
964 function updateScreen()
966 log('updateScreen(): mode=' + mode + ', window.innerHeight=' + window.innerHeight);
968 // check if opening fullscreen
970 // Note: according to Nokia's documentation, an innerHeight of
>91 is an indicator for fullscreen view.
971 // However a bug in E6's firmware causes different window widths and heights (disabled compatibility scaling).
972 // So far, values of
104 and
115 for window.innerHeight were reported, we use a safty margin here and check
974 if( window.innerHeight
> 150 && mode ==
0) {
976 cacheEntriesHtml = '';
977 document.getElementById('body').style.backgroundImage =
"";
980 else if (window.innerHeight <=
150 && mode !=
0) {
982 cacheEntriesHtml = '';
987 updateHomescreen(); // check for screen rotation
992 function handleOnShow()
996 var time = new Date();
997 if (time.getTime() - lastUpdateTime.getTime()
> config['updateDataInterval'].Value *
60 *
1000) {
998 log('updateScreen(): force updateData() because last update was too long ago (' + (time.getTime() - lastUpdateTime.getTime()) /
1000 + 's)');
1001 setUpdateTimer(); // reinitialize update timer
1005 function launchCalendar()
1008 widget.openApplication(config['calendarApp'].Value,
"");
1009 if (config['hideWidgetOnCalendarOpen'].Value)
1012 error('starting Calendar App');
1019 log('New widget instance starting up...');
1022 // call calendar service
1023 if (device !=
"undefined")
1024 calendarService = device.getServiceObject(
"Service.Calendar",
"IDataSource");
1026 throw('device object does not exist');
1028 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>');
1032 calendarList = listCalendars();
1034 updateCalendarColors();
1037 requestNotification();
1038 document.getElementById(
"settingsTitle").innerHTML = getLocalizedText('menu.settings');
1040 if (window.innerHeight
> 91) {
1041 mode =
0; // we're starting fullscreen, we set mode to homescreen in order to let updateScreen() do all the work for us
1046 log(
"init(): updateScreen()");
1048 if (config['useBackgroundImage'].Value)
1049 // check for screen rotation every
1 secs
1050 screenRotationTimer = window.setInterval('checkOrientation()',
1000 *
1);
1052 // call updateScreen() when widget changes from background to forground
1053 window.widget.onshow = handleOnShow;
1055 log(
"init(): finished...");
1057 statupSuccessful = true;
1060 function checkOrientation()
1064 updateHomescreen(); // check for screen rotation
1067 function setUpdateTimer()
1069 updateTimer = window.setInterval('updateTimerCallback()',
1000 *
60 * config['updateDataInterval'].Value);
1072 function clearUpdateTimer()
1074 window.clearInterval(updateTimer);
1077 function updateTimerCallback()
1079 log(
"updateTimerCallback()");
1083 function createMenu()
1085 window.menu.setLeftSoftkeyLabel(
"",null);
1086 window.menu.setRightSoftkeyLabel(
"",null);
1088 var menuSettings = new MenuItem(getLocalizedText('menu.settings'), id++);
1089 var menuCallApp = new MenuItem(getLocalizedText('menu.openCalendarApp'), id++);
1090 var menuHelp = new MenuItem(getLocalizedText('menu.help'), id++);
1091 var menuUpdate = new MenuItem(getLocalizedText('menu.update'), id++);
1092 var menuAbout = new MenuItem(getLocalizedText('menu.about'), id++);
1093 menuSettings.onSelect = showSettings;
1094 menuAbout.onSelect = showAbout;
1095 menuCallApp.onSelect = launchCalendar;
1096 menuUpdate.onSelect = showUpdate;
1097 menuHelp.onSelect = showHelp;
1098 window.menu.clear();
1099 window.menu.append(menuCallApp);
1100 window.menu.append(menuSettings);
1101 window.menu.append(menuHelp);
1102 window.menu.append(menuUpdate);
1103 window.menu.append(menuAbout);
1106 function showSettings()
1110 document.getElementById(
"settingsView").style.display =
"block";
1111 document.onclick = null;
1113 window.menu.setLeftSoftkeyLabel(getLocalizedText('settings.save'), function()
1115 for (var key in config) {
1116 if (config[key].Type == 'String')
1117 config[key].Value = document.forms[
0].elements[
"settings." + key].value;
1118 else if (config[key].Type == 'Int') {
1119 config[key].Value = parseInt(document.forms[
0].elements[
"settings." + key].value);
1120 if (config[key].Value <
0 || isNaN(config[key].Value))
1121 config[key].Value = config[key].Default;
1123 else if (config[key].Type == 'Bool')
1124 config[key].Value = document.forms[
0].elements[
"settings." + key].checked;
1125 else if (config[key].Type == 'UID') {
1126 config[key].Value = parseInt(document.forms[
0].elements[
"settings." + key].value);
1127 if (isNaN(config[key].Value))
1128 config[key].Value = config[key].Default;
1130 else if (config[key].Type == 'Enum') {
1131 config[key].Value = document.forms[
0].elements[
"settings." + key].value;
1132 if (config[key].ValidValues.indexOf(config[key].Value) == -
1)
1133 config[key].Value = config[key].Default;
1135 else if (config[key].Type == 'Array') {
1136 if (key == 'excludedCalendars') {
1137 config[key].Value = new Array();
1138 for(var i=
0; i < calendarList.length; i++) {
1139 var element = document.forms[
0].elements[
"settings." + key +
"." + calendarList[i]];
1140 if (element != null && element.checked == false)
1141 config[key].Value.push(calendarList[i]);
1154 window.menu.setRightSoftkeyLabel(getLocalizedText('settings.cancel'), function()
1160 var settingsHtml = '
<form>';
1161 for (var key in config) {
1162 if (config[key].Type == 'String') {
1164 if (key.substring(
0,
9) ==
"cssStyle_")
1165 prefix = getLocalizedText('settings.cssStyle_prefix');
1166 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 />';
1168 else if (config[key].Type == 'Int')
1169 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 />';
1170 else if (config[key].Type == 'Bool')
1171 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 />';
1172 else if (config[key].Type == 'UID')
1173 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 />';
1174 else if (config[key].Type == 'Enum') {
1175 settingsHtml += '
<table><tr><td>' + getLocalizedText('settings.name.' + key) + '
<br /><select name=
"settings.' + key + '" size=
"1">';
1176 for(var i =
0; i < config[key].ValidValues.length; i++) {
1177 var text = getLocalizedText('settings.validValues.' + key + '.' + config[key].ValidValues[i]);
1178 if (text.indexOf('ERROR') ==
0)
1179 text = config[key].ValidValues[i];
1180 settingsHtml += '
<option value=
"' + config[key].ValidValues[i] + '"' + (config[key].Value == config[key].ValidValues[i] ? '
selected=
"selected"' : '') + '
>' + text + '
</option>';
1182 settingsHtml += '
</select></div></td>' + printHintBox(getLocalizedText('settings.info.' + key)) + '
<hr />';
1184 else if (config[key].Type == 'Array') {
1185 settingsHtml += '
<table><tr><td>' + getLocalizedText('settings.name.' + key) + '
<br />';
1186 if (key == 'excludedCalendars') {
1187 for(var i=
0; i < calendarList.length; i++) {
1188 var checked = '
checked=
"checked"';
1189 if (config[key].Value.indexOf(calendarList[i]) != -
1)
1191 settingsHtml += '
<input name=
"settings.' + key + '.' + calendarList[i] + '" type=
"checkbox" value=
"' + calendarList[i] + '" ' + checked + '
/> ' + calendarList[i] + '
<br />';
1194 settingsHtml += '
</td>' + printHintBox(getLocalizedText('settings.info.' + key)) + '
<hr />';
1197 settingsHtml += '
<input name=
"reset" type=
"button" value=
"' + getLocalizedText('settings.restoreDefaults') + '" onclick=
"javascript:restoreDefaultSettings();showSettings();" />';
1198 settingsHtml += '
</form>';
1199 document.getElementById(
"settingsList").innerHTML = settingsHtml;
1202 function changeCssClass(classname, properties)
1204 for(var i =
0; i < document.styleSheets[
0]['cssRules'].length; i++)
1206 if (document.styleSheets[
0]['cssRules'][i].selectorText == classname) {
1207 document.styleSheets[
0].deleteRule(i);
1208 document.styleSheets[
0].insertRule(classname + ' { ' + properties + ' }', document.styleSheets[
0]['cssRules'].length);
1214 function updateCssClasses()
1216 for(var key in config) {
1217 changeCssClass(getLocalizedText('settings.name.' + key), config[key].Value);
1221 function getSettingsCalEntryId()
1223 if (settingsCalEntryId == null) {
1224 // check if entry already exists
1225 var listFiltering = {
1226 Type:'CalendarEntry',
1228 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!
1229 EndRange: new Date(
2000,
0,
2),
1230 SearchText: 'ComingNext Settings|',
1236 result = calendarService.IDataSource.GetList(listFiltering);
1237 if (result.ErrorCode)
1238 throw(result.ErrorMessage);
1241 error(
"getSettingsCalEntryId: GetList() failed: " + e + ', line ' + e.line);
1244 var list = result.ReturnValue;
1245 var entry = list.getNext();
1246 if (entry != undefined) {
1247 settingsCalEntryId = entry.LocalId;
1248 log(
"settingsCalEntryId=" + settingsCalEntryId);
1250 else { // create settings item
1251 var item = new Object();
1252 item.Type =
"DayEvent";
1253 item.StartTime = new Date(
2000,
0,
1);
1254 item.Summary =
"ComingNext Settings|";
1256 var criteria = new Object();
1257 criteria.Type =
"CalendarEntry";
1258 criteria.Item = item;
1261 var result = calendarService.IDataSource.Add(criteria);
1262 if (result.ErrorCode)
1263 throw(result.ErrorMessage);
1265 error(
"getSettingsCalEntryId: " + e + ', line ' + e.line);
1268 getSettingsCalEntryId();
1273 function restoreDefaultSettings()
1275 for (var key in config)
1276 config[key].Value = config[key].Default;
1279 function loadSettings()
1281 getSettingsCalEntryId();
1282 var listFiltering = {
1283 Type:'CalendarEntry',
1285 LocalId: settingsCalEntryId
1290 result = calendarService.IDataSource.GetList(listFiltering);
1291 if (result.ErrorCode)
1292 throw(result.ErrorMessage);
1295 error(
"loadSettings: GetList() failed: " + e + ', line ' + e.line);
1298 var entry = result.ReturnValue.getNext();
1299 if (entry != undefined) {
1300 log(
"Loading Settings...");
1301 // only reload settings if they chanced since the last reload
1302 if (settingsCache != entry.Summary)
1304 restoreDefaultSettings();
1305 var stringlist = entry.Summary.split(
"|");
1306 // skip the first two entries, those contain header and version info
1307 for(var i =
2; i < stringlist.length -
1; i++) {
1308 var pair = stringlist[i].split('=');
1310 var value = pair[
1];
1311 if (key == null || value == null || config[key] == null) {
1312 log('Warning: unknown or invalid setting: ' + stringlist[i]);
1315 log('stringlist[' + i + ']: ' + key + '=\'' + value + '\'');
1316 if (config[key].Type == 'Int') {
1317 config[key].Value = Number(value);
1318 if (isNaN(config[key].Value))
1319 config[key].Value = config[key].Default;
1321 else if (config[key].Type == 'String')
1322 config[key].Value = value;
1323 else if (config[key].Type == 'Bool')
1324 config[key].Value = (value == 'true')
1325 else if (config[key].Type == 'Enum')
1326 config[key].Value = value;
1327 else if (config[key].Type == 'UID') {
1328 config[key].Value = Number(value);
1329 if (isNaN(config[key].Value))
1330 config[key].Value = config[key].Default;
1332 else if (config[key].Type == 'Array') {
1333 config[key].Value = value.split(
"^");
1334 if (config[key].Value.length ==
1 && config[key].Value[
0] ==
"") {
1335 config[key].Value = [];
1339 settingsCache = entry.Summary;
1343 log(
"Settings already cached and did not change");
1347 error(
"Failed to load settings, calendar entry could not be found");
1351 function saveSettings()
1353 getSettingsCalEntryId();
1354 var item = new Object();
1355 item.Type =
"DayEvent";
1356 item.StartTime = new Date(
2000,
0,
1);
1357 item.LocalId = settingsCalEntryId;
1358 item.Summary =
"ComingNext Settings|" + version +
"|";
1360 for (var key in config) {
1361 if (config[key].Type == 'Int')
1362 item.Summary += key +
"=" + config[key].Value.toString() +
"|";
1363 else if (config[key].Type == 'String')
1364 item.Summary += key +
"=" + config[key].Value +
"|";
1365 else if (config[key].Type == 'Bool')
1366 item.Summary += key +
"=" + (config[key].Value ? 'true' : 'false') +
"|";
1367 else if (config[key].Type == 'Enum')
1368 item.Summary += key +
"=" + config[key].Value +
"|";
1369 else if (config[key].Type == 'UID')
1370 item.Summary += key +
"=" + config[key].Value.toString() +
"|";
1371 else if (config[key].Type == 'Array')
1372 item.Summary += key +
"=" + config[key].Value.join(
"^") +
"|";
1374 settingsCache = item.Summary;
1376 var criteria = new Object();
1377 criteria.Type =
"CalendarEntry";
1378 criteria.Item = item;
1380 log(
"Saving settings to calendar entry: " + item.Summary);
1382 var result = calendarService.IDataSource.Add(criteria);
1383 if (result.ErrorCode)
1384 throw(result.ErrorMessage);
1386 error(
"saveSettings: " + e + ', line ' + e.line);
1389 lastReloadTime = null; // force calendar data reload on next update
1394 function toggleVisibility(elementId)
1396 if (document.getElementById(elementId).style.display ==
"none")
1397 document.getElementById(elementId).style.display =
"block";
1399 document.getElementById(elementId).style.display =
"none";
1403 function printHintBox(text)
1406 return '
<td width=
"1%" align=
"right" onclick=
"javascript:toggleVisibility(\'info' + uniqueId + '\')">' + getLocalizedText('settings.help') + '
</td></tr></table>'+
1407 '
<div class=
"settingsInfo" id=
"info' + uniqueId + '" style=
"display:none">' + text + '
</div>';
1410 function showAbout()
1414 document.getElementById(
"aboutView").style.display =
"block";
1415 document.onclick = null;
1417 window.menu.setLeftSoftkeyLabel(
" ", function(){});
1418 window.menu.setRightSoftkeyLabel(getLocalizedText('softkey.back'), function()
1424 //document.getElementById(
"aboutView").innerHTML = 'aboutView';
1425 document.getElementById(
"name").innerHTML =
"Coming Next " + version;
1428 function showHelp() {
1429 widget.openURL('http://comingnext.sf.net/help');
1432 function updateFullscreen()
1436 function showFullscreen()
1438 log(
"showFullscreen()");
1440 document.getElementById(
"fullscreenView").style.display =
"block";
1441 document.getElementById('body').className =
"backgroundFullscreen";
1443 document.onclick = launchCalendar;
1448 function getBackgroundImage()
1453 if (config['backgroundImageLocation'].Value == config['backgroundImageLocation'].ValidValues[
0]) // internal
1454 bgImage = 'background_' + orientation + '.png';
1456 bgImage = 'C:/Data/background_' + panelNum + '_' + orientation + '.png';
1460 function updateHomescreen()
1462 if (config['useBackgroundImage'].Value) {
1463 // check if we have a completely unknown screen resolution
1464 var screenHeight = screen.height;
1465 var screenWidth = screen.width;
1466 if (screenHeight !=
640 && screenHeight !=
480 && screenHeight !=
360)
1467 screenHeight =
360; // we can only assume we're in portrait mode, so we set the screen dims as needed for the following code
1468 if (screenWidth !=
640 && screenWidth !=
480 && screenWidth !=
360)
1469 screenWidth =
640; // we can only assume we're in portrait mode, so we set the screen dims as needed for the following code
1471 // check for screen rotation
1472 if (orientation != 'portrait' && ((screenWidth ==
360 && screenHeight ==
640) || (screenWidth ==
640 && screenHeight ==
480))) {
1473 window.widget.prepareForTransition(
"fade");
1474 orientation = 'portrait';
1475 document.getElementById('body').style.backgroundImage = 'url(' + getBackgroundImage() + ')';
1476 document.getElementById('body').style.backgroundColor = 'none';
1477 window.widget.performTransition();
1478 } else if (orientation != 'landscape' && ((screenWidth ==
640 && screenHeight ==
360) || (screenWidth ==
480 && screenHeight ==
640))) {
1479 window.widget.prepareForTransition(
"fade");
1480 orientation = 'landscape';
1481 document.getElementById('body').style.backgroundImage = 'url(' + getBackgroundImage() + ')';
1482 document.getElementById('body').style.backgroundColor = 'none';
1483 window.widget.performTransition();
1485 else if (document.getElementById('body').style.backgroundImage ==
"")
1487 document.getElementById('body').style.backgroundImage = 'url(' + getBackgroundImage() + ')';
1492 function showHomescreen()
1494 log(
"showHomescreen()");
1496 document.getElementById(
"homescreenView").style.display =
"block";
1497 document.getElementById('body').className =
"background";
1498 document.onclick = null;
1502 function getLocalizedText(p_Txt)
1504 if (localizedText[p_Txt])
1505 return localizedText[p_Txt];
1507 return 'ERROR: missing translation for ' + p_Txt;
1510 function showUpdate()
1514 document.getElementById(
"updateView").style.display =
"block";
1515 document.onclick = null;
1517 window.menu.setLeftSoftkeyLabel(getLocalizedText('update.checknow'), function(){
1520 window.menu.setRightSoftkeyLabel(getLocalizedText('softkey.back'), function()
1526 document.getElementById(
"currentVersion").innerHTML = getLocalizedText(
"update.current") + version;
1530 function checkForUpdate()
1532 // asynch XHR to server url
1533 reqV = new XMLHttpRequest();
1534 reqV.onreadystatechange = checkForUpdateCallback;
1535 document.getElementById(
"updateDiv").innerHTML = getLocalizedText(
"update.checking");
1536 reqV.open(
"GET", versionURL, true);
1540 function checkForUpdateCallback()
1542 if (reqV.readyState ==
4) {
1543 if (reqV.status ==
200) {
1544 var resultXml = reqV.responseText;
1546 var div = document.getElementById(
"tmp");
1547 div.innerHTML = resultXml;
1548 var newVersion = div.getElementsByTagName('version')[
0].innerHTML;
1549 var newVersionURL = div.getElementsByTagName('url')[
0].innerHTML;
1551 if (version != newVersion) {
1552 document.getElementById(
"updateDiv").innerHTML = getLocalizedText(
"update.download").replace(/%
1/, newVersion).replace(/%
2/, newVersionURL);
1555 document.getElementById(
"updateDiv").innerHTML = getLocalizedText(
"update.nonewversion");
1560 document.getElementById(
"updateDiv").innerHTML = getLocalizedText(
"update.error") + reqV.status +
" " + reqV.responseText;
1565 function hideViews()
1567 document.getElementById(
"homescreenView").style.display =
"none";
1568 document.getElementById(
"fullscreenView").style.display =
"none";
1569 document.getElementById(
"aboutView").style.display =
"none";
1570 document.getElementById(
"settingsView").style.display =
"none";
1571 document.getElementById(
"updateView").style.display =
"none";
1574 function listCalendars()
1584 DefaultCalendar: false
1588 var calendarsResult = calendarService.IDataSource.GetList(criteria);
1589 if (calendarsResult.ErrorCode !=
0)
1590 throw(
"Error fetching list of calendars: " + calendarsResult.ErrorCode + ': ' + calendarsResult.ErrorMessage);
1591 var calendarListIterator = calendarsResult.ReturnValue;
1596 while (( item = calendarListIterator.getNext()) != undefined ) {
1597 calendars[count++] = item;
1599 log(
"Available Calendars: " + calendars.join(
", "));
1602 error('listing calendars:' + e + ', line ' + e.line);
1607 // Copies all objects and their properties to an array. Data is copied so nothing gets lost when the reference is removed
1608 // 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
1609 function listToArray(list, calendarName)
1611 var array = new Array();
1614 while (( item = list.getNext()) != undefined ) {
1615 var itemCopy = new Object();
1616 for(var i=
0; i < entryFields.length; i++) {
1617 itemCopy[entryFields[i]] = item[entryFields[i]];
1619 // for some reason, the CalendarName property is never correctly queried, so we assign it manually here
1620 if (!itemCopy['CalendarName']) {
1621 itemCopy['CalendarName'] = calendarName;
1623 if (config['anonymizeLogging'].Value && config['enableLogging'].Value) {
1624 if (itemCopy['Summary'])
1625 itemCopy['Summary'] = getHashForString(itemCopy['Summary']);
1626 if (itemCopy['Location'])
1627 itemCopy['Location'] = getHashForString(itemCopy['Location']);
1629 array.push(itemCopy);
1630 txt += array[array.length -
1].Summary +
", ";
1632 log(
"listToArray(): " + txt);
1636 function sortCalendarEntries(a, b)
1639 log(
"sortCalendarEntries(" + a.Summary +
"," + b.Summary +
")");
1641 if (a.InstanceStartTime != null) {
1642 atime = a.InstanceStartTime;
1644 else if (a.StartTime != null) {
1645 atime = a.StartTime;
1647 else if (a.InstanceEndTime != null) {
1648 atime = a.InstanceEndTime;
1650 else if (a.EndTime != null) {
1654 if (b.InstanceStartTime != null) {
1655 btime = b.InstanceStartTime;
1657 else if (b.StartTime != null) {
1658 btime = b.StartTime;
1660 else if (b.InstanceEndTime != null) {
1661 btime = b.InstanceEndTime;
1663 else if (b.EndTime != null) {
1667 if (atime && btime) {
1669 atime = parseDate(atime);
1670 btime = parseDate(btime);
1672 // sort by date & time
1673 if (atime < btime) {
1676 else if (atime
> btime) {
1680 else if (a.Type != b.Type) {
1681 if (a.Type < b.Type) {
1684 else if (a.Type
> b.Type) {
1688 // sort by description
1689 else if (a.Summary && b.Summary && a.Summary != b.Summary) {
1690 if (a.Summary < b.Summary) {
1693 else if (a.Summary
> b.Summary) {
1698 // NOTE: events my have no date information at all. In that case, we list events without date first
1699 else if (atime && !btime) {
1702 else if (!atime && btime) {
1705 else if (!atime && !btime) {
1707 if (a.Type != b.Type) {
1708 if (a.Type < b.Type) {
1711 else if (a.Type
> b.Type) {
1715 // sort by description
1716 else if (a.Summary && b.Summary && a.Summary != b.Summary) {
1717 if (a.Summary < b.Summary) {
1720 else if (a.Summary
> b.Summary) {
1729 function updateCalendarColors()
1732 calendarColors = [];
1733 if (calendarList.length
> maxColors) {
1734 log(
"updateCalendarColors(): Warning: more calendars than available indicator colors");
1736 for(var i=
0; i < calendarList.length; i++) {
1737 calendarColors[calendarList[i]] = (i % maxColors) +
1;
1741 function log(message)
1743 if (config['enableLogging'].Value) {
1744 console.info(message);
1748 function getDefaultFontSize()
1750 if (defaultFontSize == null) {
1751 var pa = document.body;
1752 var who = document.createElement('div');
1753 who.className = 'defaultEm';
1754 who.appendChild(document.createTextNode('M'));
1755 pa.appendChild(who);
1756 var fs = [who.offsetWidth, who.offsetHeight];
1757 pa.removeChild(who);
1758 defaultFontSize = fs;
1760 return defaultFontSize;
1763 function getHashForString(string)
1765 // cheap hashing, loosly based on Java's String.hashCode()
1766 for (var hash =
0, i =
0; i < string.length; i++)
1767 hash = (hash <<
5) - hash + string.charCodeAt(i);
1768 hash = hash & hash; // Convert to
32bit integer
1771 return hash.toString(
16).toUpperCase();
1776 <style type=
"text/css">
1778 table { margin:
0px; padding:
0px; border-spacing:
0px; border-collapse: collapse; }
1779 td { padding:
0px
5px
0px
0px; white-space:nowrap; overflow:visible; margin:
0px; }
1780 hr { color:#ffffff; background-color:#ffffff; height:
1px; text-align:left; border-style:none; }
1781 .settingsInfo { display:none; font-style:italic; }
1782 .title { font-weight:bold; font-size:
14pt; }
1783 .textInput { width:
90%; }
1784 .credits { margin-left:
40px; text-indent: -
20px; margin-bottom:
0px; }
1785 #homescreenView { width:
312px; height:
82px; overflow:hidden; }
1786 #calendarList { position:absolute; left:
5px; top:
0px; width:
307px; height:
82px; overflow:hidden; }
1787 #name { text-align:center; }
1788 #appicon { display: block; margin-left: auto; margin-right: auto; margin-top:
10px; }
1789 #smallappicon { width:
22px; height:
22px; margin-right:
10px; float:left; }
1790 .defaultEm { font-size:
1em; position:absolute; line-height:
1; padding:
0; visibility:hidden; }
1795 <body onload=
"javascript:setTimeout('init()', 10)" onresize=
"javascript:updateScreen()" id=
"body" class=
"background">
1796 <div id=
"homescreenView">
1797 <div id=
"calendarList">loading...
</div>
1799 <div id=
"fullscreenView" style=
"display:none;">
1800 <img src=
"Icon.png" id=
"smallappicon">
1801 <h1 class=
"title">Coming Next
</h1>
1803 <div id=
"fullscreenCalendarList">loading...
</div>
1805 <div id=
"settingsView" style=
"display:none">
1806 <img src=
"Icon.png" id=
"smallappicon">
1807 <h1 id=
"settingsTitle" class=
"title">Settings
</h1>
1809 <div id=
"settingsList"></div>
1811 <div id=
"aboutView" style=
"display:none">
1812 <img src=
"Icon.png" id=
"appicon">
1813 <h1 id=
"name">Coming Next
</h1>
1815 <p>Created by Dr. Cochambre and Michael Prager.
</p>
1816 <p>Contributions:
</p>
1817 <p class=
"credits">Paul Moore (bug fixes, new features and code cleanup)
</p>
1818 <p class=
"credits">Manfred Hanselmann (DST support)
</p>
1819 <p class=
"credits">Christophe Milsent (translation support & French translation)
</p>
1820 <p class=
"credits">Flavio Nathan (Portuguese-Brazilian translation)
</p>
1821 <p class=
"credits">Tokeda (Russian translation)
</p>
1822 <p class=
"credits">Marcella Ferrari (Italian translation)
</p>
1823 <p class=
"credits">Venos (Italian translation)
</p>
1824 <p class=
"credits">Francisco Rodero (Catalan translation)
</p>
1825 <p class=
"credits">zbigzbig20 (Polish translation)
</p>
1826 <p class=
"credits">Streamkeskus (Finnish translation)
</p>
1827 <p class=
"credits">renek (Czech translation)
</p>
1828 <p>This software is open source and licensed under the GPLv3.
</p>
1829 <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>
1832 <div id=
"updateView" style=
"display:none">
1833 <img src=
"Icon.png" id=
"smallappicon">
1834 <h1 class=
"title">Check for update
</h1>
1836 <div id=
"currentVersion">Coming Next ??
</div>
1837 <div id=
"updateDiv"></div>
1838 <div id=
"tmp" style=
"display:none;"></div>