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 //-------------------------------------------------------
91 if (d.getTimezoneOffset() < -
12*
60) {
92 d = new Date((new Date(d.getTime()-
24*
3600*
1000)).toLocaleString());
98 return fixDate(new Date());
101 var panelNum =
0; // use
1 for second panel
102 var version =
"1.37";
103 var versionURL =
"http://comingnext.sourceforge.net/version.xml";
104 var calendarService = null;
105 var cacheEntriesHtml = [];
106 var months_translated = [];
107 var weekdays_translated = [];
108 var orientation = '';
110 var mode =
0; //
0 = homescreen,
1 = fullscreen,
2 = settings,
3 = about,
4 = check for update
112 var settingsCalEntryId = null;
113 var settingsCache = null;
114 var notificationRequests = new Array();
115 var calendarList = [];
116 var calendarColors = [];
117 var updateTimer = null;
118 var screenRotationTimer = null;
119 var lastUpdateTime = now; // last time we updated the display
120 var lastReloadTime = null; // last time we fetched calendar data
121 var reloadInterval =
6 *
60 *
60 *
1000; // =
6 hours; time interval for reloading calendar data
122 var errorOccured = false;
123 var entryLists = null; // stores all fetched calendar entries until data is refreshed
124 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.
125 var use12hoursTimeFormat = false; // defines how time should be formated:
19:
00 or
07:
00 pm
126 var timeFormatSeparator =
":"; // format time
19:
00 or
19.00 depending on system setting
127 var defaultFontSize = null; // default browser font size will be set by init
129 // vars for daylight saving time
130 var summertime = false; // true, if current date is in summer, false if in winter
131 var daylightSavingDates = new Object(); // caches calculated DST winter and summer time shift dates
133 // this is a list of data fields a calendar event can have
147 function isLeapYear( year ) {
148 if (( year %
4 ==
0 && year %
100 !=
0 ) || year %
400 ==
0 )
154 function calcLeapYear(year, days)
156 if (isLeapYear(year))
162 function subToSunday(myDate, year, days, prevMonthDays)
164 for (i = myDate.getDay(); i
> 0 ;i--)
166 days -= prevMonthDays;
167 days = isLeapYear(year) ? --days : days;
171 function isSummertime(curDate)
176 // if we already calculated DST summer and winter time dates for this year, use cached values
177 var dst = daylightSavingDates[curDate.getFullYear()];
179 var thisYearS = new Date(curDate.getFullYear(),
3,
0,
0,
0,
0 );
180 var thisYearW = new Date(curDate.getFullYear(),
10,
0,
0,
0,
0 );
181 var nextYearS = new Date(curDate.getFullYear() +
1,
3,
0,
0,
0,
0 );
182 var nextYearW = new Date(curDate.getFullYear() +
1,
10,
0,
0,
0,
0 );
184 thisYearSDays = nextYearSDays =
90;
185 thisYearWDays = nextYearWDays =
304;
187 thisYearSDays = calcLeapYear(curDate.getFullYear(), thisYearSDays);
188 thisYearWDays = calcLeapYear(curDate.getFullYear(), thisYearWDays);
189 nextYearSDays = calcLeapYear(curDate.getFullYear() +
1, nextYearSDays);
190 nextYearWDays = calcLeapYear(curDate.getFullYear() +
1, nextYearWDays);
192 thisYearSDays = subToSunday(thisYearS, curDate.getFullYear(), thisYearSDays,
59);
193 thisYearWDays = subToSunday(thisYearW, curDate.getFullYear(), thisYearWDays,
273);
194 nextYearSDays = subToSunday(nextYearS, curDate.getFullYear() +
1, nextYearSDays,
59);
195 nextYearWDays = subToSunday(nextYearW, curDate.getFullYear() +
1, nextYearWDays,
273);
198 Summer: new Date (curDate.getFullYear(),
03-
1, thisYearSDays,
2,
0,
0),
199 Winter: new Date (curDate.getFullYear(),
10-
1, thisYearWDays,
2,
0,
0),
201 daylightSavingDates[curDate.getFullYear()] = dst;
204 if (dst.Summer < curDate)
206 if (dst.Winter < curDate)
208 if (summer && !winter)
214 function error(message)
216 console.info('Error: ' + message);
217 document.getElementById(
"calendarList").innerHTML = 'Error: ' + message;
218 document.getElementById(
"fullscreenCalendarList").innerHTML = 'Error: ' + message;
220 document.onclick = null;
223 function areDatesEqual(date1, date2)
225 return (date1.getFullYear() == date2.getFullYear() &&
226 date1.getMonth() == date2.getMonth() &&
227 date1.getDate() == date2.getDate());
230 function isTomorrow(date)
232 // tommorow = now +
1 day
233 // ToDo: some days can be shorter as
24 hours(daylight saving change day)
234 return areDatesEqual(date, new Date (now.getTime() +
24*
60*
60*
1000));
237 function isToday(date)
239 return areDatesEqual(date, now);
242 function collectLocales()
244 var tmpyear =
2000 + panelNum;
247 if (months_translated.length
> 0)
249 for (month =
0; month <
12; month++) {
250 var startDate = new Date(tmpyear, month,
15);
252 var item = new Object();
253 item.Type =
"DayEvent";
254 item.StartTime = startDate;
255 item.Summary =
"__temp" + month;
257 var criteria = new Object();
258 criteria.Type =
"CalendarEntry";
259 criteria.Item = item;
262 var result = calendarService.IDataSource.Add(criteria);
263 if (result.ErrorCode)
264 throw(result.ErrorMessage);
266 error(
"collectLocales: " + e + ', line ' + e.line);
269 for (weekday =
0; weekday <
7; weekday++) {
270 var startDate = new Date(
2000,
0,
2 + weekday,
12); // date that we know for sure is a sunday
272 var item = new Object();
273 item.Type =
"DayEvent";
274 item.StartTime = startDate;
275 item.Summary =
"__weekday_temp" + weekday;
277 var criteria = new Object();
278 criteria.Type =
"CalendarEntry";
279 criteria.Item = item;
282 var result = calendarService.IDataSource.Add(criteria);
283 if (result.ErrorCode)
284 throw(result.ErrorMessage);
286 error(
"collectLocales: " + e + ', line ' + e.line);
290 var startTime = new Date(tmpyear,
0,
1);
291 var endTime = new Date(tmpyear,
11,
31);
292 var listFiltering = {
293 Type:'CalendarEntry',
295 StartRange: startTime,
297 SearchText: '__temp',
301 var result = calendarService.IDataSource.GetList(listFiltering);
302 if (result.ErrorCode)
303 throw(result.ErrorMessage);
304 var list = result.ReturnValue;
306 error(
"collectLocales: " + e + ', line ' + e.line);
309 var ids = new Array();
315 while (list && (entry = list.getNext()) != undefined) {
316 dateArr = (entry.StartTime + '').replace(/,/g,'').replace(/\./g,':').replace(/ /g,' ').split(' ');
317 var day = dateArr[
1];
318 var month = dateArr[
2];
319 var year = dateArr[
3];
321 // make sure month is set properly
322 if (isNaN(parseInt(day))) {
326 } else if (isNaN(parseInt(year))) {
332 log(entry.StartTime + ' -
> ' + month + ' ' + counter);
333 ids[counter] = entry.id;
334 months_translated[month] = counter +
1;
338 error(
"collectLocales: " + e + ', line ' + e.line);
342 var startTime = new Date(
2000,
0,
2);
343 var endTime = new Date(
2000,
0,
9);
344 var listFiltering = {
345 Type:'CalendarEntry',
347 StartRange: startTime,
349 SearchText: '__weekday_temp',
353 var result = calendarService.IDataSource.GetList(listFiltering);
354 if (result.ErrorCode)
355 throw(result.ErrorMessage);
356 var weekdaylist = result.ReturnValue;
358 error(
"collectLocales: " + e + ', line ' + e.line);
366 while (weekdaylist && (entry = weekdaylist.getNext()) != undefined) {
367 detectTimeFormat(entry.StartTime + '');
368 curWeekday = (entry.StartTime + '').split(',')[
0];
369 log(entry.StartTime + ' -
> ' + curWeekday + ' ' + counter2);
370 ids[counter + counter2] = entry.id;
371 weekdays_translated[counter2] = curWeekday;
375 error(
"collectLocales: " + e + ', line ' + e.line);
380 var criteria = new Object();
381 criteria.Type =
"CalendarEntry";
386 var result = calendarService.IDataSource.Delete(criteria);
387 if (result.ErrorCode)
388 throw(result.ErrorMessage);
390 error('deleting temp calendar entries:' + e + ', line ' + e.line);
395 function stringEndsWith(str, suffix)
397 return str.indexOf(suffix, str.length - suffix.length) !== -
1;
400 // detects the system's current time format by parsing a native calendar timestamp (this is the only reliable formating across all devices and firmwares)
401 function detectTimeFormat(localeTimeString)
403 localeTimeString = localeTimeString.toLowerCase();
404 use12hoursTimeFormat = stringEndsWith(localeTimeString,
"am") || stringEndsWith(localeTimeString,
"pm");
405 timeFormatSeparator = localeTimeString.indexOf(
":") != -
1 ?
":" :
".";
408 function requestNotification()
410 var criteria = new Object();
411 criteria.Type =
"CalendarEntry";
412 criteria.Filter = new Object();
413 for(var i=
0; i < calendarList.length; i++) {
414 criteria.Filter.CalendarName = calendarList[i];
416 var notificationRequest = calendarService.IDataSource.RequestNotification(criteria, callback);
417 if (notificationRequest.ErrorCode)
418 error('requestNotification failed with error code ' + notificationRequest.ErrorCode);
419 notificationRequests.push(notificationRequest);
421 error(
"requestNotification: " + e + ', line ' + e.line);
425 var criteria2 = new Object();
426 criteria2.Type =
"CalendarEntry";
427 criteria2.Filter = new Object();
428 criteria2.Filter.LocalIdList = new Array();
429 criteria2.Filter.LocalIdList[
0] = settingsCalEntryId;
431 var notificationRequest = calendarService.IDataSource.RequestNotification(criteria2, settingsCallback);
432 if (notificationRequest.ErrorCode)
433 error('requestNotification failed with error code ' + notificationRequest.ErrorCode);
434 notificationRequests.push(notificationRequest);
436 error(
"requestNotification: " + e + ', line ' + e.line);
440 function cancelNotification()
442 for(var i=
0; i < notificationRequests.length; i++) {
444 var result = calendarService.IDataSource.Cancel(notificationRequests[i]);
445 if (result.ErrorCode)
446 error('cancelNotification failed with error code ' + result.ErrorCode);
448 error(
"cancelNotification: " + e + ', line ' + e.line);
453 function callback(transId, eventCode, result)
455 log(
"callback(): panelNum: " + panelNum +
" transId: " + transId +
" eventCode: " + eventCode +
" result.ErrorCode: " + result.ErrorCode);
456 lastReloadTime = null; // force calendar data reload on next update
460 function settingsCallback(transId, eventCode, result)
462 log(
"settingsCallback(): panelNum: " + panelNum +
" transId: " + transId +
" eventCode: " + eventCode +
" result.ErrorCode: " + result.ErrorCode);
466 function parseDate(dateString)
469 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:
470 Wednesday,
26 August,
2009 24:
00:
00
471 Wednesday,
26 August,
2009 12:
00:
00 am
472 Wednesday, August
26,
2009 12:
00:
00 am
473 Wednesday,
2009 August,
26 12:
00:
00 am
474 Wednesday,
2009 August,
28 8.00.00 pm
475 Wednesday,
2009 August,
28 08:
00:
00 PM
479 if (dateString ==
"" || dateString == null || dateString == undefined)
481 if (dateString instanceof Date) {
482 // we already have a date object, no need to parse string here
486 var dateArr = (dateString + '').replace(/,/g, '').replace(/\./g, ':').replace(/ /g, ' ').split(' ');
487 if (dateArr.length !=
5 && dateArr.length !=
6)
491 var weekDay = dateArr[
0];
492 var day = dateArr[
1];
493 var month = dateArr[
2];
494 var year = dateArr[
3];
495 // make sure month is set properly
496 if (isNaN(parseInt(day))) {
502 if (isNaN(parseInt(year))) {
507 // make sure day and year are set properly
508 if (Number(day)
> Number(year)) {
513 month = months_translated[month];
516 var timeArr = dateArr[
4].split(':');
517 if (timeArr.length !=
3)
519 var hours = Number(timeArr[
0]);
520 var minutes = Number(timeArr[
1]);
521 var seconds = Number(timeArr[
2]);
522 if (dateArr.length ==
6 && dateArr[
5].toLowerCase() == 'pm' && hours <
12)
524 if (dateArr.length ==
6 && dateArr[
5].toLowerCase() == 'am' && hours ==
12)
527 result = new Date(year, month -
1, day, hours, minutes, seconds);
530 // take care of daylight saving time
531 if (config['enableDaylightSaving'].Value) {
533 // determine if date is in summer or winter time
534 var dateSummerTime = isSummertime(result);
536 // work around bug in Nokias calendar api resulting in dates within a different DST to be off by
1 hour
537 if (summertime && !dateSummerTime) {
538 result = new Date(result.getTime() -
1000 *
60 *
60 * config['daylightSavingOffset'].Value); // -
1 hour
539 log('parseDate(): fixing time -
1h: ' + result);
541 else if (!summertime && dateSummerTime) {
542 result = new Date(result.getTime() +
1000 *
60 *
60 * config['daylightSavingOffset'].Value); // +
1 hour
543 log('parseDate(): fixing time +
1h: ' + result);
550 function getWeekdayLocalized(date) {
551 var localizedString = date.toLocaleDateString();
552 if (localizedString.indexOf(
",") == -
1) {
553 return weekdays_translated[date.getDay()];
555 return localizedString.split(',')[
0];
558 // 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"
559 function formatDate(date, format)
561 var day = date.getDate().toString();
562 var month = (date.getMonth() +
1).toString();
563 while (day.length <
2) { day = '
0' + day; }
564 while (month.length <
2) { month = '
0' + month; }
566 if (config['showTodayAsText'].Value && isToday(date))
567 return '
<span class=
"today">' + config['todayText'].Value + '
</span>';
568 if (config['showTodayAsText'].Value && isTomorrow(date))
569 return '
<span class=
"tomorrow">' + config['tomorrowText'].Value + '
</span>';
571 if (format instanceof Date) {
572 // we don't know how to format this
573 if (config['dateFormat'].Value == 'auto' || config['dateFormat'].Value == 'DDMM')
574 return day + config['dateSeparator'].Value + month;
576 return month + config['dateSeparator'].Value + day;
578 var dateArr = format.replace(/,/g,'').replace(/\./g,':').replace(/ /g,' ').split(' ');
579 if (dateArr.length !=
5 && dateArr.length !=
6) {
580 // we don't know how to format this
581 if (config['dateFormat'].Value == 'auto' || config['dateFormat'].Value == 'DDMM')
582 return day + config['dateSeparator'].Value + month;
584 return month + config['dateSeparator'].Value + day;
588 if (config['dateFormat'].Value == 'MMDD')
590 else if (config['dateFormat'].Value == 'DDMM')
593 // config['dateFormat'].Value == 'auto', try to detect system setting
595 var day_ = dateArr[
1];
596 var month_ = dateArr[
2];
597 var year_ = dateArr[
3];
598 // make sure month is set properly
599 if (isNaN(parseInt(day_))) {
604 } else if (isNaN(parseInt(year_))) {
610 // make sure day and year are set properly
611 if (Number(day_)
> Number(year_))
616 return day + config['dateSeparator'].Value + month;
618 return month + config['dateSeparator'].Value + day;
621 function formatTime(date)
623 // date is a Date() object
624 var hour = date.getHours();
625 var minute = date.getMinutes();
627 // don't use Date().toLocaleTimeString() as it is utterly broken on newer firmwares
628 if (use12hoursTimeFormat) {
639 minute =
"0" + minute;
640 time = hour + timeFormatSeparator + minute +
" " + ap;
646 minute =
"0" + minute;
647 time = hour + timeFormatSeparator + minute;
650 if (config['showNowAsText'].Value && date.getTime() == now.getTime())
651 time = '
<span class=
"now">' + config['nowText'].Value + '
</span>';
652 log(
"formatTime(): " + time +
", use12hoursTimeFormat=" + use12hoursTimeFormat +
", timeFormatSeparator=" + timeFormatSeparator +
", date.toLocateTimeString(): " + date.toLocaleTimeString());
656 function updateData()
663 // check if we got additional or less calendars since our last update
664 var newCalendarList = listCalendars();
665 if (newCalendarList == null) {
666 // Something went wrong fetching the calendars list.
667 // This usually happens when a backup is being made.
668 // Retry the next time updateData() is called by
669 // resetting errorOccured
670 log('updateData(): listCalendars() failed, trying again later...');
671 cacheEntriesHtml = ''; // make sure we replace the currently shown error message on the next update
672 errorOccured = false;
675 if (newCalendarList.length != calendarList.length) {
676 calendarList = newCalendarList;
677 updateCalendarColors();
678 cancelNotification();
679 requestNotification();
680 lastReloadTime = null; // force calendar data reload on this update
685 // only reload calendar data every
6 hours, visual updates occure more often
686 if (!lastReloadTime || now.getTime() - lastReloadTime.getTime()
> reloadInterval) {
687 log('updateData(): reloading calendar data');
689 // meetings have time
690 // 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
691 summertime = isSummertime(now); // cache summer time info for today
692 var meetingList = [];
693 for(var i=
0; i < calendarList.length; i++) {
694 // ignore excluded calendars
695 if (config['excludedCalendars'].Value.indexOf(calendarList[i]) != -
1)
697 var meetingListFiltering = {
698 Type:'CalendarEntry',
700 CalendarName: calendarList[i],
701 StartRange: (new Date(now.getFullYear(), now.getMonth(), now.getDate(),
0,
0,
0)),
702 EndRange: (new Date(now.getFullYear(), now.getMonth() + config['monthRange'].Value, now.getDate(),
0,
0,
0))
705 var meetingResult = calendarService.IDataSource.GetList(meetingListFiltering);
706 if (meetingResult.ErrorCode !=
0)
707 throw(
"Error fetching calendar data: " + meetingResult.ErrorCode + ': ' + meetingResult.ErrorMessage);
708 var list = meetingResult.ReturnValue;
709 meetingList = meetingList.concat(listToArray(list, calendarList[i]));
711 log(
"updateData(): meetingList.sort()");
712 meetingList.sort(sortCalendarEntries);
714 // todos don't, they start on
00:
00 hrs., but should be visible anyway
715 // this will generate a list of passed todos. We have to check if they have been marked as
"done" yet
716 if (config['includeTodos'].Value) {
717 var todayTodoList = [];
718 for(var i=
0; i < calendarList.length; i++) {
719 // ignore excluded calendars
720 if (config['excludedCalendars'].Value.indexOf(calendarList[i]) != -
1)
722 var todayTodoListFiltering = {
723 Type:'CalendarEntry',
725 CalendarName: calendarList[i],
727 StartRange: (new Date(now.getFullYear() -
1, now.getMonth(), now.getDate(),
0,
0,
0)),
728 EndRange: (new Date(now.getFullYear(), now.getMonth(), now.getDate(),
0,
0,
1))
731 var todayTodoResult = calendarService.IDataSource.GetList(todayTodoListFiltering);
732 var list = todayTodoResult.ReturnValue;
733 todayTodoList = todayTodoList.concat(listToArray(list, calendarList[i]));
735 log(
"updateData(): todayTodoList.sort()");
736 todayTodoList.sort(sortCalendarEntries);
737 entryLists = [todayTodoList, meetingList];
739 entryLists = [meetingList];
741 lastReloadTime = newDate();
743 error('loading Calendar items list:' + e + ', line ' + e.line);
753 var fontsize = getDefaultFontSize()[
1] + 'px';
754 var lineheight = fontsize;
756 if (config['fontsize'].Value == config['fontsize'].ValidValues[
0]) {
757 fontsize = parseInt(
72 / config['eventsPerWidget'].Value) + 'px';
758 lineheight = parseInt(
72 / config['eventsPerWidget'].Value) + 'px';
761 if (config['fontsize'].Value != config['fontsize'].ValidValues[
0]) {
762 fontsize = config['fontsize'].Value + 'px';
763 lineheight = fontsize;
765 changeCssClass('.icon', config['cssStyle_icon'].Value + '; width:' + fontsize + '; height:' + fontsize + ';');
766 var entriesHtml = '
<table style=
"font-size:' + fontsize + '; line-height:' + lineheight + ';">';
768 entriesHtml = '
<table width=
"307" height=
"82"><tr><td>' + entriesHtml; // this is needed to center the actual content vertically
772 max = (panelNum +
1) * config['eventsPerWidget'].Value;
774 max = config[
"maxNumberOfEventsOnFullscreen"].Value; // we can display a lot more events in fullscreen mode
776 if (config['enableLogging'].Value) {
778 for (var i=
0; i < entryLists.length; i++) {
779 listinfo = listinfo +
" " + entryLists[i].length;
780 var entrieslist =
"";
781 for (var j=
0; j < entryLists[i].length; j++) {
782 entrieslist += entryLists[i][j].Summary +
", ";
784 log(
"updateData(): entrieslist: " + entrieslist);
786 log(
"updateData(): inner loop, " + entryLists.length +
" lists, [" + listinfo +
"] entries");
789 // the first outer loop iteration is for passed ToDos, the second loop is for all upcomming events (may also include ToDos)
790 for (var i=
0; counter < max && i < entryLists.length; i++) {
791 for (var j=
0; (counter < max) && (j < entryLists[i].length); j++) {
792 entry = entryLists[i][j];
795 // output event info for debugging
796 var entryInfo =
"event: ";
797 for(var k=
0; k < entryFields.length; ++k) {
798 if (entry[entryFields[k]] != undefined) {
799 entryInfo += entryFields[k] +
"=" + entry[entryFields[k]] +
",";
804 // we don't want ToDos when includeTodos == false or when they are completed
805 if (entry.Type == 'ToDo' && (entry.Status ==
"TodoCompleted" || !config['includeTodos'].Value)) {
806 log('skipping ' + entry.id );
811 // make sure that we don't include an event twice (useful for ToDos that might come up twice)
812 if (eventIds[entry.id] ==
1 && entry.Type == 'ToDo') {
813 log('skipped (already included) ' + entry.id);
817 eventIds[entry.id] =
1;
819 // summary can be undefined!
820 var Summary = ((entry.Summary == null) ? '' : entry.Summary);
821 if (entry.Location != '' && entry.Location != undefined && config['showLocation'].Value)
822 Summary += ', ' + entry.Location;
824 // fix by yves: determine start and end dates/times
825 entryStartTime = ((entry.InstanceStartTime == null) ? entry.StartTime : entry.InstanceStartTime);
826 entryEndTime = ((entry.InstanceEndTime == null) ? entry.EndTime : entry.InstanceEndTime);
828 // there can be ToDos that have no date at all!
829 if (entry.Type == 'ToDo' && entry.EndTime == null)
830 entryDate =
""; // this will cause parseDate(entryDate) to return null;
832 entryDate = ((entry.Type == 'ToDo') ? entryEndTime : entryStartTime); // ToDo's use their EndTime, the rest use StartTime
834 // Convert date/time string to Date object
835 var date = parseDate(entryDate);
836 log('date: ' + date);
837 var endDate = ((entryEndTime == null) ? null : parseDate(entryEndTime));
838 log('endDate: ' + endDate);
840 // check if Meeting is actually a DayEvent. Bug introduced by
"Anna" updates to various Symbian^
3 devices.
841 // Note that this workaround is not
100% save! It might missinterpret some meetings as dayevents of starting and ending on
00:
00
842 if (entry.Type == 'Meeting' && date.getHours() ==
0 && date.getMinutes() ==
0 &&
843 endDate != null && endDate.getHours() ==
0 && endDate.getMinutes() ==
0) {
844 log('fixing event type: changed from
"Meeting" to
"DayEvent".');
845 entry.Type = 'DayEvent';
848 // check if meeting event has already passed
849 if (entry.Type == 'Meeting') {
850 var compareTime = ((endDate == null) ? date.getTime() : endDate.getTime());
851 if (now.getTime()
> compareTime) {
852 log('skipping Meeting (already passed) ' + entry.id);
854 eventIds[entry.id] =
0;
859 // check if anniversary passed (not sure why they are in the list, the query was only for today - nokia?)
860 if (entry.Type == 'Anniversary') {
861 var tmp = new Date(now.getFullYear(), now.getMonth(), now.getDate(),
0,
0,
0);
862 if (date.getTime() < tmp.getTime()) {
863 log('skipping Anniversary (already passed) ' + entry.id);
865 eventIds[entry.id] =
0;
870 // fix DayEvents end time. End times are off by
1 Second. It's possible that the event has already passed
871 if (entry.Type == 'DayEvent' && endDate != null) {
872 endDate.setMinutes(endDate.getMinutes() -
1);
873 log('fixing DayEvent endDate: ' + endDate);
874 if (now.getTime()
> endDate.getTime()) {
875 log('event already passed ' + entry.id);
877 eventIds[entry.id] =
0;
882 // check if the event is currently taking place
883 if (entryStartTime != null && entryEndTime != null && date != null && endDate != null) {
884 // check if we are between start and endtime
885 if ((date.getTime() < now.getTime()) && (now.getTime() < endDate.getTime())) {
886 date = now; // change appointment date/time to now
887 log('event is currently taking place: ' + date);
891 // skip events for the first panel in case this is the second one and we're not in fullscreen mode
892 if (mode ==
0 && panelNum
> 0 && counter < panelNum * config['eventsPerWidget'].Value +
1) {
893 log('skipping (already in first widget) ' + entry.id);
897 // mark overdue todos
899 if (entry.Type == 'ToDo' && date != null) {
900 var tmp1 = new Date(date.getFullYear(), date.getMonth(), date.getDate(),
0,
0,
0);
901 var tmp2 = new Date(now.getFullYear(), now.getMonth(), now.getDate(),
0,
0,
0);
902 if (tmp1.getTime() < tmp2.getTime()) {
907 // generate html output
908 entriesHtml += '
<tr>';
909 if (config['showCalendarIndicator'].Value && calendarList.length - config['excludedCalendars'].Value.length
> 1) {
910 entriesHtml += '
<td><div class=
"calendar' + calendarColors[entry.CalendarName] + '" style=
"height:' + (lineheight.split("px
")[0] - 1) + 'px; width:4px;"></div></td>';
912 if (config['showIcons'].Value)
913 entriesHtml += '
<td><img class=
"icon" align=
"top" src=
"' + entry.Type + '.png" /></td>';
915 entriesHtml += '
<td style=
"padding:0px;"></td>';
917 // some languages have very strange locale date formats, can't parse all those. Also some todos don't have dates at all.
918 entriesHtml += '
<td colspan=
"4"><span class=
"date">' + entryDate + '
</span> ';
920 var weekDay = getWeekdayLocalized(date).substr(
0,config['weekDayLength'].Value);
921 log('date.toLocaleDateString(): ' + date.toLocaleDateString());
922 log('weekDay: ' + weekDay);
923 var time = formatTime(date);
924 var dateStr = formatDate(date, entryDate);
925 if (entry.Type == 'ToDo' && overdue && config['markOverdueTodos'].Value) {
926 dateStr = '
<span class=
"overdue">' + config['overdueText'].Value + '
</span>';
927 entriesHtml += '
<td colspan=
"4" width=
"1px"><span class=
"date">' + dateStr + '
</span> ';
928 } else if (entry.Type == 'ToDo' || entry.Type == 'Anniversary' || entry.Type == 'DayEvent' || entry.Type == 'Reminder') {
929 if ((isToday(date) || isTomorrow(date)) && config['showTodayAsText'].Value) // show weekday if the date string is not text. looks odd otherwise
930 entriesHtml += '
<td colspan=
"4" width=
"1px"><span class=
"date">' + dateStr + '
</span> ';
932 entriesHtml += '
<td class=
"weekDay" width=
"1px">' + weekDay + '
</td><td width=
"1px" class=
"date">' + dateStr + '
</td><td colspan=
"2">';
933 } else if (entry.Type == 'Meeting') {
934 if (config['showCombinedDateTime'].Value) {
936 entriesHtml += '
<td width=
"1px" colspan=
"4"><span class=
"today">' + time + '
</span> ';
937 else if (isTomorrow(date))
938 entriesHtml += '
<td width=
"1px" colspan=
"4"><span class=
"tomorrow">' + dateStr + '
</span> <span class=
"time">' + time + '
</span> ';
940 entriesHtml += '
<td width=
"1px" class=
"weekDay">' + weekDay + '
</td><td width=
"1px" class=
"date">' + dateStr + '
</td><td colspan=
"2">';
942 if ((isToday(date) || isTomorrow(date)) && config['showTodayAsText'].Value)
943 entriesHtml += '
<td colspan=
"4" width=
"1px"><span class=
"today">' + dateStr + '
</span> <span class=
"time">' + time + '
</span> ';
945 entriesHtml += '
<td width=
"1px" class=
"weekDay">' + weekDay + '
</td><td width=
"1px" class=
"date">' + dateStr + '
</td><td width=
"1px" class=
"time">' + time + '
</td><td>';
949 entriesHtml += '
<span class=
"description">' + Summary + '
</span></td></tr>';
952 entriesHtml += '
</table>';
954 entriesHtml = entriesHtml + '
</td></tr></table>';
955 if (config['showNothingText'].Value && entriesHtml == '
<table></table>') {
956 var text = config['nothingText'].Value.replace(/%d/, config['monthRange'].Value);
957 entriesHtml = '
<div style=
"width:295px; height:75px; text-align:center; line-height:75px; overflow:visible;">' + text + '
</div>';
959 log(
"output: " + entriesHtml);
960 if (cacheEntriesHtml != entriesHtml) {
962 document.getElementById('calendarList').innerHTML = entriesHtml;
964 document.getElementById('fullscreenCalendarList').innerHTML = entriesHtml;
965 cacheEntriesHtml = entriesHtml;
968 lastUpdateTime = newDate();
970 error('displaying list:' + e + ', line ' + e.line);
975 // called by handleOnShow() and onResize events
976 function updateScreen()
978 log('updateScreen(): mode=' + mode + ', window.innerHeight=' + window.innerHeight);
980 // check if opening fullscreen
982 // Note: according to Nokia's documentation, an innerHeight of
>91 is an indicator for fullscreen view.
983 // However a bug in E6's firmware causes different window widths and heights (disabled compatibility scaling).
984 // So far, values of
104 and
115 for window.innerHeight were reported, we use a safty margin here and check
986 if( window.innerHeight
> 150 && mode ==
0) {
988 cacheEntriesHtml = '';
989 document.getElementById('body').style.backgroundImage =
"";
992 else if (window.innerHeight <=
150 && mode !=
0) {
994 cacheEntriesHtml = '';
999 updateHomescreen(); // check for screen rotation
1004 function handleOnShow()
1008 var time = newDate();
1009 if (time.getTime() - lastUpdateTime.getTime()
> config['updateDataInterval'].Value *
60 *
1000) {
1010 log('updateScreen(): force updateData() because last update was too long ago (' + (time.getTime() - lastUpdateTime.getTime()) /
1000 + 's)');
1013 setUpdateTimer(); // reinitialize update timer
1017 function launchCalendar()
1020 widget.openApplication(config['calendarApp'].Value,
"");
1021 if (config['hideWidgetOnCalendarOpen'].Value)
1024 error('starting Calendar App');
1031 log('New widget instance starting up...');
1034 // call calendar service
1035 if (device !=
"undefined")
1036 calendarService = device.getServiceObject(
"Service.Calendar",
"IDataSource");
1038 throw('device object does not exist');
1040 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>');
1044 calendarList = listCalendars();
1046 updateCalendarColors();
1049 requestNotification();
1050 document.getElementById(
"settingsTitle").innerHTML = getLocalizedText('menu.settings');
1052 if (window.innerHeight
> 91) {
1053 mode =
0; // we're starting fullscreen, we set mode to homescreen in order to let updateScreen() do all the work for us
1058 log(
"init(): updateScreen()");
1060 if (config['useBackgroundImage'].Value)
1061 // check for screen rotation every
1 secs
1062 screenRotationTimer = window.setInterval('checkOrientation()',
1000 *
1);
1064 // call updateScreen() when widget changes from background to forground
1065 window.widget.onshow = handleOnShow;
1067 log(
"init(): finished...");
1069 statupSuccessful = true;
1072 function checkOrientation()
1076 updateHomescreen(); // check for screen rotation
1079 function setUpdateTimer()
1081 updateTimer = window.setInterval('updateTimerCallback()',
1000 *
60 * config['updateDataInterval'].Value);
1084 function clearUpdateTimer()
1086 window.clearInterval(updateTimer);
1089 function updateTimerCallback()
1091 log(
"updateTimerCallback()");
1095 function createMenu()
1097 window.menu.setLeftSoftkeyLabel(
"",null);
1098 window.menu.setRightSoftkeyLabel(
"",null);
1100 var menuSettings = new MenuItem(getLocalizedText('menu.settings'), id++);
1101 var menuCallApp = new MenuItem(getLocalizedText('menu.openCalendarApp'), id++);
1102 var menuHelp = new MenuItem(getLocalizedText('menu.help'), id++);
1103 var menuUpdate = new MenuItem(getLocalizedText('menu.update'), id++);
1104 var menuAbout = new MenuItem(getLocalizedText('menu.about'), id++);
1105 menuSettings.onSelect = showSettings;
1106 menuAbout.onSelect = showAbout;
1107 menuCallApp.onSelect = launchCalendar;
1108 menuUpdate.onSelect = showUpdate;
1109 menuHelp.onSelect = showHelp;
1110 window.menu.clear();
1111 window.menu.append(menuCallApp);
1112 window.menu.append(menuSettings);
1113 window.menu.append(menuHelp);
1114 window.menu.append(menuUpdate);
1115 window.menu.append(menuAbout);
1118 function showSettings()
1122 document.getElementById(
"settingsView").style.display =
"block";
1123 document.onclick = null;
1125 window.menu.setLeftSoftkeyLabel(getLocalizedText('settings.save'), function()
1127 for (var key in config) {
1128 if (config[key].Type == 'String')
1129 config[key].Value = document.forms[
0].elements[
"settings." + key].value;
1130 else if (config[key].Type == 'Int') {
1131 config[key].Value = parseInt(document.forms[
0].elements[
"settings." + key].value);
1132 if (config[key].Value <
0 || isNaN(config[key].Value))
1133 config[key].Value = config[key].Default;
1135 else if (config[key].Type == 'Bool')
1136 config[key].Value = document.forms[
0].elements[
"settings." + key].checked;
1137 else if (config[key].Type == 'UID') {
1138 config[key].Value = parseInt(document.forms[
0].elements[
"settings." + key].value);
1139 if (isNaN(config[key].Value))
1140 config[key].Value = config[key].Default;
1142 else if (config[key].Type == 'Enum') {
1143 config[key].Value = document.forms[
0].elements[
"settings." + key].value;
1144 if (config[key].ValidValues.indexOf(config[key].Value) == -
1)
1145 config[key].Value = config[key].Default;
1147 else if (config[key].Type == 'Array') {
1148 if (key == 'excludedCalendars') {
1149 config[key].Value = new Array();
1150 for(var i=
0; i < calendarList.length; i++) {
1151 var element = document.forms[
0].elements[
"settings." + key +
"." + calendarList[i]];
1152 if (element != null && element.checked == false)
1153 config[key].Value.push(calendarList[i]);
1166 window.menu.setRightSoftkeyLabel(getLocalizedText('settings.cancel'), function()
1172 var settingsHtml = '
<form>';
1173 for (var key in config) {
1174 if (config[key].Type == 'String') {
1176 if (key.substring(
0,
9) ==
"cssStyle_")
1177 prefix = getLocalizedText('settings.cssStyle_prefix');
1178 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 />';
1180 else if (config[key].Type == 'Int')
1181 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 />';
1182 else if (config[key].Type == 'Bool')
1183 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 />';
1184 else if (config[key].Type == 'UID')
1185 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 />';
1186 else if (config[key].Type == 'Enum') {
1187 settingsHtml += '
<table><tr><td>' + getLocalizedText('settings.name.' + key) + '
<br /><select name=
"settings.' + key + '" size=
"1">';
1188 for(var i =
0; i < config[key].ValidValues.length; i++) {
1189 var text = getLocalizedText('settings.validValues.' + key + '.' + config[key].ValidValues[i]);
1190 if (text.indexOf('ERROR') ==
0)
1191 text = config[key].ValidValues[i];
1192 settingsHtml += '
<option value=
"' + config[key].ValidValues[i] + '"' + (config[key].Value == config[key].ValidValues[i] ? '
selected=
"selected"' : '') + '
>' + text + '
</option>';
1194 settingsHtml += '
</select></div></td>' + printHintBox(getLocalizedText('settings.info.' + key)) + '
<hr />';
1196 else if (config[key].Type == 'Array') {
1197 settingsHtml += '
<table><tr><td>' + getLocalizedText('settings.name.' + key) + '
<br />';
1198 if (key == 'excludedCalendars') {
1199 for(var i=
0; i < calendarList.length; i++) {
1200 var checked = '
checked=
"checked"';
1201 if (config[key].Value.indexOf(calendarList[i]) != -
1)
1203 settingsHtml += '
<input name=
"settings.' + key + '.' + calendarList[i] + '" type=
"checkbox" value=
"' + calendarList[i] + '" ' + checked + '
/> ' + calendarList[i] + '
<br />';
1206 settingsHtml += '
</td>' + printHintBox(getLocalizedText('settings.info.' + key)) + '
<hr />';
1209 settingsHtml += '
<input name=
"reset" type=
"button" value=
"' + getLocalizedText('settings.restoreDefaults') + '" onclick=
"javascript:restoreDefaultSettings();showSettings();" />';
1210 settingsHtml += '
</form>';
1211 document.getElementById(
"settingsList").innerHTML = settingsHtml;
1214 function changeCssClass(classname, properties)
1216 for(var i =
0; i < document.styleSheets[
0]['cssRules'].length; i++)
1218 if (document.styleSheets[
0]['cssRules'][i].selectorText == classname) {
1219 document.styleSheets[
0].deleteRule(i);
1220 document.styleSheets[
0].insertRule(classname + ' { ' + properties + ' }', document.styleSheets[
0]['cssRules'].length);
1226 function updateCssClasses()
1228 for(var key in config) {
1229 changeCssClass(getLocalizedText('settings.name.' + key), config[key].Value);
1233 function getSettingsCalEntryId()
1235 if (settingsCalEntryId == null) {
1236 // check if entry already exists
1237 var listFiltering = {
1238 Type:'CalendarEntry',
1240 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!
1241 EndRange: new Date(
2000,
0,
2),
1242 SearchText: 'ComingNext Settings|',
1248 result = calendarService.IDataSource.GetList(listFiltering);
1249 if (result.ErrorCode)
1250 throw(result.ErrorMessage);
1253 error(
"getSettingsCalEntryId: GetList() failed: " + e + ', line ' + e.line);
1256 var list = result.ReturnValue;
1257 var entry = list.getNext();
1258 if (entry != undefined) {
1259 settingsCalEntryId = entry.LocalId;
1260 log(
"settingsCalEntryId=" + settingsCalEntryId);
1262 else { // create settings item
1263 var item = new Object();
1264 item.Type =
"DayEvent";
1265 item.StartTime = new Date(
2000,
0,
1);
1266 item.Summary =
"ComingNext Settings|";
1268 var criteria = new Object();
1269 criteria.Type =
"CalendarEntry";
1270 criteria.Item = item;
1273 var result = calendarService.IDataSource.Add(criteria);
1274 if (result.ErrorCode)
1275 throw(result.ErrorMessage);
1277 error(
"getSettingsCalEntryId: " + e + ', line ' + e.line);
1280 getSettingsCalEntryId();
1285 function restoreDefaultSettings()
1287 for (var key in config)
1288 config[key].Value = config[key].Default;
1291 function loadSettings()
1293 getSettingsCalEntryId();
1294 var listFiltering = {
1295 Type:'CalendarEntry',
1297 LocalId: settingsCalEntryId
1302 result = calendarService.IDataSource.GetList(listFiltering);
1303 if (result.ErrorCode)
1304 throw(result.ErrorMessage);
1307 error(
"loadSettings: GetList() failed: " + e + ', line ' + e.line);
1310 var entry = result.ReturnValue.getNext();
1311 if (entry != undefined) {
1312 log(
"Loading Settings...");
1313 // only reload settings if they chanced since the last reload
1314 if (settingsCache != entry.Summary)
1316 restoreDefaultSettings();
1317 var stringlist = entry.Summary.split(
"|");
1318 // skip the first two entries, those contain header and version info
1319 for(var i =
2; i < stringlist.length -
1; i++) {
1320 var pair = stringlist[i].split('=');
1322 var value = pair[
1];
1323 if (key == null || value == null || config[key] == null) {
1324 log('Warning: unknown or invalid setting: ' + stringlist[i]);
1327 log('stringlist[' + i + ']: ' + key + '=\'' + value + '\'');
1328 if (config[key].Type == 'Int') {
1329 config[key].Value = Number(value);
1330 if (isNaN(config[key].Value))
1331 config[key].Value = config[key].Default;
1333 else if (config[key].Type == 'String')
1334 config[key].Value = value;
1335 else if (config[key].Type == 'Bool')
1336 config[key].Value = (value == 'true')
1337 else if (config[key].Type == 'Enum')
1338 config[key].Value = value;
1339 else if (config[key].Type == 'UID') {
1340 config[key].Value = Number(value);
1341 if (isNaN(config[key].Value))
1342 config[key].Value = config[key].Default;
1344 else if (config[key].Type == 'Array') {
1345 config[key].Value = value.split(
"^");
1346 if (config[key].Value.length ==
1 && config[key].Value[
0] ==
"") {
1347 config[key].Value = [];
1351 settingsCache = entry.Summary;
1355 log(
"Settings already cached and did not change");
1359 error(
"Failed to load settings, calendar entry could not be found");
1363 function saveSettings()
1365 getSettingsCalEntryId();
1366 var item = new Object();
1367 item.Type =
"DayEvent";
1368 item.StartTime = new Date(
2000,
0,
1);
1369 item.LocalId = settingsCalEntryId;
1370 item.Summary =
"ComingNext Settings|" + version +
"|";
1372 for (var key in config) {
1373 if (config[key].Type == 'Int')
1374 item.Summary += key +
"=" + config[key].Value.toString() +
"|";
1375 else if (config[key].Type == 'String')
1376 item.Summary += key +
"=" + config[key].Value +
"|";
1377 else if (config[key].Type == 'Bool')
1378 item.Summary += key +
"=" + (config[key].Value ? 'true' : 'false') +
"|";
1379 else if (config[key].Type == 'Enum')
1380 item.Summary += key +
"=" + config[key].Value +
"|";
1381 else if (config[key].Type == 'UID')
1382 item.Summary += key +
"=" + config[key].Value.toString() +
"|";
1383 else if (config[key].Type == 'Array')
1384 item.Summary += key +
"=" + config[key].Value.join(
"^") +
"|";
1386 settingsCache = item.Summary;
1388 var criteria = new Object();
1389 criteria.Type =
"CalendarEntry";
1390 criteria.Item = item;
1392 log(
"Saving settings to calendar entry: " + item.Summary);
1394 var result = calendarService.IDataSource.Add(criteria);
1395 if (result.ErrorCode)
1396 throw(result.ErrorMessage);
1398 error(
"saveSettings: " + e + ', line ' + e.line);
1401 lastReloadTime = null; // force calendar data reload on next update
1406 function toggleVisibility(elementId)
1408 if (document.getElementById(elementId).style.display ==
"none")
1409 document.getElementById(elementId).style.display =
"block";
1411 document.getElementById(elementId).style.display =
"none";
1415 function printHintBox(text)
1418 return '
<td width=
"1%" align=
"right" onclick=
"javascript:toggleVisibility(\'info' + uniqueId + '\')">' + getLocalizedText('settings.help') + '
</td></tr></table>'+
1419 '
<div class=
"settingsInfo" id=
"info' + uniqueId + '" style=
"display:none">' + text + '
</div>';
1422 function showAbout()
1426 document.getElementById(
"aboutView").style.display =
"block";
1427 document.onclick = null;
1429 window.menu.setLeftSoftkeyLabel(
" ", function(){});
1430 window.menu.setRightSoftkeyLabel(getLocalizedText('softkey.back'), function()
1436 //document.getElementById(
"aboutView").innerHTML = 'aboutView';
1437 document.getElementById(
"name").innerHTML =
"Coming Next " + version;
1440 function showHelp() {
1441 widget.openURL('http://comingnext.sf.net/help');
1444 function updateFullscreen()
1448 function showFullscreen()
1450 log(
"showFullscreen()");
1452 document.getElementById(
"fullscreenView").style.display =
"block";
1453 document.getElementById('body').className =
"backgroundFullscreen";
1455 document.onclick = launchCalendar;
1460 function getBackgroundImage()
1465 if (config['backgroundImageLocation'].Value == config['backgroundImageLocation'].ValidValues[
0]) // internal
1466 bgImage = 'background_' + orientation + '.png';
1468 bgImage = 'C:/Data/background_' + panelNum + '_' + orientation + '.png';
1472 function updateHomescreen()
1474 if (config['useBackgroundImage'].Value) {
1475 // check if we have a completely unknown screen resolution
1476 var screenHeight = screen.height;
1477 var screenWidth = screen.width;
1478 if (screenHeight !=
640 && screenHeight !=
480 && screenHeight !=
360)
1479 screenHeight =
360; // we can only assume we're in portrait mode, so we set the screen dims as needed for the following code
1480 if (screenWidth !=
640 && screenWidth !=
480 && screenWidth !=
360)
1481 screenWidth =
640; // we can only assume we're in portrait mode, so we set the screen dims as needed for the following code
1483 // check for screen rotation
1484 if (orientation != 'portrait' && ((screenWidth ==
360 && screenHeight ==
640) || (screenWidth ==
640 && screenHeight ==
480))) {
1485 window.widget.prepareForTransition(
"fade");
1486 orientation = 'portrait';
1487 document.getElementById('body').style.backgroundImage = 'url(' + getBackgroundImage() + ')';
1488 document.getElementById('body').style.backgroundColor = 'none';
1489 window.widget.performTransition();
1490 } else if (orientation != 'landscape' && ((screenWidth ==
640 && screenHeight ==
360) || (screenWidth ==
480 && screenHeight ==
640))) {
1491 window.widget.prepareForTransition(
"fade");
1492 orientation = 'landscape';
1493 document.getElementById('body').style.backgroundImage = 'url(' + getBackgroundImage() + ')';
1494 document.getElementById('body').style.backgroundColor = 'none';
1495 window.widget.performTransition();
1497 else if (document.getElementById('body').style.backgroundImage ==
"")
1499 document.getElementById('body').style.backgroundImage = 'url(' + getBackgroundImage() + ')';
1504 function showHomescreen()
1506 log(
"showHomescreen()");
1508 document.getElementById(
"homescreenView").style.display =
"block";
1509 document.getElementById('body').className =
"background";
1510 document.onclick = null;
1514 function getLocalizedText(p_Txt)
1516 if (localizedText[p_Txt])
1517 return localizedText[p_Txt];
1519 return 'ERROR: missing translation for ' + p_Txt;
1522 function showUpdate()
1526 document.getElementById(
"updateView").style.display =
"block";
1527 document.onclick = null;
1529 window.menu.setLeftSoftkeyLabel(getLocalizedText('update.checknow'), function(){
1532 window.menu.setRightSoftkeyLabel(getLocalizedText('softkey.back'), function()
1538 document.getElementById(
"currentVersion").innerHTML = getLocalizedText(
"update.current") + version;
1542 function checkForUpdate()
1544 // asynch XHR to server url
1545 reqV = new XMLHttpRequest();
1546 reqV.onreadystatechange = checkForUpdateCallback;
1547 document.getElementById(
"updateDiv").innerHTML = getLocalizedText(
"update.checking");
1548 reqV.open(
"GET", versionURL, true);
1552 function checkForUpdateCallback()
1554 if (reqV.readyState ==
4) {
1555 if (reqV.status ==
200) {
1556 var resultXml = reqV.responseText;
1558 var div = document.getElementById(
"tmp");
1559 div.innerHTML = resultXml;
1560 var newVersion = div.getElementsByTagName('version')[
0].innerHTML;
1561 var newVersionURL = div.getElementsByTagName('url')[
0].innerHTML;
1563 if (version != newVersion) {
1564 document.getElementById(
"updateDiv").innerHTML = getLocalizedText(
"update.download").replace(/%
1/, newVersion).replace(/%
2/, newVersionURL);
1567 document.getElementById(
"updateDiv").innerHTML = getLocalizedText(
"update.nonewversion");
1572 document.getElementById(
"updateDiv").innerHTML = getLocalizedText(
"update.error") + reqV.status +
" " + reqV.responseText;
1577 function hideViews()
1579 document.getElementById(
"homescreenView").style.display =
"none";
1580 document.getElementById(
"fullscreenView").style.display =
"none";
1581 document.getElementById(
"aboutView").style.display =
"none";
1582 document.getElementById(
"settingsView").style.display =
"none";
1583 document.getElementById(
"updateView").style.display =
"none";
1586 function listCalendars()
1596 DefaultCalendar: false
1600 var calendarsResult = calendarService.IDataSource.GetList(criteria);
1601 if (calendarsResult.ErrorCode !=
0)
1602 throw(
"Error fetching list of calendars: " + calendarsResult.ErrorCode + ': ' + calendarsResult.ErrorMessage);
1603 var calendarListIterator = calendarsResult.ReturnValue;
1608 while (( item = calendarListIterator.getNext()) != undefined ) {
1609 calendars[count++] = item;
1611 log(
"Available Calendars: " + calendars.join(
", "));
1614 error('listing calendars:' + e + ', line ' + e.line);
1619 // Copies all objects and their properties to an array. Data is copied so nothing gets lost when the reference is removed
1620 // 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
1621 function listToArray(list, calendarName)
1623 var array = new Array();
1626 while (( item = list.getNext()) != undefined ) {
1627 var itemCopy = new Object();
1628 for(var i=
0; i < entryFields.length; i++) {
1629 itemCopy[entryFields[i]] = item[entryFields[i]];
1631 // for some reason, the CalendarName property is never correctly queried, so we assign it manually here
1632 if (!itemCopy['CalendarName']) {
1633 itemCopy['CalendarName'] = calendarName;
1635 if (config['anonymizeLogging'].Value && config['enableLogging'].Value) {
1636 if (itemCopy['Summary'])
1637 itemCopy['Summary'] = getHashForString(itemCopy['Summary']);
1638 if (itemCopy['Location'])
1639 itemCopy['Location'] = getHashForString(itemCopy['Location']);
1641 array.push(itemCopy);
1642 txt += array[array.length -
1].Summary +
", ";
1644 log(
"listToArray(): " + txt);
1648 function sortCalendarEntries(a, b)
1651 log(
"sortCalendarEntries(" + a.Summary +
"," + b.Summary +
")");
1653 if (a.InstanceStartTime != null) {
1654 atime = a.InstanceStartTime;
1656 else if (a.StartTime != null) {
1657 atime = a.StartTime;
1659 else if (a.InstanceEndTime != null) {
1660 atime = a.InstanceEndTime;
1662 else if (a.EndTime != null) {
1666 if (b.InstanceStartTime != null) {
1667 btime = b.InstanceStartTime;
1669 else if (b.StartTime != null) {
1670 btime = b.StartTime;
1672 else if (b.InstanceEndTime != null) {
1673 btime = b.InstanceEndTime;
1675 else if (b.EndTime != null) {
1679 if (atime && btime) {
1681 atime = parseDate(atime);
1682 btime = parseDate(btime);
1684 // sort by date & time
1685 if (atime < btime) {
1688 else if (atime
> btime) {
1692 else if (a.Type != b.Type) {
1693 if (a.Type < b.Type) {
1696 else if (a.Type
> b.Type) {
1700 // sort by description
1701 else if (a.Summary && b.Summary && a.Summary != b.Summary) {
1702 if (a.Summary < b.Summary) {
1705 else if (a.Summary
> b.Summary) {
1710 // NOTE: events my have no date information at all. In that case, we list events without date first
1711 else if (atime && !btime) {
1714 else if (!atime && btime) {
1717 else if (!atime && !btime) {
1719 if (a.Type != b.Type) {
1720 if (a.Type < b.Type) {
1723 else if (a.Type
> b.Type) {
1727 // sort by description
1728 else if (a.Summary && b.Summary && a.Summary != b.Summary) {
1729 if (a.Summary < b.Summary) {
1732 else if (a.Summary
> b.Summary) {
1741 function updateCalendarColors()
1744 calendarColors = [];
1745 if (calendarList.length
> maxColors) {
1746 log(
"updateCalendarColors(): Warning: more calendars than available indicator colors");
1748 for(var i=
0; i < calendarList.length; i++) {
1749 calendarColors[calendarList[i]] = (i % maxColors) +
1;
1753 function log(message)
1755 if (config['enableLogging'].Value) {
1756 console.info(message);
1760 function getDefaultFontSize()
1762 if (defaultFontSize == null) {
1763 var pa = document.body;
1764 var who = document.createElement('div');
1765 who.className = 'defaultEm';
1766 who.appendChild(document.createTextNode('M'));
1767 pa.appendChild(who);
1768 var fs = [who.offsetWidth, who.offsetHeight];
1769 pa.removeChild(who);
1770 defaultFontSize = fs;
1772 return defaultFontSize;
1775 function getHashForString(string)
1777 // cheap hashing, loosly based on Java's String.hashCode()
1778 for (var hash =
0, i =
0; i < string.length; i++)
1779 hash = (hash <<
5) - hash + string.charCodeAt(i);
1780 hash = hash & hash; // Convert to
32bit integer
1783 return hash.toString(
16).toUpperCase();
1788 <style type=
"text/css">
1790 table { margin:
0px; padding:
0px; border-spacing:
0px; border-collapse: collapse; }
1791 td { padding:
0px
5px
0px
0px; white-space:nowrap; overflow:visible; margin:
0px; }
1792 hr { color:#ffffff; background-color:#ffffff; height:
1px; text-align:left; border-style:none; }
1793 .settingsInfo { display:none; font-style:italic; }
1794 .title { font-weight:bold; font-size:
14pt; }
1795 .textInput { width:
90%; }
1796 .credits { margin-left:
40px; text-indent: -
20px; margin-bottom:
0px; }
1797 #homescreenView { width:
312px; height:
82px; overflow:hidden; }
1798 #calendarList { position:absolute; left:
5px; top:
0px; width:
307px; height:
82px; overflow:hidden; }
1799 #name { text-align:center; }
1800 #appicon { display: block; margin-left: auto; margin-right: auto; margin-top:
10px; }
1801 #smallappicon { width:
22px; height:
22px; margin-right:
10px; float:left; }
1802 .defaultEm { font-size:
1em; position:absolute; line-height:
1; padding:
0; visibility:hidden; }
1807 <body onload=
"javascript:setTimeout('init()', 10)" onresize=
"javascript:updateScreen()" id=
"body" class=
"background">
1808 <div id=
"homescreenView">
1809 <div id=
"calendarList">loading...
</div>
1811 <div id=
"fullscreenView" style=
"display:none;">
1812 <img src=
"Icon.png" id=
"smallappicon">
1813 <h1 class=
"title">Coming Next
</h1>
1815 <div id=
"fullscreenCalendarList">loading...
</div>
1817 <div id=
"settingsView" style=
"display:none">
1818 <img src=
"Icon.png" id=
"smallappicon">
1819 <h1 id=
"settingsTitle" class=
"title">Settings
</h1>
1821 <div id=
"settingsList"></div>
1823 <div id=
"aboutView" style=
"display:none">
1824 <img src=
"Icon.png" id=
"appicon">
1825 <h1 id=
"name">Coming Next
</h1>
1827 <p>Created by Dr. Cochambre and Michael Prager.
</p>
1828 <p>Contributions:
</p>
1829 <p class=
"credits">Paul Moore (bug fixes, new features and code cleanup)
</p>
1830 <p class=
"credits">Manfred Hanselmann (DST support)
</p>
1831 <p class=
"credits">Christophe Milsent (translation support & French translation)
</p>
1832 <p class=
"credits">Flavio Nathan (Portuguese-Brazilian translation)
</p>
1833 <p class=
"credits">Tokeda (Russian translation)
</p>
1834 <p class=
"credits">Marcella Ferrari (Italian translation)
</p>
1835 <p class=
"credits">Venos (Italian translation)
</p>
1836 <p class=
"credits">Francisco Rodero (Catalan translation)
</p>
1837 <p class=
"credits">zbigzbig20 (Polish translation)
</p>
1838 <p class=
"credits">Streamkeskus (Finnish translation)
</p>
1839 <p class=
"credits">renek (Czech translation)
</p>
1840 <p>This software is open source and licensed under the GPLv3.
</p>
1841 <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>
1844 <div id=
"updateView" style=
"display:none">
1845 <img src=
"Icon.png" id=
"smallappicon">
1846 <h1 class=
"title">Check for update
</h1>
1848 <div id=
"currentVersion">Coming Next ??
</div>
1849 <div id=
"updateDiv"></div>
1850 <div id=
"tmp" style=
"display:none;"></div>