1 <?xml version=
"1.0" encoding=
"UTF-8"?>
2 <!DOCTYPE html PUBLIC
"-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
3 <html xmlns=
"http://www.w3.org/1999/xhtml">
6 <title>Coming Next
</title>
8 <style type=
"text/css">
9 /* The following classes can be modified by widget settings */
10 .background { color:#ffffff; background-color:#
000000 }
11 .backgroundFullscreen { color:#ffffff; background-color:#
000000 }
14 .today { color:#ff0000 }
15 .tomorrow { color:#
0000ff }
17 .now { color:#ff00ff }
19 .icon { width:
15px; height:
15px }
20 .overdue { color:#ffff00 }
21 .calendar1 { background-color:#
0757cf }
22 .calendar2 { background-color:#
579f37 }
23 .calendar3 { background-color:#ff9f07 }
24 .calendar4 { background-color:#af8fef }
25 .calendar5 { background-color:#
57afbf }
26 .calendar6 { background-color:#
9fdf57 }
29 <script type=
"text/javascript" src=
"localizedTextStrings.js" charset=
"utf-8"></script>
30 <script type=
"text/javascript" src=
"../debug.js" charset=
"utf-8"></script>
31 <script type=
"text/javascript">
32 // valid types for the config object are 'Int', 'Bool', 'String', 'Enum', 'UID', 'Array'
34 fontsize: { Type: 'Enum', Default: 'auto', Value: 'auto', ValidValues: ['auto', '
8', '
9', '
10', '
11', '
12', '
13', '
14', '
15', '
16', '
17', '
18', '
19', '
20', '
21', '
22', '
23', '
24', '
25', '
26', '
27', '
28'],},
35 eventsPerWidget: { Type: 'Int', Default:
4, Value:
4,},
36 monthRange: { Type: 'Int', Default:
2, Value:
2,},
37 includeTodos: { Type: 'Bool', Default: true, Value: true,},
38 useBackgroundImage: { Type: 'Bool', Default: true, Value: true,},
39 backgroundImageLocation: { Type: 'Enum', Default: 'internal', Value: 'internal', ValidValues: ['internal', 'external']},
40 showCombinedDateTime: { Type: 'Bool', Default: false, Value: false,},
41 showLocation: { Type: 'Bool', Default: true, Value: true,},
42 showIcons: { Type: 'Bool', Default: true, Value: true,},
43 showTodayAsText: { Type: 'Bool', Default: true, Value: true,},
44 todayText: { Type: 'String', Default: getLocalizedText('settings.default.todayText'), Value: getLocalizedText('settings.default.todayText'),},
45 tomorrowText: { Type: 'String', Default: getLocalizedText('settings.default.tomorrowText'), Value: getLocalizedText('settings.default.tomorrowText'),},
46 showNowAsText: { Type: 'Bool', Default: true, Value: true,},
47 nowText: { Type: 'String', Default: getLocalizedText('settings.default.nowText'), Value: getLocalizedText('settings.default.nowText'),},
48 markOverdueTodos: { Type: 'Bool', Default: true, Value: true,},
49 overdueText: {Type: 'String', Default: getLocalizedText('settings.default.overdueText'), Value: getLocalizedText('settings.default.overdueText'),},
50 dateSeparator: { Type: 'String', Default: getLocalizedText('settings.default.dateSeparator'), Value: getLocalizedText('settings.default.dateSeparator'),},
51 dateFormat: { Type: 'Enum', Default: 'auto', Value: 'auto', ValidValues: ['auto', 'DDMM', 'MMDD'],},
52 weekDayLength: { Type: 'Int', Default:
2, Value:
2,},
53 updateDataInterval: { Type: 'Int', Default:
5, Value:
5,},
54 calendarApp: { Type: 'UID', Default:
0x10005901, Value:
0x10005901,},
55 showNothingText: { Type: 'Bool', Default: true, Value: true,},
56 nothingText: { Type: 'String', Default: getLocalizedText('settings.default.nothingText'), Value: getLocalizedText('settings.default.nothingText'),},
57 enableDaylightSaving: { Type: 'Bool', Default: true, Value: true,},
58 daylightSavingOffset: { Type: 'Int', Default:
1, Value:
1,},
59 hideWidgetOnCalendarOpen: { Type: 'Bool', Default: false, Value: false,},
60 showCalendarIndicator: { Type: 'Bool', Default: true, Value: true,},
61 excludedCalendars: { Type: 'Array', Default: [], Value: [],},
62 enableLogging: { Type: 'Bool', Default: false, Value: false,},
63 anonymizeLogging: { Type: 'Bool', Default: false, Value: false,},
64 cssStyle_background: { Type: 'String', Default: 'color:#ffffff; background-color:#
000000', Value: 'color:#ffffff; background-color:#
000000',},
65 cssStyle_backgroundFullscreen: { Type: 'String', Default: 'color:#ffffff; background-color:#
000000', Value: 'color:#ffffff; background-color:#
000000',},
66 cssStyle_weekDay: { Type: 'String', Default: '', Value: '',},
67 cssStyle_date: { Type: 'String', Default: '', Value: '',},
68 cssStyle_today: { Type: 'String', Default: 'color:#ff0000', Value: 'color:#ff0000',},
69 cssStyle_tomorrow: { Type: 'String', Default: 'color:#
0000ff', Value: 'color:#
0000ff',},
70 cssStyle_time: { Type: 'String', Default: '', Value: '',},
71 cssStyle_now: { Type: 'String', Default: 'color:#ff00ff', Value: 'color:#ff00ff',},
72 cssStyle_description: { Type: 'String', Default: '', Value: '',},
73 cssStyle_icon: { Type: 'String', Default: '', Value: '',},
74 cssStyle_overdue: { Type: 'String', Default: 'color:#ffff00', Value: 'color:#ffff00',},
75 cssStyle_calendar1: { Type: 'String', Default: 'background-color:#
0757cf', Value: 'background-color:#
0757cf',},
76 cssStyle_calendar2: { Type: 'String', Default: 'background-color:#
579f37', Value: 'background-color:#
579f37',},
77 cssStyle_calendar3: { Type: 'String', Default: 'background-color:#ff9f07', Value: 'background-color:#ff9f07',},
78 cssStyle_calendar4: { Type: 'String', Default: 'background-color:#af8fef', Value: 'background-color:#af8fef',},
79 cssStyle_calendar5: { Type: 'String', Default: 'background-color:#
57afbf', Value: 'background-color:#
57afbf',},
80 cssStyle_calendar6: { Type: 'String', Default: 'background-color:#
9fdf57', Value: 'background-color:#
9fdf57',},
85 //-------------------------------------------------------
86 // Nothing of interest from here on...
87 //-------------------------------------------------------
88 var panelNum =
0; // use
1 for second panel
90 var versionURL =
"http://comingnext.sourceforge.net/version.xml";
91 var calendarService = null;
92 var cacheEntriesHtml = [];
93 var months_translated = [];
94 var weekdays_translated = [];
97 var mode =
0; //
0 = homescreen,
1 = fullscreen,
2 = settings,
3 = about,
4 = check for update
99 var settingsCalEntryId = null;
100 var settingsCache = null;
101 var notificationRequests = new Array();
102 var calendarList = [];
103 var calendarColors = [];
104 var updateTimer = null;
105 var screenRotationTimer = null;
106 var lastUpdateTime = now; // last time we updated the display
107 var lastReloadTime = null; // last time we fetched calendar data
108 var reloadInterval =
6 *
60 *
60 *
1000; // =
6 hours; time interval for reloading calendar data
109 var errorOccured = false;
110 var entryLists = null; // stores all fetched calendar entries until data is refreshed
111 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.
112 var use12hoursTimeFormat = false; // defines how time should be formated:
19:
00 or
07:
00 pm
113 var timeFormatSeparator =
":"; // format time
19:
00 or
19.00 depending on system setting
114 var defaultFontSize = null; // default browser font size will be set by init
116 // vars for daylight saving time
117 var summertime = false; // true, if current date is in summer, false if in winter
118 var daylightSavingDates = new Object(); // caches calculated DST winter and summer time shift dates
120 // this is a list of data fields a calendar event can have
134 function isLeapYear( year ) {
135 if (( year %
4 ==
0 && year %
100 !=
0 ) || year %
400 ==
0 )
141 function calcLeapYear(year, days)
143 if (isLeapYear(year))
149 function subToSunday(myDate, year, days, prevMonthDays)
151 for (i = myDate.getDay(); i
> 0 ;i--)
153 days -= prevMonthDays;
154 days = isLeapYear(year) ? --days : days;
158 function isSummertime(curDate)
163 // if we already calculated DST summer and winter time dates for this year, use cached values
164 var dst = daylightSavingDates[curDate.getFullYear()];
166 var thisYearS = new Date(curDate.getFullYear(),
3,
0,
0,
0,
0 );
167 var thisYearW = new Date(curDate.getFullYear(),
10,
0,
0,
0,
0 );
168 var nextYearS = new Date(curDate.getFullYear() +
1,
3,
0,
0,
0,
0 );
169 var nextYearW = new Date(curDate.getFullYear() +
1,
10,
0,
0,
0,
0 );
171 thisYearSDays = nextYearSDays =
90;
172 thisYearWDays = nextYearWDays =
304;
174 thisYearSDays = calcLeapYear(curDate.getFullYear(), thisYearSDays);
175 thisYearWDays = calcLeapYear(curDate.getFullYear(), thisYearWDays);
176 nextYearSDays = calcLeapYear(curDate.getFullYear() +
1, nextYearSDays);
177 nextYearWDays = calcLeapYear(curDate.getFullYear() +
1, nextYearWDays);
179 thisYearSDays = subToSunday(thisYearS, curDate.getFullYear(), thisYearSDays,
59);
180 thisYearWDays = subToSunday(thisYearW, curDate.getFullYear(), thisYearWDays,
273);
181 nextYearSDays = subToSunday(nextYearS, curDate.getFullYear() +
1, nextYearSDays,
59);
182 nextYearWDays = subToSunday(nextYearW, curDate.getFullYear() +
1, nextYearWDays,
273);
185 Summer: new Date (curDate.getFullYear(),
03-
1, thisYearSDays,
2,
0,
0),
186 Winter: new Date (curDate.getFullYear(),
10-
1, thisYearWDays,
2,
0,
0),
188 daylightSavingDates[curDate.getFullYear()] = dst;
191 if (dst.Summer < curDate)
193 if (dst.Winter < curDate)
195 if (summer && !winter)
201 function error(message)
203 console.info('Error: ' + message);
204 document.getElementById(
"calendarList").innerHTML = 'Error: ' + message;
205 document.getElementById(
"fullscreenCalendarList").innerHTML = 'Error: ' + message;
207 document.onclick = null;
210 function areDatesEqual(date1, date2)
212 return (date1.getFullYear() == date2.getFullYear() &&
213 date1.getMonth() == date2.getMonth() &&
214 date1.getDate() == date2.getDate());
217 function isTomorrow(date)
219 // tommorow = now +
1 day
220 // ToDo: some days can be shorter as
24 hours(daylight saving change day)
221 return areDatesEqual(date, new Date (now.getTime() +
24*
60*
60*
1000));
224 function isToday(date)
226 return areDatesEqual(date, now);
229 function collectLocales()
231 var tmpyear =
2000 + panelNum;
234 if (months_translated.length
> 0)
236 for (month =
0; month <
12; month++) {
237 var startDate = new Date(tmpyear, month,
15);
239 var item = new Object();
240 item.Type =
"DayEvent";
241 item.StartTime = startDate;
242 item.Summary =
"__temp" + month;
244 var criteria = new Object();
245 criteria.Type =
"CalendarEntry";
246 criteria.Item = item;
249 var result = calendarService.IDataSource.Add(criteria);
250 if (result.ErrorCode)
251 throw(result.ErrorMessage);
253 error(
"collectLocales: " + e + ', line ' + e.line);
256 for (weekday =
0; weekday <
7; weekday++) {
257 var startDate = new Date(
2000,
0,
2 + weekday); // date that we know for sure is a sunday
259 var item = new Object();
260 item.Type =
"DayEvent";
261 item.StartTime = startDate;
262 item.Summary =
"__weekday_temp" + weekday;
264 var criteria = new Object();
265 criteria.Type =
"CalendarEntry";
266 criteria.Item = item;
269 var result = calendarService.IDataSource.Add(criteria);
270 if (result.ErrorCode)
271 throw(result.ErrorMessage);
273 error(
"collectLocales: " + e + ', line ' + e.line);
277 var startTime = new Date(tmpyear,
0,
1);
278 var endTime = new Date(tmpyear,
11,
31);
279 var listFiltering = {
280 Type:'CalendarEntry',
282 StartRange: startTime,
284 SearchText: '__temp',
288 var result = calendarService.IDataSource.GetList(listFiltering);
289 if (result.ErrorCode)
290 throw(result.ErrorMessage);
291 var list = result.ReturnValue;
293 error(
"collectLocales: " + e + ', line ' + e.line);
296 var ids = new Array();
302 while (list && (entry = list.getNext()) != undefined) {
303 dateArr = (entry.StartTime + '').replace(/,/g,'').replace(/\./g,':').replace(/ /g,' ').split(' ');
304 var day = dateArr[
1];
305 var month = dateArr[
2];
306 var year = dateArr[
3];
308 // make sure month is set properly
309 if (isNaN(parseInt(day))) {
313 } else if (isNaN(parseInt(year))) {
319 log(entry.StartTime + ' -
> ' + month + ' ' + counter);
320 ids[counter] = entry.id;
321 months_translated[month] = counter +
1;
325 error(
"collectLocales: " + e + ', line ' + e.line);
329 var startTime = new Date(
2000,
0,
2);
330 var endTime = new Date(
2000,
0,
9);
331 var listFiltering = {
332 Type:'CalendarEntry',
334 StartRange: startTime,
336 SearchText: '__weekday_temp',
340 var result = calendarService.IDataSource.GetList(listFiltering);
341 if (result.ErrorCode)
342 throw(result.ErrorMessage);
343 var weekdaylist = result.ReturnValue;
345 error(
"collectLocales: " + e + ', line ' + e.line);
353 while (weekdaylist && (entry = weekdaylist.getNext()) != undefined) {
354 detectTimeFormat(entry.StartTime + '');
355 curWeekday = (entry.StartTime + '').split(',')[
0];
356 log(entry.StartTime + ' -
> ' + curWeekday + ' ' + counter2);
357 ids[counter + counter2] = entry.id;
358 weekdays_translated[counter2] = curWeekday;
362 error(
"collectLocales: " + e + ', line ' + e.line);
367 var criteria = new Object();
368 criteria.Type =
"CalendarEntry";
373 var result = calendarService.IDataSource.Delete(criteria);
374 if (result.ErrorCode)
375 throw(result.ErrorMessage);
377 error('deleting temp calendar entries:' + e + ', line ' + e.line);
382 function stringEndsWith(str, suffix)
384 return str.indexOf(suffix, str.length - suffix.length) !== -
1;
387 // detects the system's current time format by parsing a native calendar timestamp (this is the only reliable formating across all devices and firmwares)
388 function detectTimeFormat(localeTimeString)
390 localeTimeString = localeTimeString.toLowerCase();
391 use12hoursTimeFormat = stringEndsWith(localeTimeString,
"am") || stringEndsWith(localeTimeString,
"pm");
392 timeFormatSeparator = localeTimeString.indexOf(
":") != -
1 ?
":" :
".";
395 function requestNotification()
397 var criteria = new Object();
398 criteria.Type =
"CalendarEntry";
399 criteria.Filter = new Object();
400 for(var i=
0; i < calendarList.length; i++) {
401 criteria.Filter.CalendarName = calendarList[i];
403 var notificationRequest = calendarService.IDataSource.RequestNotification(criteria, callback);
404 if (notificationRequest.ErrorCode)
405 error('requestNotification failed with error code ' + notificationRequest.ErrorCode);
406 notificationRequests.push(notificationRequest);
408 error(
"requestNotification: " + e + ', line ' + e.line);
412 var criteria2 = new Object();
413 criteria2.Type =
"CalendarEntry";
414 criteria2.Filter = new Object();
415 criteria2.Filter.LocalIdList = new Array();
416 criteria2.Filter.LocalIdList[
0] = settingsCalEntryId;
418 var notificationRequest = calendarService.IDataSource.RequestNotification(criteria2, settingsCallback);
419 if (notificationRequest.ErrorCode)
420 error('requestNotification failed with error code ' + notificationRequest.ErrorCode);
421 notificationRequests.push(notificationRequest);
423 error(
"requestNotification: " + e + ', line ' + e.line);
427 function cancelNotification()
429 for(var i=
0; i < notificationRequests.length; i++) {
431 var result = calendarService.IDataSource.Cancel(notificationRequests[i]);
432 if (result.ErrorCode)
433 error('cancelNotification failed with error code ' + result.ErrorCode);
435 error(
"cancelNotification: " + e + ', line ' + e.line);
440 function callback(transId, eventCode, result)
442 log(
"callback(): panelNum: " + panelNum +
" transId: " + transId +
" eventCode: " + eventCode +
" result.ErrorCode: " + result.ErrorCode);
443 lastReloadTime = null; // force calendar data reload on next update
447 function settingsCallback(transId, eventCode, result)
449 log(
"settingsCallback(): panelNum: " + panelNum +
" transId: " + transId +
" eventCode: " + eventCode +
" result.ErrorCode: " + result.ErrorCode);
453 function parseDate(dateString)
456 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:
457 Wednesday,
26 August,
2009 24:
00:
00
458 Wednesday,
26 August,
2009 12:
00:
00 am
459 Wednesday, August
26,
2009 12:
00:
00 am
460 Wednesday,
2009 August,
26 12:
00:
00 am
461 Wednesday,
2009 August,
28 8.00.00 pm
462 Wednesday,
2009 August,
28 08:
00:
00 PM
466 if (dateString ==
"" || dateString == null || dateString == undefined)
468 if (dateString instanceof Date) {
469 // we already have a date object, no need to parse string here
473 var dateArr = (dateString + '').replace(/,/g, '').replace(/\./g, ':').replace(/ /g, ' ').split(' ');
474 if (dateArr.length !=
5 && dateArr.length !=
6)
478 var weekDay = dateArr[
0];
479 var day = dateArr[
1];
480 var month = dateArr[
2];
481 var year = dateArr[
3];
482 // make sure month is set properly
483 if (isNaN(parseInt(day))) {
489 if (isNaN(parseInt(year))) {
494 // make sure day and year are set properly
495 if (Number(day)
> Number(year)) {
500 month = months_translated[month];
503 var timeArr = dateArr[
4].split(':');
504 if (timeArr.length !=
3)
506 var hours = Number(timeArr[
0]);
507 var minutes = Number(timeArr[
1]);
508 var seconds = Number(timeArr[
2]);
509 if (dateArr.length ==
6 && dateArr[
5].toLowerCase() == 'pm' && hours <
12)
511 if (dateArr.length ==
6 && dateArr[
5].toLowerCase() == 'am' && hours ==
12)
514 result = new Date(year, month -
1, day, hours, minutes, seconds);
517 // take care of daylight saving time
518 if (config['enableDaylightSaving'].Value) {
520 // determine if date is in summer or winter time
521 var dateSummerTime = isSummertime(result);
523 // work around bug in Nokias calendar api resulting in dates within a different DST to be off by
1 hour
524 if (summertime && !dateSummerTime) {
525 result = new Date(result.getTime() -
1000 *
60 *
60 * config['daylightSavingOffset'].Value); // -
1 hour
526 log('parseDate(): fixing time -
1h: ' + result);
528 else if (!summertime && dateSummerTime) {
529 result = new Date(result.getTime() +
1000 *
60 *
60 * config['daylightSavingOffset'].Value); // +
1 hour
530 log('parseDate(): fixing time +
1h: ' + result);
537 function getWeekdayLocalized(date) {
538 var localizedString = date.toLocaleDateString();
539 if (localizedString.indexOf(
",") == -
1) {
540 return weekdays_translated[date.getDay()];
542 return localizedString.split(',')[
0];
545 // 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"
546 function formatDate(date, format)
548 var day = date.getDate().toString();
549 var month = (date.getMonth() +
1).toString();
550 while (day.length <
2) { day = '
0' + day; }
551 while (month.length <
2) { month = '
0' + month; }
553 if (config['showTodayAsText'].Value && isToday(date))
554 return '
<span class=
"today">' + config['todayText'].Value + '
</span>';
555 if (config['showTodayAsText'].Value && isTomorrow(date))
556 return '
<span class=
"tomorrow">' + config['tomorrowText'].Value + '
</span>';
558 if (format instanceof Date) {
559 // we don't know how to format this
560 if (config['dateFormat'].Value == 'auto' || config['dateFormat'].Value == 'DDMM')
561 return day + config['dateSeparator'].Value + month;
563 return month + config['dateSeparator'].Value + day;
565 var dateArr = format.replace(/,/g,'').replace(/\./g,':').replace(/ /g,' ').split(' ');
566 if (dateArr.length !=
5 && dateArr.length !=
6) {
567 // we don't know how to format this
568 if (config['dateFormat'].Value == 'auto' || config['dateFormat'].Value == 'DDMM')
569 return day + config['dateSeparator'].Value + month;
571 return month + config['dateSeparator'].Value + day;
575 if (config['dateFormat'].Value == 'MMDD')
577 else if (config['dateFormat'].Value == 'DDMM')
580 // config['dateFormat'].Value == 'auto', try to detect system setting
582 var day_ = dateArr[
1];
583 var month_ = dateArr[
2];
584 var year_ = dateArr[
3];
585 // make sure month is set properly
586 if (isNaN(parseInt(day_))) {
591 } else if (isNaN(parseInt(year_))) {
597 // make sure day and year are set properly
598 if (Number(day_)
> Number(year_))
603 return day + config['dateSeparator'].Value + month;
605 return month + config['dateSeparator'].Value + day;
608 function formatTime(date)
610 // date is a Date() object
611 var hour = date.getHours();
612 var minute = date.getMinutes();
614 // don't use Date().toLocaleTimeString() as it is utterly broken on newer firmwares
615 if (use12hoursTimeFormat) {
626 minute =
"0" + minute;
627 time = hour + timeFormatSeparator + minute +
" " + ap;
633 minute =
"0" + minute;
634 time = hour + timeFormatSeparator + minute;
637 if (config['showNowAsText'].Value && date.getTime() == now.getTime())
638 time = '
<span class=
"now">' + config['nowText'].Value + '
</span>';
639 log(
"formatTime(): " + time +
", use12hoursTimeFormat=" + use12hoursTimeFormat +
", timeFormatSeparator=" + timeFormatSeparator +
", date.toLocateTimeString(): " + date.toLocaleTimeString());
643 function updateData()
650 // check if we got additional or less calendars since our last update
651 var newCalendarList = listCalendars();
652 if (newCalendarList == null) {
653 // Something went wrong fetching the calendars list.
654 // This usually happens when a backup is being made.
655 // Retry the next time updateData() is called by
656 // resetting errorOccured
657 log('updateData(): listCalendars() failed, trying again later...');
658 cacheEntriesHtml = ''; // make sure we replace the currently shown error message on the next update
659 errorOccured = false;
662 if (newCalendarList.length != calendarList.length) {
663 calendarList = newCalendarList;
664 updateCalendarColors();
665 cancelNotification();
666 requestNotification();
667 lastReloadTime = null; // force calendar data reload on this update
672 // only reload calendar data every
6 hours, visual updates occure more often
673 if (!lastReloadTime || now.getTime() - lastReloadTime.getTime()
> reloadInterval) {
674 log('updateData(): reloading calendar data');
676 // meetings have time
677 // 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
678 summertime = isSummertime(now); // cache summer time info for today
679 var meetingList = [];
680 for(var i=
0; i < calendarList.length; i++) {
681 // ignore excluded calendars
682 if (config['excludedCalendars'].Value.indexOf(calendarList[i]) != -
1)
684 var meetingListFiltering = {
685 Type:'CalendarEntry',
687 CalendarName: calendarList[i],
688 StartRange: (new Date(now.getFullYear(), now.getMonth(), now.getDate(),
0,
0,
0)),
689 EndRange: (new Date(now.getFullYear(), now.getMonth() + config['monthRange'].Value, now.getDate(),
0,
0,
0))
692 var meetingResult = calendarService.IDataSource.GetList(meetingListFiltering);
693 if (meetingResult.ErrorCode !=
0)
694 throw(
"Error fetching calendar data: " + meetingResult.ErrorCode + ': ' + meetingResult.ErrorMessage);
695 var list = meetingResult.ReturnValue;
696 meetingList = meetingList.concat(listToArray(list, calendarList[i]));
698 log(
"updateData(): meetingList.sort()");
699 meetingList.sort(sortCalendarEntries);
701 // todos don't, they start on
00:
00 hrs., but should be visible anyway
702 // this will generate a list of passed todos. We have to check if they have been marked as
"done" yet
703 if (config['includeTodos'].Value) {
704 var todayTodoList = [];
705 for(var i=
0; i < calendarList.length; i++) {
706 // ignore excluded calendars
707 if (config['excludedCalendars'].Value.indexOf(calendarList[i]) != -
1)
709 var todayTodoListFiltering = {
710 Type:'CalendarEntry',
712 CalendarName: calendarList[i],
714 StartRange: (new Date(now.getFullYear() -
1, now.getMonth(), now.getDate(),
0,
0,
0)),
715 EndRange: (new Date(now.getFullYear(), now.getMonth(), now.getDate(),
0,
0,
1))
718 var todayTodoResult = calendarService.IDataSource.GetList(todayTodoListFiltering);
719 var list = todayTodoResult.ReturnValue;
720 todayTodoList = todayTodoList.concat(listToArray(list, calendarList[i]));
722 log(
"updateData(): todayTodoList.sort()");
723 todayTodoList.sort(sortCalendarEntries);
724 entryLists = [todayTodoList, meetingList];
726 entryLists = [meetingList];
728 lastReloadTime = new Date();
730 error('loading Calendar items list:' + e + ', line ' + e.line);
740 var fontsize = getDefaultFontSize()[
1] + 'px';
741 var lineheight = fontsize;
743 if (config['fontsize'].Value == config['fontsize'].ValidValues[
0]) {
744 fontsize = parseInt(
72 / config['eventsPerWidget'].Value) + 'px';
745 lineheight = parseInt(
72 / config['eventsPerWidget'].Value) + 'px';
748 if (config['fontsize'].Value != config['fontsize'].ValidValues[
0]) {
749 fontsize = config['fontsize'].Value + 'px';
750 lineheight = fontsize;
752 changeCssClass('.icon', config['cssStyle_icon'].Value + '; width:' + fontsize + '; height:' + fontsize + ';');
753 var entriesHtml = '
<table style=
"font-size:' + fontsize + '; line-height:' + lineheight + ';">';
755 entriesHtml = '
<table width=
"307" height=
"82"><tr><td>' + entriesHtml; // this is needed to center the actual content vertically
759 max = (panelNum +
1) * config['eventsPerWidget'].Value;
761 max =
30; // we can display a lot more events in fullscreen mode
763 if (config['enableLogging'].Value) {
765 for (var i=
0; i < entryLists.length; i++) {
766 listinfo = listinfo +
" " + entryLists[i].length;
767 var entrieslist =
"";
768 for (var j=
0; j < entryLists[i].length; j++) {
769 entrieslist += entryLists[i][j].Summary +
", ";
771 log(
"updateData(): entrieslist: " + entrieslist);
773 log(
"updateData(): inner loop, " + entryLists.length +
" lists, [" + listinfo +
"] entries");
776 // the first outer loop iteration is for passed ToDos, the second loop is for all upcomming events (may also include ToDos)
777 for (var i=
0; counter < max && i < entryLists.length; i++) {
778 for (var j=
0; (counter < max) && (j < entryLists[i].length); j++) {
779 entry = entryLists[i][j];
782 // output event info for debugging
783 var entryInfo =
"event: ";
784 for(var k=
0; k < entryFields.length; ++k) {
785 if (entry[entryFields[k]] != undefined) {
786 entryInfo += entryFields[k] +
"=" + entry[entryFields[k]] +
",";
791 // we don't want ToDos when includeTodos == false or when they are completed
792 if (entry.Type == 'ToDo' && (entry.Status ==
"TodoCompleted" || !config['includeTodos'].Value)) {
793 log('skipping ' + entry.id );
798 // make sure that we don't include an event twice (useful for ToDos that might come up twice)
799 if (eventIds[entry.id] ==
1 && entry.Type == 'ToDo') {
800 log('skipped (already included) ' + entry.id);
804 eventIds[entry.id] =
1;
806 // summary can be undefined!
807 var Summary = ((entry.Summary == null) ? '' : entry.Summary);
808 if (entry.Location != '' && entry.Location != undefined && config['showLocation'].Value)
809 Summary += ', ' + entry.Location;
811 // fix by yves: determine start and end dates/times
812 entryStartTime = ((entry.InstanceStartTime == null) ? entry.StartTime : entry.InstanceStartTime);
813 entryEndTime = ((entry.InstanceEndTime == null) ? entry.EndTime : entry.InstanceEndTime);
815 // there can be ToDos that have no date at all!
816 if (entry.Type == 'ToDo' && entry.EndTime == null)
817 entryDate =
""; // this will cause parseDate(entryDate) to return null;
819 entryDate = ((entry.Type == 'ToDo') ? entryEndTime : entryStartTime); // ToDo's use their EndTime, the rest use StartTime
821 // Convert date/time string to Date object
822 var date = parseDate(entryDate);
823 log('date: ' + date);
824 var endDate = ((entryEndTime == null) ? null : parseDate(entryEndTime));
825 log('endDate: ' + endDate);
827 // check if Meeting is actually a DayEvent. Bug introduced by
"Anna" updates to various Symbian^
3 devices.
828 // Note that this workaround is not
100% save! It might missinterpret some meetings as dayevents of starting and ending on
00:
00
829 if (entry.Type == 'Meeting' && date.getHours() ==
0 && date.getMinutes() ==
0 &&
830 endDate != null && endDate.getHours() ==
0 && endDate.getMinutes() ==
0) {
831 log('fixing event type: changed from
"Meeting" to
"DayEvent".');
832 entry.Type = 'DayEvent';
835 // check if meeting event has already passed
836 if (entry.Type == 'Meeting') {
837 var compareTime = ((endDate == null) ? date.getTime() : endDate.getTime());
838 if (now.getTime()
> compareTime) {
839 log('skipping Meeting (already passed) ' + entry.id);
841 eventIds[entry.id] =
0;
846 // check if anniversary passed (not sure why they are in the list, the query was only for today - nokia?)
847 if (entry.Type == 'Anniversary') {
848 var tmp = new Date(now.getFullYear(), now.getMonth(), now.getDate(),
0,
0,
0);
849 if (date.getTime() < tmp.getTime()) {
850 log('skipping Anniversary (already passed) ' + entry.id);
852 eventIds[entry.id] =
0;
857 // fix DayEvents end time. End times are off by
1 Second. It's possible that the event has already passed
858 if (entry.Type == 'DayEvent' && endDate != null) {
859 endDate.setMinutes(endDate.getMinutes() -
1);
860 log('fixing DayEvent endDate: ' + endDate);
861 if (now.getTime()
> endDate.getTime()) {
862 log('event already passed ' + entry.id);
864 eventIds[entry.id] =
0;
869 // check if the event is currently taking place
870 if (entryStartTime != null && entryEndTime != null && date != null && endDate != null) {
871 // check if we are between start and endtime
872 if ((date.getTime() < now.getTime()) && (now.getTime() < endDate.getTime())) {
873 date = now; // change appointment date/time to now
874 log('event is currently taking place: ' + date);
878 // skip events for the first panel in case this is the second one and we're not in fullscreen mode
879 if (mode ==
0 && panelNum
> 0 && counter < panelNum * config['eventsPerWidget'].Value +
1) {
880 log('skipping (already in first widget) ' + entry.id);
884 // mark overdue todos
886 if (entry.Type == 'ToDo' && date != null) {
887 var tmp1 = new Date(date.getFullYear(), date.getMonth(), date.getDate(),
0,
0,
0);
888 var tmp2 = new Date(now.getFullYear(), now.getMonth(), now.getDate(),
0,
0,
0);
889 if (tmp1.getTime() < tmp2.getTime()) {
894 // generate html output
895 entriesHtml += '
<tr>';
896 if (config['showCalendarIndicator'].Value && calendarList.length - config['excludedCalendars'].Value.length
> 1) {
897 entriesHtml += '
<td><div class=
"calendar' + calendarColors[entry.CalendarName] + '" style=
"height:' + (lineheight.split("px
")[0] - 1) + 'px; width:4px;"></div></td>';
899 if (config['showIcons'].Value)
900 entriesHtml += '
<td><img class=
"icon" align=
"top" src=
"' + entry.Type + '.png" /></td>';
902 entriesHtml += '
<td style=
"padding:0px;"></td>';
904 // some languages have very strange locale date formats, can't parse all those. Also some todos don't have dates at all.
905 entriesHtml += '
<td colspan=
"4"><span class=
"date">' + entryDate + '
</span> ';
907 var weekDay = getWeekdayLocalized(date).substr(
0,config['weekDayLength'].Value);
908 log('date.toLocaleDateString(): ' + date.toLocaleDateString());
909 log('weekDay: ' + weekDay);
910 var time = formatTime(date);
911 var dateStr = formatDate(date, entryDate);
912 if (entry.Type == 'ToDo' && overdue && config['markOverdueTodos'].Value) {
913 dateStr = '
<span class=
"overdue">' + config['overdueText'].Value + '
</span>';
914 entriesHtml += '
<td colspan=
"4" width=
"1px"><span class=
"date">' + dateStr + '
</span> ';
915 } else if (entry.Type == 'ToDo' || entry.Type == 'Anniversary' || entry.Type == 'DayEvent' || entry.Type == 'Reminder') {
916 if ((isToday(date) || isTomorrow(date)) && config['showTodayAsText'].Value) // show weekday if the date string is not text. looks odd otherwise
917 entriesHtml += '
<td colspan=
"4" width=
"1px"><span class=
"date">' + dateStr + '
</span> ';
919 entriesHtml += '
<td class=
"weekDay" width=
"1px">' + weekDay + '
</td><td width=
"1px" class=
"date">' + dateStr + '
</td><td colspan=
"2">';
920 } else if (entry.Type == 'Meeting') {
921 if (config['showCombinedDateTime'].Value) {
923 entriesHtml += '
<td width=
"1px" colspan=
"4"><span class=
"today">' + time + '
</span> ';
924 else if (isTomorrow(date))
925 entriesHtml += '
<td width=
"1px" colspan=
"4"><span class=
"tomorrow">' + dateStr + '
</span> <span class=
"time">' + time + '
</span> ';
927 entriesHtml += '
<td width=
"1px" class=
"weekDay">' + weekDay + '
</td><td width=
"1px" class=
"date">' + dateStr + '
</td><td colspan=
"2">';
929 if ((isToday(date) || isTomorrow(date)) && config['showTodayAsText'].Value)
930 entriesHtml += '
<td colspan=
"4" width=
"1px"><span class=
"today">' + dateStr + '
</span> <span class=
"time">' + time + '
</span> ';
932 entriesHtml += '
<td width=
"1px" class=
"weekDay">' + weekDay + '
</td><td width=
"1px" class=
"date">' + dateStr + '
</td><td width=
"1px" class=
"time">' + time + '
</td><td>';
936 entriesHtml += '
<span class=
"description">' + Summary + '
</span></td></tr>';
939 entriesHtml += '
</table>';
941 entriesHtml = entriesHtml + '
</td></tr></table>';
942 if (config['showNothingText'].Value && entriesHtml == '
<table></table>') {
943 var text = config['nothingText'].Value.replace(/%d/, config['monthRange'].Value);
944 entriesHtml = '
<div style=
"width:295px; height:75px; text-align:center; line-height:75px; overflow:visible;">' + text + '
</div>';
946 log(
"output: " + entriesHtml);
947 if (cacheEntriesHtml != entriesHtml) {
949 document.getElementById('calendarList').innerHTML = entriesHtml;
951 document.getElementById('fullscreenCalendarList').innerHTML = entriesHtml;
952 cacheEntriesHtml = entriesHtml;
955 lastUpdateTime = new Date();
957 error('displaying list:' + e + ', line ' + e.line);
962 // called by handleOnShow() and onResize events
963 function updateScreen()
965 log('updateScreen(): mode=' + mode + ', window.innerHeight=' + window.innerHeight);
967 // check if opening fullscreen
969 // Note: according to Nokia's documentation, an innerHeight of
>91 is an indicator for fullscreen view.
970 // However a bug in E6's firmware causes different window widths and heights (disabled compatibility scaling).
971 // So far, values of
104 and
115 for window.innerHeight were reported, we use a safty margin here and check
973 if( window.innerHeight
> 150 && mode ==
0) {
975 cacheEntriesHtml = '';
976 document.getElementById('body').style.backgroundImage =
"";
979 else if (window.innerHeight <=
150 && mode !=
0) {
981 cacheEntriesHtml = '';
986 updateHomescreen(); // check for screen rotation
991 function handleOnShow()
995 var time = new Date();
996 if (time.getTime() - lastUpdateTime.getTime()
> config['updateDataInterval'].Value *
60 *
1000) {
997 log('updateScreen(): force updateData() because last update was too long ago (' + (time.getTime() - lastUpdateTime.getTime()) /
1000 + 's)');
1000 setUpdateTimer(); // reinitialize update timer
1004 function launchCalendar()
1007 widget.openApplication(config['calendarApp'].Value,
"");
1008 if (config['hideWidgetOnCalendarOpen'].Value)
1011 error('starting Calendar App');
1018 log('New widget instance starting up...');
1021 // call calendar service
1022 if (device !=
"undefined")
1023 calendarService = device.getServiceObject(
"Service.Calendar",
"IDataSource");
1025 throw('device object does not exist');
1027 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>');
1031 calendarList = listCalendars();
1033 updateCalendarColors();
1036 requestNotification();
1037 document.getElementById(
"settingsTitle").innerHTML = getLocalizedText('menu.settings');
1039 if (window.innerHeight
> 91) {
1040 mode =
0; // we're starting fullscreen, we set mode to homescreen in order to let updateScreen() do all the work for us
1045 log(
"init(): updateScreen()");
1047 if (config['useBackgroundImage'].Value)
1048 // check for screen rotation every
1 secs
1049 screenRotationTimer = window.setInterval('checkOrientation()',
1000 *
1);
1051 // call updateScreen() when widget changes from background to forground
1052 window.widget.onshow = handleOnShow;
1054 log(
"init(): finished...");
1056 statupSuccessful = true;
1059 function checkOrientation()
1063 updateHomescreen(); // check for screen rotation
1066 function setUpdateTimer()
1068 updateTimer = window.setInterval('updateTimerCallback()',
1000 *
60 * config['updateDataInterval'].Value);
1071 function clearUpdateTimer()
1073 window.clearInterval(updateTimer);
1076 function updateTimerCallback()
1078 log(
"updateTimerCallback()");
1082 function createMenu()
1084 window.menu.setLeftSoftkeyLabel(
"",null);
1085 window.menu.setRightSoftkeyLabel(
"",null);
1087 var menuSettings = new MenuItem(getLocalizedText('menu.settings'), id++);
1088 var menuCallApp = new MenuItem(getLocalizedText('menu.openCalendarApp'), id++);
1089 var menuHelp = new MenuItem(getLocalizedText('menu.help'), id++);
1090 var menuUpdate = new MenuItem(getLocalizedText('menu.update'), id++);
1091 var menuAbout = new MenuItem(getLocalizedText('menu.about'), id++);
1092 menuSettings.onSelect = showSettings;
1093 menuAbout.onSelect = showAbout;
1094 menuCallApp.onSelect = launchCalendar;
1095 menuUpdate.onSelect = showUpdate;
1096 menuHelp.onSelect = showHelp;
1097 window.menu.clear();
1098 window.menu.append(menuCallApp);
1099 window.menu.append(menuSettings);
1100 window.menu.append(menuHelp);
1101 window.menu.append(menuUpdate);
1102 window.menu.append(menuAbout);
1105 function showSettings()
1109 document.getElementById(
"settingsView").style.display =
"block";
1110 document.onclick = null;
1112 window.menu.setLeftSoftkeyLabel(getLocalizedText('settings.save'), function()
1114 for (var key in config) {
1115 if (config[key].Type == 'String')
1116 config[key].Value = document.forms[
0].elements[
"settings." + key].value;
1117 else if (config[key].Type == 'Int') {
1118 config[key].Value = parseInt(document.forms[
0].elements[
"settings." + key].value);
1119 if (config[key].Value <
0 || isNaN(config[key].Value))
1120 config[key].Value = config[key].Default;
1122 else if (config[key].Type == 'Bool')
1123 config[key].Value = document.forms[
0].elements[
"settings." + key].checked;
1124 else if (config[key].Type == 'UID') {
1125 config[key].Value = parseInt(document.forms[
0].elements[
"settings." + key].value);
1126 if (isNaN(config[key].Value))
1127 config[key].Value = config[key].Default;
1129 else if (config[key].Type == 'Enum') {
1130 config[key].Value = document.forms[
0].elements[
"settings." + key].value;
1131 if (config[key].ValidValues.indexOf(config[key].Value) == -
1)
1132 config[key].Value = config[key].Default;
1134 else if (config[key].Type == 'Array') {
1135 if (key == 'excludedCalendars') {
1136 config[key].Value = new Array();
1137 for(var i=
0; i < calendarList.length; i++) {
1138 var element = document.forms[
0].elements[
"settings." + key +
"." + calendarList[i]];
1139 if (element != null && element.checked == false)
1140 config[key].Value.push(calendarList[i]);
1153 window.menu.setRightSoftkeyLabel(getLocalizedText('settings.cancel'), function()
1159 var settingsHtml = '
<form>';
1160 for (var key in config) {
1161 if (config[key].Type == 'String') {
1163 if (key.substring(
0,
9) ==
"cssStyle_")
1164 prefix = getLocalizedText('settings.cssStyle_prefix');
1165 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 />';
1167 else if (config[key].Type == 'Int')
1168 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 />';
1169 else if (config[key].Type == 'Bool')
1170 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 />';
1171 else if (config[key].Type == 'UID')
1172 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 />';
1173 else if (config[key].Type == 'Enum') {
1174 settingsHtml += '
<table><tr><td>' + getLocalizedText('settings.name.' + key) + '
<br /><select name=
"settings.' + key + '" size=
"1">';
1175 for(var i =
0; i < config[key].ValidValues.length; i++) {
1176 var text = getLocalizedText('settings.validValues.' + key + '.' + config[key].ValidValues[i]);
1177 if (text.indexOf('ERROR') ==
0)
1178 text = config[key].ValidValues[i];
1179 settingsHtml += '
<option value=
"' + config[key].ValidValues[i] + '"' + (config[key].Value == config[key].ValidValues[i] ? '
selected=
"selected"' : '') + '
>' + text + '
</option>';
1181 settingsHtml += '
</select></div></td>' + printHintBox(getLocalizedText('settings.info.' + key)) + '
<hr />';
1183 else if (config[key].Type == 'Array') {
1184 settingsHtml += '
<table><tr><td>' + getLocalizedText('settings.name.' + key) + '
<br />';
1185 if (key == 'excludedCalendars') {
1186 for(var i=
0; i < calendarList.length; i++) {
1187 var checked = '
checked=
"checked"';
1188 if (config[key].Value.indexOf(calendarList[i]) != -
1)
1190 settingsHtml += '
<input name=
"settings.' + key + '.' + calendarList[i] + '" type=
"checkbox" value=
"' + calendarList[i] + '" ' + checked + '
/> ' + calendarList[i] + '
<br />';
1193 settingsHtml += '
</td>' + printHintBox(getLocalizedText('settings.info.' + key)) + '
<hr />';
1196 settingsHtml += '
<input name=
"reset" type=
"button" value=
"' + getLocalizedText('settings.restoreDefaults') + '" onclick=
"javascript:restoreDefaultSettings();showSettings();" />';
1197 settingsHtml += '
</form>';
1198 document.getElementById(
"settingsList").innerHTML = settingsHtml;
1201 function changeCssClass(classname, properties)
1203 for(var i =
0; i < document.styleSheets[
0]['cssRules'].length; i++)
1205 if (document.styleSheets[
0]['cssRules'][i].selectorText == classname) {
1206 document.styleSheets[
0].deleteRule(i);
1207 document.styleSheets[
0].insertRule(classname + ' { ' + properties + ' }', document.styleSheets[
0]['cssRules'].length);
1213 function updateCssClasses()
1215 for(var key in config) {
1216 changeCssClass(getLocalizedText('settings.name.' + key), config[key].Value);
1220 function getSettingsCalEntryId()
1222 if (settingsCalEntryId == null) {
1223 // check if entry already exists
1224 var listFiltering = {
1225 Type:'CalendarEntry',
1227 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!
1228 EndRange: new Date(
2000,
0,
2),
1229 SearchText: 'ComingNext Settings|',
1235 result = calendarService.IDataSource.GetList(listFiltering);
1236 if (result.ErrorCode)
1237 throw(result.ErrorMessage);
1240 error(
"getSettingsCalEntryId: GetList() failed: " + e + ', line ' + e.line);
1243 var list = result.ReturnValue;
1244 var entry = list.getNext();
1245 if (entry != undefined) {
1246 settingsCalEntryId = entry.LocalId;
1247 log(
"settingsCalEntryId=" + settingsCalEntryId);
1249 else { // create settings item
1250 var item = new Object();
1251 item.Type =
"DayEvent";
1252 item.StartTime = new Date(
2000,
0,
1);
1253 item.Summary =
"ComingNext Settings|";
1255 var criteria = new Object();
1256 criteria.Type =
"CalendarEntry";
1257 criteria.Item = item;
1260 var result = calendarService.IDataSource.Add(criteria);
1261 if (result.ErrorCode)
1262 throw(result.ErrorMessage);
1264 error(
"getSettingsCalEntryId: " + e + ', line ' + e.line);
1267 getSettingsCalEntryId();
1272 function restoreDefaultSettings()
1274 for (var key in config)
1275 config[key].Value = config[key].Default;
1278 function loadSettings()
1280 getSettingsCalEntryId();
1281 var listFiltering = {
1282 Type:'CalendarEntry',
1284 LocalId: settingsCalEntryId
1289 result = calendarService.IDataSource.GetList(listFiltering);
1290 if (result.ErrorCode)
1291 throw(result.ErrorMessage);
1294 error(
"loadSettings: GetList() failed: " + e + ', line ' + e.line);
1297 var entry = result.ReturnValue.getNext();
1298 if (entry != undefined) {
1299 log(
"Loading Settings...");
1300 // only reload settings if they chanced since the last reload
1301 if (settingsCache != entry.Summary)
1303 restoreDefaultSettings();
1304 var stringlist = entry.Summary.split(
"|");
1305 // skip the first two entries, those contain header and version info
1306 for(var i =
2; i < stringlist.length -
1; i++) {
1307 var pair = stringlist[i].split('=');
1309 var value = pair[
1];
1310 if (key == null || value == null || config[key] == null) {
1311 log('Warning: unknown or invalid setting: ' + stringlist[i]);
1314 log('stringlist[' + i + ']: ' + key + '=\'' + value + '\'');
1315 if (config[key].Type == 'Int') {
1316 config[key].Value = Number(value);
1317 if (isNaN(config[key].Value))
1318 config[key].Value = config[key].Default;
1320 else if (config[key].Type == 'String')
1321 config[key].Value = value;
1322 else if (config[key].Type == 'Bool')
1323 config[key].Value = (value == 'true')
1324 else if (config[key].Type == 'Enum')
1325 config[key].Value = value;
1326 else if (config[key].Type == 'UID') {
1327 config[key].Value = Number(value);
1328 if (isNaN(config[key].Value))
1329 config[key].Value = config[key].Default;
1331 else if (config[key].Type == 'Array') {
1332 config[key].Value = value.split(
"^");
1333 if (config[key].Value.length ==
1 && config[key].Value[
0] ==
"") {
1334 config[key].Value = [];
1338 settingsCache = entry.Summary;
1342 log(
"Settings already cached and did not change");
1346 error(
"Failed to load settings, calendar entry could not be found");
1350 function saveSettings()
1352 getSettingsCalEntryId();
1353 var item = new Object();
1354 item.Type =
"DayEvent";
1355 item.StartTime = new Date(
2000,
0,
1);
1356 item.LocalId = settingsCalEntryId;
1357 item.Summary =
"ComingNext Settings|" + version +
"|";
1359 for (var key in config) {
1360 if (config[key].Type == 'Int')
1361 item.Summary += key +
"=" + config[key].Value.toString() +
"|";
1362 else if (config[key].Type == 'String')
1363 item.Summary += key +
"=" + config[key].Value +
"|";
1364 else if (config[key].Type == 'Bool')
1365 item.Summary += key +
"=" + (config[key].Value ? 'true' : 'false') +
"|";
1366 else if (config[key].Type == 'Enum')
1367 item.Summary += key +
"=" + config[key].Value +
"|";
1368 else if (config[key].Type == 'UID')
1369 item.Summary += key +
"=" + config[key].Value.toString() +
"|";
1370 else if (config[key].Type == 'Array')
1371 item.Summary += key +
"=" + config[key].Value.join(
"^") +
"|";
1373 settingsCache = item.Summary;
1375 var criteria = new Object();
1376 criteria.Type =
"CalendarEntry";
1377 criteria.Item = item;
1379 log(
"Saving settings to calendar entry: " + item.Summary);
1381 var result = calendarService.IDataSource.Add(criteria);
1382 if (result.ErrorCode)
1383 throw(result.ErrorMessage);
1385 error(
"saveSettings: " + e + ', line ' + e.line);
1388 lastReloadTime = null; // force calendar data reload on next update
1393 function toggleVisibility(elementId)
1395 if (document.getElementById(elementId).style.display ==
"none")
1396 document.getElementById(elementId).style.display =
"block";
1398 document.getElementById(elementId).style.display =
"none";
1402 function printHintBox(text)
1405 return '
<td width=
"1%" align=
"right" onclick=
"javascript:toggleVisibility(\'info' + uniqueId + '\')">' + getLocalizedText('settings.help') + '
</td></tr></table>'+
1406 '
<div class=
"settingsInfo" id=
"info' + uniqueId + '" style=
"display:none">' + text + '
</div>';
1409 function showAbout()
1413 document.getElementById(
"aboutView").style.display =
"block";
1414 document.onclick = null;
1416 window.menu.setLeftSoftkeyLabel(
" ", function(){});
1417 window.menu.setRightSoftkeyLabel(getLocalizedText('softkey.back'), function()
1423 //document.getElementById(
"aboutView").innerHTML = 'aboutView';
1424 document.getElementById(
"name").innerHTML =
"Coming Next " + version;
1427 function showHelp() {
1428 widget.openURL('http://comingnext.sf.net/help');
1431 function updateFullscreen()
1435 function showFullscreen()
1437 log(
"showFullscreen()");
1439 document.getElementById(
"fullscreenView").style.display =
"block";
1440 document.getElementById('body').className =
"backgroundFullscreen";
1442 document.onclick = launchCalendar;
1447 function getBackgroundImage()
1452 if (config['backgroundImageLocation'].Value == config['backgroundImageLocation'].ValidValues[
0]) // internal
1453 bgImage = 'background_' + orientation + '.png';
1455 bgImage = 'C:/Data/background_' + panelNum + '_' + orientation + '.png';
1459 function updateHomescreen()
1461 if (config['useBackgroundImage'].Value) {
1462 // check if we have a completely unknown screen resolution
1463 var screenHeight = screen.height;
1464 var screenWidth = screen.width;
1465 if (screenHeight !=
640 && screenHeight !=
480 && screenHeight !=
360)
1466 screenHeight =
360; // we can only assume we're in portrait mode, so we set the screen dims as needed for the following code
1467 if (screenWidth !=
640 && screenWidth !=
480 && screenWidth !=
360)
1468 screenWidth =
640; // we can only assume we're in portrait mode, so we set the screen dims as needed for the following code
1470 // check for screen rotation
1471 if (orientation != 'portrait' && ((screenWidth ==
360 && screenHeight ==
640) || (screenWidth ==
640 && screenHeight ==
480))) {
1472 window.widget.prepareForTransition(
"fade");
1473 orientation = 'portrait';
1474 document.getElementById('body').style.backgroundImage = 'url(' + getBackgroundImage() + ')';
1475 document.getElementById('body').style.backgroundColor = 'none';
1476 window.widget.performTransition();
1477 } else if (orientation != 'landscape' && ((screenWidth ==
640 && screenHeight ==
360) || (screenWidth ==
480 && screenHeight ==
640))) {
1478 window.widget.prepareForTransition(
"fade");
1479 orientation = 'landscape';
1480 document.getElementById('body').style.backgroundImage = 'url(' + getBackgroundImage() + ')';
1481 document.getElementById('body').style.backgroundColor = 'none';
1482 window.widget.performTransition();
1484 else if (document.getElementById('body').style.backgroundImage ==
"")
1486 document.getElementById('body').style.backgroundImage = 'url(' + getBackgroundImage() + ')';
1491 function showHomescreen()
1493 log(
"showHomescreen()");
1495 document.getElementById(
"homescreenView").style.display =
"block";
1496 document.getElementById('body').className =
"background";
1497 document.onclick = null;
1501 function getLocalizedText(p_Txt)
1503 if (localizedText[p_Txt])
1504 return localizedText[p_Txt];
1506 return 'ERROR: missing translation for ' + p_Txt;
1509 function showUpdate()
1513 document.getElementById(
"updateView").style.display =
"block";
1514 document.onclick = null;
1516 window.menu.setLeftSoftkeyLabel(getLocalizedText('update.checknow'), function(){
1519 window.menu.setRightSoftkeyLabel(getLocalizedText('softkey.back'), function()
1525 document.getElementById(
"currentVersion").innerHTML = getLocalizedText(
"update.current") + version;
1529 function checkForUpdate()
1531 // asynch XHR to server url
1532 reqV = new XMLHttpRequest();
1533 reqV.onreadystatechange = checkForUpdateCallback;
1534 document.getElementById(
"updateDiv").innerHTML = getLocalizedText(
"update.checking");
1535 reqV.open(
"GET", versionURL, true);
1539 function checkForUpdateCallback()
1541 if (reqV.readyState ==
4) {
1542 if (reqV.status ==
200) {
1543 var resultXml = reqV.responseText;
1545 var div = document.getElementById(
"tmp");
1546 div.innerHTML = resultXml;
1547 var newVersion = div.getElementsByTagName('version')[
0].innerHTML;
1548 var newVersionURL = div.getElementsByTagName('url')[
0].innerHTML;
1550 if (version != newVersion) {
1551 document.getElementById(
"updateDiv").innerHTML = getLocalizedText(
"update.download").replace(/%
1/, newVersion).replace(/%
2/, newVersionURL);
1554 document.getElementById(
"updateDiv").innerHTML = getLocalizedText(
"update.nonewversion");
1559 document.getElementById(
"updateDiv").innerHTML = getLocalizedText(
"update.error") + reqV.status +
" " + reqV.responseText;
1564 function hideViews()
1566 document.getElementById(
"homescreenView").style.display =
"none";
1567 document.getElementById(
"fullscreenView").style.display =
"none";
1568 document.getElementById(
"aboutView").style.display =
"none";
1569 document.getElementById(
"settingsView").style.display =
"none";
1570 document.getElementById(
"updateView").style.display =
"none";
1573 function listCalendars()
1583 DefaultCalendar: false
1587 var calendarsResult = calendarService.IDataSource.GetList(criteria);
1588 if (calendarsResult.ErrorCode !=
0)
1589 throw(
"Error fetching list of calendars: " + calendarsResult.ErrorCode + ': ' + calendarsResult.ErrorMessage);
1590 var calendarListIterator = calendarsResult.ReturnValue;
1595 while (( item = calendarListIterator.getNext()) != undefined ) {
1596 calendars[count++] = item;
1598 log(
"Available Calendars: " + calendars.join(
", "));
1601 error('listing calendars:' + e + ', line ' + e.line);
1606 // Copies all objects and their properties to an array. Data is copied so nothing gets lost when the reference is removed
1607 // 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
1608 function listToArray(list, calendarName)
1610 var array = new Array();
1613 while (( item = list.getNext()) != undefined ) {
1614 var itemCopy = new Object();
1615 for(var i=
0; i < entryFields.length; i++) {
1616 itemCopy[entryFields[i]] = item[entryFields[i]];
1618 // for some reason, the CalendarName property is never correctly queried, so we assign it manually here
1619 if (!itemCopy['CalendarName']) {
1620 itemCopy['CalendarName'] = calendarName;
1622 if (config['anonymizeLogging'].Value && config['enableLogging'].Value) {
1623 if (itemCopy['Summary'])
1624 itemCopy['Summary'] = getHashForString(itemCopy['Summary']);
1625 if (itemCopy['Location'])
1626 itemCopy['Location'] = getHashForString(itemCopy['Location']);
1628 array.push(itemCopy);
1629 txt += array[array.length -
1].Summary +
", ";
1631 log(
"listToArray(): " + txt);
1635 function sortCalendarEntries(a, b)
1638 log(
"sortCalendarEntries(" + a.Summary +
"," + b.Summary +
")");
1640 if (a.InstanceStartTime != null) {
1641 atime = a.InstanceStartTime;
1643 else if (a.StartTime != null) {
1644 atime = a.StartTime;
1646 else if (a.InstanceEndTime != null) {
1647 atime = a.InstanceEndTime;
1649 else if (a.EndTime != null) {
1653 if (b.InstanceStartTime != null) {
1654 btime = b.InstanceStartTime;
1656 else if (b.StartTime != null) {
1657 btime = b.StartTime;
1659 else if (b.InstanceEndTime != null) {
1660 btime = b.InstanceEndTime;
1662 else if (b.EndTime != null) {
1666 if (atime && btime) {
1668 atime = parseDate(atime);
1669 btime = parseDate(btime);
1671 // sort by date & time
1672 if (atime < btime) {
1675 else if (atime
> btime) {
1679 else if (a.Type != b.Type) {
1680 if (a.Type < b.Type) {
1683 else if (a.Type
> b.Type) {
1687 // sort by description
1688 else if (a.Summary && b.Summary && a.Summary != b.Summary) {
1689 if (a.Summary < b.Summary) {
1692 else if (a.Summary
> b.Summary) {
1697 // NOTE: events my have no date information at all. In that case, we list events without date first
1698 else if (atime && !btime) {
1701 else if (!atime && btime) {
1704 else if (!atime && !btime) {
1706 if (a.Type != b.Type) {
1707 if (a.Type < b.Type) {
1710 else if (a.Type
> b.Type) {
1714 // sort by description
1715 else if (a.Summary && b.Summary && a.Summary != b.Summary) {
1716 if (a.Summary < b.Summary) {
1719 else if (a.Summary
> b.Summary) {
1728 function updateCalendarColors()
1731 calendarColors = [];
1732 if (calendarList.length
> maxColors) {
1733 log(
"updateCalendarColors(): Warning: more calendars than available indicator colors");
1735 for(var i=
0; i < calendarList.length; i++) {
1736 calendarColors[calendarList[i]] = (i % maxColors) +
1;
1740 function log(message)
1742 if (config['enableLogging'].Value) {
1743 console.info(message);
1747 function getDefaultFontSize()
1749 if (defaultFontSize == null) {
1750 var pa = document.body;
1751 var who = document.createElement('div');
1752 who.className = 'defaultEm';
1753 who.appendChild(document.createTextNode('M'));
1754 pa.appendChild(who);
1755 var fs = [who.offsetWidth, who.offsetHeight];
1756 pa.removeChild(who);
1757 defaultFontSize = fs;
1759 return defaultFontSize;
1762 function getHashForString(string)
1764 // cheap hashing, loosly based on Java's String.hashCode()
1765 for (var hash =
0, i =
0; i < string.length; i++)
1766 hash = (hash <<
5) - hash + string.charCodeAt(i);
1767 hash = hash & hash; // Convert to
32bit integer
1770 return hash.toString(
16).toUpperCase();
1775 <style type=
"text/css">
1777 table { margin:
0px; padding:
0px; border-spacing:
0px; border-collapse: collapse; }
1778 td { padding:
0px
5px
0px
0px; white-space:nowrap; overflow:visible; margin:
0px; }
1779 hr { color:#ffffff; background-color:#ffffff; height:
1px; text-align:left; border-style:none; }
1780 .settingsInfo { display:none; font-style:italic; }
1781 .title { font-weight:bold; font-size:
14pt; }
1782 .textInput { width:
90%; }
1783 .credits { margin-left:
40px; text-indent: -
20px; margin-bottom:
0px; }
1784 #homescreenView { width:
312px; height:
82px; overflow:hidden; }
1785 #calendarList { position:absolute; left:
5px; top:
0px; width:
307px; height:
82px; overflow:hidden; }
1786 #name { text-align:center; }
1787 #appicon { display: block; margin-left: auto; margin-right: auto; margin-top:
10px; }
1788 #smallappicon { width:
22px; height:
22px; margin-right:
10px; float:left; }
1789 .defaultEm { font-size:
1em; position:absolute; line-height:
1; padding:
0; visibility:hidden; }
1794 <body onload=
"javascript:setTimeout('init()', 10)" onresize=
"javascript:updateScreen()" id=
"body" class=
"background">
1795 <div id=
"homescreenView">
1796 <div id=
"calendarList">loading...
</div>
1798 <div id=
"fullscreenView" style=
"display:none;">
1799 <img src=
"Icon.png" id=
"smallappicon">
1800 <h1 class=
"title">Coming Next
</h1>
1802 <div id=
"fullscreenCalendarList">loading...
</div>
1804 <div id=
"settingsView" style=
"display:none">
1805 <img src=
"Icon.png" id=
"smallappicon">
1806 <h1 id=
"settingsTitle" class=
"title">Settings
</h1>
1808 <div id=
"settingsList"></div>
1810 <div id=
"aboutView" style=
"display:none">
1811 <img src=
"Icon.png" id=
"appicon">
1812 <h1 id=
"name">Coming Next
</h1>
1814 <p>Created by Dr. Cochambre and Michael Prager.
</p>
1815 <p>Contributions:
</p>
1816 <p class=
"credits">Paul Moore (bug fixes, new features and code cleanup)
</p>
1817 <p class=
"credits">Manfred Hanselmann (DST support)
</p>
1818 <p class=
"credits">Christophe Milsent (translation support & French translation)
</p>
1819 <p class=
"credits">Flavio Nathan (Portuguese-Brazilian translation)
</p>
1820 <p class=
"credits">Tokeda (Russian translation)
</p>
1821 <p class=
"credits">Marcella Ferrari (Italian translation)
</p>
1822 <p class=
"credits">Venos (Italian translation)
</p>
1823 <p class=
"credits">Francisco Rodero (Catalan translation)
</p>
1824 <p class=
"credits">zbigzbig20 (Polish translation)
</p>
1825 <p class=
"credits">Streamkeskus (Finnish translation)
</p>
1826 <p class=
"credits">renek (Czech translation)
</p>
1827 <p>This software is open source and licensed under the GPLv3.
</p>
1828 <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>
1831 <div id=
"updateView" style=
"display:none">
1832 <img src=
"Icon.png" id=
"smallappicon">
1833 <h1 class=
"title">Check for update
</h1>
1835 <div id=
"currentVersion">Coming Next ??
</div>
1836 <div id=
"updateDiv"></div>
1837 <div id=
"tmp" style=
"display:none;"></div>