1 <?xml version=
"1.0" encoding=
"UTF-8"?>
2 <!DOCTYPE html PUBLIC
"-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
3 <html xmlns=
"http://www.w3.org/1999/xhtml">
6 <title>Coming Next
</title>
8 <style type=
"text/css">
9 /* The following classes can be modified by widget settings */
10 .background { color:#ffffff; background-color:#
000000 }
11 .backgroundFullscreen { color:#ffffff; background-color:#
000000 }
14 .today { color:#ff0000 }
15 .tomorrow { color:#
0000ff }
17 .now { color:#ff00ff }
19 .icon { width:
15px; height:
15px }
20 .overdue { color:#ffff00 }
21 .calendar1 { background-color:#
0757cf }
22 .calendar2 { background-color:#
579f37 }
23 .calendar3 { background-color:#ff9f07 }
24 .calendar4 { background-color:#af8fef }
25 .calendar5 { background-color:#
57afbf }
26 .calendar6 { background-color:#
9fdf57 }
29 <script type=
"text/javascript" src=
"localizedTextStrings.js" charset=
"utf-8"></script>
30 <script type=
"text/javascript" src=
"../debug.js" charset=
"utf-8"></script>
31 <script type=
"text/javascript">
32 // valid types for the config object are 'Int', 'Bool', 'String', 'Enum', 'UID', 'Array'
34 monthRange: { Type: 'Int', Default:
2, Value:
2,},
35 includeTodos: { Type: 'Bool', Default: true, Value: true,},
36 useBackgroundImage: { Type: 'Bool', Default: true, Value: true,},
37 backgroundImageLocation: { Type: 'Enum', Default: 'internal', Value: 'internal', ValidValues: ['internal', 'external']},
38 showCombinedDateTime: { Type: 'Bool', Default: false, Value: false,},
39 showLocation: { Type: 'Bool', Default: true, Value: true,},
40 showIcons: { Type: 'Bool', Default: true, Value: true,},
41 showTodayAsText: { Type: 'Bool', Default: true, Value: true,},
42 todayText: { Type: 'String', Default: getLocalizedText('settings.default.todayText'), Value: getLocalizedText('settings.default.todayText'),},
43 tomorrowText: { Type: 'String', Default: getLocalizedText('settings.default.tomorrowText'), Value: getLocalizedText('settings.default.tomorrowText'),},
44 showNowAsText: { Type: 'Bool', Default: true, Value: true,},
45 nowText: { Type: 'String', Default: getLocalizedText('settings.default.nowText'), Value: getLocalizedText('settings.default.nowText'),},
46 markOverdueTodos: { Type: 'Bool', Default: true, Value: true,},
47 overdueText: {Type: 'String', Default: getLocalizedText('settings.default.overdueText'), Value: getLocalizedText('settings.default.overdueText'),},
48 dateSeparator: { Type: 'String', Default: getLocalizedText('settings.default.dateSeparator'), Value: getLocalizedText('settings.default.dateSeparator'),},
49 dateFormat: { Type: 'Enum', Default: 'auto', Value: 'auto', ValidValues: ['auto', 'DDMM', 'MMDD'],},
50 weekDayLength: { Type: 'Int', Default:
2, Value:
2,},
51 updateDataInterval: { Type: 'Int', Default:
5, Value:
5,},
52 calendarApp: { Type: 'UID', Default:
0x10005901, Value:
0x10005901,},
53 eventsPerWidget: { Type: 'Int', Default:
4, Value:
4,},
54 showNothingText: { Type: 'Bool', Default: true, Value: true,},
55 nothingText: { Type: 'String', Default: getLocalizedText('settings.default.nothingText'), Value: getLocalizedText('settings.default.nothingText'),},
56 enableDaylightSaving: { Type: 'Bool', Default: true, Value: true,},
57 daylightSavingOffset: { Type: 'Int', Default:
1, Value:
1,},
58 hideWidgetOnCalendarOpen: { Type: 'Bool', Default: false, Value: false,},
59 showCalendarIndicator: { Type: 'Bool', Default: true, Value: true,},
60 excludedCalendars: { Type: 'Array', Default: [], Value: [],},
61 enableLogging: { Type: 'Bool', Default: false, Value: false,},
62 cssStyle_background: { Type: 'String', Default: 'color:#ffffff; background-color:#
000000', Value: 'color:#ffffff; background-color:#
000000',},
63 cssStyle_backgroundFullscreen: { Type: 'String', Default: 'color:#ffffff; background-color:#
000000', Value: 'color:#ffffff; background-color:#
000000',},
64 cssStyle_weekDay: { Type: 'String', Default: '', Value: '',},
65 cssStyle_date: { Type: 'String', Default: '', Value: '',},
66 cssStyle_today: { Type: 'String', Default: 'color:#ff0000', Value: 'color:#ff0000',},
67 cssStyle_tomorrow: { Type: 'String', Default: 'color:#
0000ff', Value: 'color:#
0000ff',},
68 cssStyle_time: { Type: 'String', Default: '', Value: '',},
69 cssStyle_now: { Type: 'String', Default: 'color:#ff00ff', Value: 'color:#ff00ff',},
70 cssStyle_description: { Type: 'String', Default: '', Value: '',},
71 cssStyle_icon: { Type: 'String', Default: '', Value: '',},
72 cssStyle_overdue: { Type: 'String', Default: 'color:#ffff00', Value: 'color:#ffff00',},
73 cssStyle_calendar1: { Type: 'String', Default: 'background-color:#
0757cf', Value: 'background-color:#
0757cf',},
74 cssStyle_calendar2: { Type: 'String', Default: 'background-color:#
579f37', Value: 'background-color:#
579f37',},
75 cssStyle_calendar3: { Type: 'String', Default: 'background-color:#ff9f07', Value: 'background-color:#ff9f07',},
76 cssStyle_calendar4: { Type: 'String', Default: 'background-color:#af8fef', Value: 'background-color:#af8fef',},
77 cssStyle_calendar5: { Type: 'String', Default: 'background-color:#
57afbf', Value: 'background-color:#
57afbf',},
78 cssStyle_calendar6: { Type: 'String', Default: 'background-color:#
9fdf57', Value: 'background-color:#
9fdf57',},
83 //-------------------------------------------------------
84 // Nothing of interest from here on...
85 //-------------------------------------------------------
86 var panelNum =
0; // use
1 for second panel
88 var versionURL =
"http://comingnext.sourceforge.net/version.xml";
89 var calendarService = null;
90 var cacheEntriesHtml = [];
91 var months_translated = [];
92 var weekdays_translated = [];
95 var mode =
0; //
0 = homescreen,
1 = fullscreen,
2 = settings,
3 = about,
4 = check for update
97 var settingsCalEntryId = null;
98 var settingsCache = null;
99 var notificationRequests = new Array();
100 var calendarList = [];
101 var calendarColors = [];
102 var updateTimer = null;
103 var screenRotationTimer = null;
104 var lastUpdateTime = now; // last time we updated the display
105 var lastReloadTime = null; // last time we fetched calendar data
106 var reloadInterval =
6 *
60 *
60 *
1000; // =
6 hours; time interval for reloading calendar data
107 var errorOccured = false;
108 var entryLists = null; // stores all fetched calendar entries until data is refreshed
109 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.
110 var use12hoursTimeFormat = false; // defines how time should be formated:
19:
00 or
07:
00 pm
111 var timeFormatSeparator =
":"; // format time
19:
00 or
19.00 depending on system setting
113 // vars for daylight saving time
114 var summertime = false; // true, if current date is in summer, false if in winter
115 var daylightSavingDates = new Object(); // caches calculated DST winter and summer time shift dates
117 // this is a list of data fields a calendar event can have
131 function isLeapYear( year ) {
132 if (( year %
4 ==
0 && year %
100 !=
0 ) || year %
400 ==
0 )
138 function calcLeapYear(year, days)
140 if (isLeapYear(year))
146 function subToSunday(myDate, year, days, prevMonthDays)
148 for (i = myDate.getDay(); i
> 0 ;i--)
150 days -= prevMonthDays;
151 days = isLeapYear(year) ? --days : days;
155 function isSummertime(curDate)
160 // if we already calculated DST summer and winter time dates for this year, use cached values
161 var dst = daylightSavingDates[curDate.getFullYear()];
163 var thisYearS = new Date(curDate.getFullYear(),
3,
0,
0,
0,
0 );
164 var thisYearW = new Date(curDate.getFullYear(),
10,
0,
0,
0,
0 );
165 var nextYearS = new Date(curDate.getFullYear() +
1,
3,
0,
0,
0,
0 );
166 var nextYearW = new Date(curDate.getFullYear() +
1,
10,
0,
0,
0,
0 );
168 thisYearSDays = nextYearSDays =
90;
169 thisYearWDays = nextYearWDays =
304;
171 thisYearSDays = calcLeapYear(curDate.getFullYear(), thisYearSDays);
172 thisYearWDays = calcLeapYear(curDate.getFullYear(), thisYearWDays);
173 nextYearSDays = calcLeapYear(curDate.getFullYear() +
1, nextYearSDays);
174 nextYearWDays = calcLeapYear(curDate.getFullYear() +
1, nextYearWDays);
176 thisYearSDays = subToSunday(thisYearS, curDate.getFullYear(), thisYearSDays,
59);
177 thisYearWDays = subToSunday(thisYearW, curDate.getFullYear(), thisYearWDays,
273);
178 nextYearSDays = subToSunday(nextYearS, curDate.getFullYear() +
1, nextYearSDays,
59);
179 nextYearWDays = subToSunday(nextYearW, curDate.getFullYear() +
1, nextYearWDays,
273);
182 Summer: new Date (curDate.getFullYear(),
03-
1, thisYearSDays,
2,
0,
0),
183 Winter: new Date (curDate.getFullYear(),
10-
1, thisYearWDays,
2,
0,
0),
185 daylightSavingDates[curDate.getFullYear()] = dst;
188 if (dst.Summer < curDate)
190 if (dst.Winter < curDate)
192 if (summer && !winter)
198 function error(message)
200 console.info('Error: ' + message);
201 document.getElementById(
"calendarList").innerHTML = 'Error: ' + message;
202 document.getElementById(
"fullscreenCalendarList").innerHTML = 'Error: ' + message;
204 document.onclick = null;
207 function areDatesEqual(date1, date2)
209 return (date1.getFullYear() == date2.getFullYear() &&
210 date1.getMonth() == date2.getMonth() &&
211 date1.getDate() == date2.getDate());
214 function isTomorrow(date)
216 // tommorow = now +
1 day
217 // ToDo: some days can be shorter as
24 hours(daylight saving change day)
218 return areDatesEqual(date, new Date (now.getTime() +
24*
60*
60*
1000));
221 function isToday(date)
223 return areDatesEqual(date, now);
226 function collectLocales()
228 var tmpyear =
2000 + panelNum;
231 if (months_translated.length
> 0)
233 for (month =
0; month <
12; month++) {
234 var startDate = new Date(tmpyear, month,
15);
236 var item = new Object();
237 item.Type =
"DayEvent";
238 item.StartTime = startDate;
239 item.Summary =
"__temp" + month;
241 var criteria = new Object();
242 criteria.Type =
"CalendarEntry";
243 criteria.Item = item;
246 var result = calendarService.IDataSource.Add(criteria);
247 if (result.ErrorCode)
248 throw(result.ErrorMessage);
250 error(
"collectLocales: " + e + ', line ' + e.line);
253 for (weekday =
0; weekday <
7; weekday++) {
254 var startDate = new Date(
2000,
0,
2 + weekday); // date that we know for sure is a sunday
256 var item = new Object();
257 item.Type =
"DayEvent";
258 item.StartTime = startDate;
259 item.Summary =
"__weekday_temp" + weekday;
261 var criteria = new Object();
262 criteria.Type =
"CalendarEntry";
263 criteria.Item = item;
266 var result = calendarService.IDataSource.Add(criteria);
267 if (result.ErrorCode)
268 throw(result.ErrorMessage);
270 error(
"collectLocales: " + e + ', line ' + e.line);
274 var startTime = new Date(tmpyear,
0,
1);
275 var endTime = new Date(tmpyear,
11,
31);
276 var listFiltering = {
277 Type:'CalendarEntry',
279 StartRange: startTime,
281 SearchText: '__temp',
285 var result = calendarService.IDataSource.GetList(listFiltering);
286 if (result.ErrorCode)
287 throw(result.ErrorMessage);
288 var list = result.ReturnValue;
290 error(
"collectLocales: " + e + ', line ' + e.line);
293 var ids = new Array();
299 while (list && (entry = list.getNext()) != undefined) {
300 dateArr = (entry.StartTime + '').replace(/,/g,'').replace(/\./g,':').replace(/ /g,' ').split(' ');
301 var day = dateArr[
1];
302 var month = dateArr[
2];
303 var year = dateArr[
3];
305 // make sure month is set properly
306 if (isNaN(parseInt(day))) {
310 } else if (isNaN(parseInt(year))) {
316 log(entry.StartTime + ' -
> ' + month + ' ' + counter);
317 ids[counter] = entry.id;
318 months_translated[month] = counter +
1;
322 error(
"collectLocales: " + e + ', line ' + e.line);
326 var startTime = new Date(
2000,
0,
2);
327 var endTime = new Date(
2000,
0,
9);
328 var listFiltering = {
329 Type:'CalendarEntry',
331 StartRange: startTime,
333 SearchText: '__weekday_temp',
337 var result = calendarService.IDataSource.GetList(listFiltering);
338 if (result.ErrorCode)
339 throw(result.ErrorMessage);
340 var weekdaylist = result.ReturnValue;
342 error(
"collectLocales: " + e + ', line ' + e.line);
350 while (weekdaylist && (entry = weekdaylist.getNext()) != undefined) {
351 detectTimeFormat(entry.StartTime + '');
352 curWeekday = (entry.StartTime + '').split(',')[
0];
353 log(entry.StartTime + ' -
> ' + curWeekday + ' ' + counter2);
354 ids[counter + counter2] = entry.id;
355 weekdays_translated[counter2] = curWeekday;
359 error(
"collectLocales: " + e + ', line ' + e.line);
364 var criteria = new Object();
365 criteria.Type =
"CalendarEntry";
370 var result = calendarService.IDataSource.Delete(criteria);
371 if (result.ErrorCode)
372 throw(result.ErrorMessage);
374 error('deleting temp calendar entries:' + e + ', line ' + e.line);
379 function stringEndsWith(str, suffix)
381 return str.indexOf(suffix, str.length - suffix.length) !== -
1;
384 // detects the system's current time format by parsing a native calendar timestamp (this is the only reliable formating across all devices and firmwares)
385 function detectTimeFormat(localeTimeString)
387 localeTimeString = localeTimeString.toLowerCase();
388 use12hoursTimeFormat = stringEndsWith(localeTimeString,
"am") || stringEndsWith(localeTimeString,
"pm");
389 timeFormatSeparator = localeTimeString.indexOf(
":") != -
1 ?
":" :
".";
392 function requestNotification()
394 var criteria = new Object();
395 criteria.Type =
"CalendarEntry";
396 criteria.Filter = new Object();
397 for(var i=
0; i < calendarList.length; i++) {
398 criteria.Filter.CalendarName = calendarList[i];
400 var notificationRequest = calendarService.IDataSource.RequestNotification(criteria, callback);
401 if (notificationRequest.ErrorCode)
402 error('requestNotification failed with error code ' + notificationRequest.ErrorCode);
403 notificationRequests.push(notificationRequest);
405 error(
"requestNotification: " + e + ', line ' + e.line);
409 var criteria2 = new Object();
410 criteria2.Type =
"CalendarEntry";
411 criteria2.Filter = new Object();
412 criteria2.Filter.LocalIdList = new Array();
413 criteria2.Filter.LocalIdList[
0] = settingsCalEntryId;
415 var notificationRequest = calendarService.IDataSource.RequestNotification(criteria2, settingsCallback);
416 if (notificationRequest.ErrorCode)
417 error('requestNotification failed with error code ' + notificationRequest.ErrorCode);
418 notificationRequests.push(notificationRequest);
420 error(
"requestNotification: " + e + ', line ' + e.line);
424 function cancelNotification()
426 for(var i=
0; i < notificationRequests.length; i++) {
428 var result = calendarService.IDataSource.Cancel(notificationRequests[i]);
429 if (result.ErrorCode)
430 error('cancelNotification failed with error code ' + result.ErrorCode);
432 error(
"cancelNotification: " + e + ', line ' + e.line);
437 function callback(transId, eventCode, result)
439 log(
"callback(): panelNum: " + panelNum +
" transId: " + transId +
" eventCode: " + eventCode +
" result.ErrorCode: " + result.ErrorCode);
440 lastReloadTime = null; // force calendar data reload on next update
444 function settingsCallback(transId, eventCode, result)
446 log(
"settingsCallback(): panelNum: " + panelNum +
" transId: " + transId +
" eventCode: " + eventCode +
" result.ErrorCode: " + result.ErrorCode);
450 function parseDate(dateString)
453 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:
454 Wednesday,
26 August,
2009 24:
00:
00
455 Wednesday,
26 August,
2009 12:
00:
00 am
456 Wednesday, August
26,
2009 12:
00:
00 am
457 Wednesday,
2009 August,
26 12:
00:
00 am
458 Wednesday,
2009 August,
28 8.00.00 pm
459 Wednesday,
2009 August,
28 08:
00:
00 PM
463 if (dateString ==
"" || dateString == null || dateString == undefined)
465 if (dateString instanceof Date) {
466 // we already have a date object, no need to parse string here
470 var dateArr = (dateString + '').replace(/,/g, '').replace(/\./g, ':').replace(/ /g, ' ').split(' ');
471 if (dateArr.length !=
5 && dateArr.length !=
6)
475 var weekDay = dateArr[
0];
476 var day = dateArr[
1];
477 var month = dateArr[
2];
478 var year = dateArr[
3];
479 // make sure month is set properly
480 if (isNaN(parseInt(day))) {
486 if (isNaN(parseInt(year))) {
491 // make sure day and year are set properly
492 if (Number(day)
> Number(year)) {
497 month = months_translated[month];
500 var timeArr = dateArr[
4].split(':');
501 if (timeArr.length !=
3)
503 var hours = Number(timeArr[
0]);
504 var minutes = Number(timeArr[
1]);
505 var seconds = Number(timeArr[
2]);
506 if (dateArr.length ==
6 && dateArr[
5].toLowerCase() == 'pm' && hours <
12)
508 if (dateArr.length ==
6 && dateArr[
5].toLowerCase() == 'am' && hours ==
12)
511 result = new Date(year, month -
1, day, hours, minutes, seconds);
514 // take care of daylight saving time
515 if (config['enableDaylightSaving'].Value) {
517 // determine if date is in summer or winter time
518 var dateSummerTime = isSummertime(result);
520 // work around bug in Nokias calendar api resulting in dates within a different DST to be off by
1 hour
521 if (summertime && !dateSummerTime) {
522 result = new Date(result.getTime() -
1000 *
60 *
60 * config['daylightSavingOffset'].Value); // -
1 hour
523 log('parseDate(): fixing time -
1h: ' + result);
525 else if (!summertime && dateSummerTime) {
526 result = new Date(result.getTime() +
1000 *
60 *
60 * config['daylightSavingOffset'].Value); // +
1 hour
527 log('parseDate(): fixing time +
1h: ' + result);
534 function getWeekdayLocalized(date) {
535 var localizedString = date.toLocaleDateString();
536 if (localizedString.indexOf(
",") == -
1) {
537 return weekdays_translated[date.getDay()];
539 return localizedString.split(',')[
0];
542 // 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"
543 function formatDate(date, format)
545 var day = date.getDate().toString();
546 var month = (date.getMonth() +
1).toString();
547 while (day.length <
2) { day = '
0' + day; }
548 while (month.length <
2) { month = '
0' + month; }
550 if (config['showTodayAsText'].Value && isToday(date))
551 return '
<span class=
"today">' + config['todayText'].Value + '
</span>';
552 if (config['showTodayAsText'].Value && isTomorrow(date))
553 return '
<span class=
"tomorrow">' + config['tomorrowText'].Value + '
</span>';
555 if (format instanceof Date) {
556 // we don't know how to format this
557 if (config['dateFormat'].Value == 'auto' || config['dateFormat'].Value == 'DDMM')
558 return day + config['dateSeparator'].Value + month;
560 return month + config['dateSeparator'].Value + day;
562 var dateArr = format.replace(/,/g,'').replace(/\./g,':').replace(/ /g,' ').split(' ');
563 if (dateArr.length !=
5 && dateArr.length !=
6) {
564 // we don't know how to format this
565 if (config['dateFormat'].Value == 'auto' || config['dateFormat'].Value == 'DDMM')
566 return day + config['dateSeparator'].Value + month;
568 return month + config['dateSeparator'].Value + day;
572 if (config['dateFormat'].Value == 'MMDD')
574 else if (config['dateFormat'].Value == 'DDMM')
577 // config['dateFormat'].Value == 'auto', try to detect system setting
579 var day_ = dateArr[
1];
580 var month_ = dateArr[
2];
581 var year_ = dateArr[
3];
582 // make sure month is set properly
583 if (isNaN(parseInt(day_))) {
588 } else if (isNaN(parseInt(year_))) {
594 // make sure day and year are set properly
595 if (Number(day_)
> Number(year_))
600 return day + config['dateSeparator'].Value + month;
602 return month + config['dateSeparator'].Value + day;
605 function formatTime(date)
607 // date is a Date() object
608 var hour = date.getHours();
609 var minute = date.getMinutes();
611 // don't use Date().toLocaleTimeString() as it is utterly broken on newer firmwares
612 if (use12hoursTimeFormat) {
623 minute =
"0" + minute;
624 time = hour + timeFormatSeparator + minute +
" " + ap;
630 minute =
"0" + minute;
631 time = hour + timeFormatSeparator + minute;
634 if (config['showNowAsText'].Value && date.getTime() == now.getTime())
635 time = '
<span class=
"now">' + config['nowText'].Value + '
</span>';
636 log(
"formatTime(): " + time +
", use12hoursTimeFormat=" + use12hoursTimeFormat +
", timeFormatSeparator=" + timeFormatSeparator +
", date.toLocateTimeString(): " + date.toLocaleTimeString());
640 function updateData()
647 // check if we got additional or less calendars since our last update
648 var newCalendarList = listCalendars();
649 if (newCalendarList == null) {
650 // Something went wrong fetching the calendars list.
651 // This usually happens when a backup is being made.
652 // Retry the next time updateData() is called by
653 // resetting errorOccured
654 log('updateData(): listCalendars() failed, trying again later...');
655 cacheEntriesHtml = ''; // make sure we replace the currently shown error message on the next update
656 errorOccured = false;
659 if (newCalendarList.length != calendarList.length) {
660 calendarList = newCalendarList;
661 updateCalendarColors();
662 cancelNotification();
663 requestNotification();
664 lastReloadTime = null; // force calendar data reload on this update
669 // only reload calendar data every
6 hours, visual updates occure more often
670 if (!lastReloadTime || now.getTime() - lastReloadTime.getTime()
> reloadInterval) {
671 log('updateData(): reloading calendar data');
673 // meetings have time
674 // 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
675 summertime = isSummertime(now); // cache summer time info for today
676 var meetingList = [];
677 for(var i=
0; i < calendarList.length; i++) {
678 // ignore excluded calendars
679 if (config['excludedCalendars'].Value.indexOf(calendarList[i]) != -
1)
681 var meetingListFiltering = {
682 Type:'CalendarEntry',
684 CalendarName: calendarList[i],
685 StartRange: (new Date(now.getFullYear(), now.getMonth(), now.getDate(),
0,
0,
0)),
686 EndRange: (new Date(now.getFullYear(), now.getMonth() + config['monthRange'].Value, now.getDate(),
0,
0,
0))
689 var meetingResult = calendarService.IDataSource.GetList(meetingListFiltering);
690 if (meetingResult.ErrorCode !=
0)
691 throw(
"Error fetching calendar data: " + meetingResult.ErrorCode + ': ' + meetingResult.ErrorMessage);
692 var list = meetingResult.ReturnValue;
693 meetingList = meetingList.concat(listToArray(list, calendarList[i]));
695 log(
"updateData(): meetingList.sort()");
696 meetingList.sort(sortCalendarEntries);
698 // todos don't, they start on
00:
00 hrs., but should be visible anyway
699 // this will generate a list of passed todos. We have to check if they have been marked as
"done" yet
700 if (config['includeTodos'].Value) {
701 var todayTodoList = [];
702 for(var i=
0; i < calendarList.length; i++) {
703 // ignore excluded calendars
704 if (config['excludedCalendars'].Value.indexOf(calendarList[i]) != -
1)
706 var todayTodoListFiltering = {
707 Type:'CalendarEntry',
709 CalendarName: calendarList[i],
711 StartRange: (new Date(now.getFullYear() -
1, now.getMonth(), now.getDate(),
0,
0,
0)),
712 EndRange: (new Date(now.getFullYear(), now.getMonth(), now.getDate(),
0,
0,
1))
715 var todayTodoResult = calendarService.IDataSource.GetList(todayTodoListFiltering);
716 var list = todayTodoResult.ReturnValue;
717 todayTodoList = todayTodoList.concat(listToArray(list, calendarList[i]));
719 log(
"updateData(): todayTodoList.sort()");
720 todayTodoList.sort(sortCalendarEntries);
721 entryLists = [todayTodoList, meetingList];
723 entryLists = [meetingList];
725 lastReloadTime = new Date();
727 error('loading Calendar items list:' + e + ', line ' + e.line);
737 var fontsize = 'normal';
738 var lineheight = 'normal';
740 fontsize = parseInt(
72 / config['eventsPerWidget'].Value) + 'px';
741 lineheight = parseInt(
82 / config['eventsPerWidget'].Value) + 'px';
743 if (config['eventsPerWidget'].Value ==
3) {
744 changeCssClass('.icon', 'width:
20px; height:
20px');
746 else if (config['eventsPerWidget'].Value ==
5) {
747 changeCssClass('.icon', 'width:
10px; height:
10px');
749 else if (config['eventsPerWidget'].Value ==
6) {
750 changeCssClass('.icon', 'width:
8px; height:
8px');
754 changeCssClass('.icon', config['cssStyle_icon'].Value);
755 var entriesHtml = '
<table style=
"font-size:' + fontsize + '; line-height:' + lineheight + ';">';
757 entriesHtml = '
<table width=
"307" height=
"82"><tr><td>' + entriesHtml; // this is needed to center the actual content vertically
761 max = (panelNum +
1) * config['eventsPerWidget'].Value;
763 max =
30; // we can display a lot more events in fullscreen mode
765 if (config['enableLogging'].Value) {
767 for (var i=
0; i < entryLists.length; i++) {
768 listinfo = listinfo +
" " + entryLists[i].length;
769 var entrieslist =
"";
770 for (var j=
0; j < entryLists[i].length; j++) {
771 entrieslist += entryLists[i][j].Summary +
", ";
773 log(
"updateData(): entrieslist: " + entrieslist);
775 log(
"updateData(): inner loop, " + entryLists.length +
" lists, [" + listinfo +
"] entries");
778 // the first outer loop iteration is for passed ToDos, the second loop is for all upcomming events (may also include ToDos)
779 for (var i=
0; counter < max && i < entryLists.length; i++) {
780 for (var j=
0; (counter < max) && (j < entryLists[i].length); j++) {
781 entry = entryLists[i][j];
784 // output event info for debugging
785 var entryInfo =
"event: ";
786 for(var k=
0; k < entryFields.length; ++k) {
787 if (entry[entryFields[k]] != undefined) {
788 entryInfo += entryFields[k] +
"=" + entry[entryFields[k]] +
",";
793 // we don't want ToDos when includeTodos == false or when they are completed
794 if (entry.Type == 'ToDo' && (entry.Status ==
"TodoCompleted" || !config['includeTodos'].Value)) {
795 log('skipping ' + entry.id );
800 // make sure that we don't include an event twice (useful for ToDos that might come up twice)
801 if (eventIds[entry.id] ==
1 && entry.Type == 'ToDo') {
802 log('skipped (already included) ' + entry.id);
806 eventIds[entry.id] =
1;
808 // summary can be undefined!
809 var Summary = ((entry.Summary == null) ? '' : entry.Summary);
810 if (entry.Location != '' && entry.Location != undefined && config['showLocation'].Value)
811 Summary += ', ' + entry.Location;
813 // fix by yves: determine start and end dates/times
814 entryStartTime = ((entry.InstanceStartTime == null) ? entry.StartTime : entry.InstanceStartTime);
815 entryEndTime = ((entry.InstanceEndTime == null) ? entry.EndTime : entry.InstanceEndTime);
817 // there can be ToDos that have no date at all!
818 if (entry.Type == 'ToDo' && entry.EndTime == null)
819 entryDate =
""; // this will cause parseDate(entryDate) to return null;
821 entryDate = ((entry.Type == 'ToDo') ? entryEndTime : entryStartTime); // ToDo's use their EndTime, the rest use StartTime
823 // Convert date/time string to Date object
824 var date = parseDate(entryDate);
825 log('date: ' + date);
826 var endDate = ((entryEndTime == null) ? null : parseDate(entryEndTime));
827 log('endDate: ' + endDate);
829 // check if Meeting is actually a DayEvent. Bug introduced by
"Anna" updates to various Symbian^
3 devices.
830 // Note that this workaround is not
100% save! It might missinterpret some meetings as dayevents of starting and ending on
00:
00
831 if (entry.Type == 'Meeting' && date.getHours() ==
0 && date.getMinutes() ==
0 &&
832 endDate != null && endDate.getHours() ==
0 && endDate.getMinutes() ==
0) {
833 log('fixing event type: changed from
"Meeting" to
"DayEvent".');
834 entry.Type = 'DayEvent';
837 // check if meeting event has already passed
838 if (entry.Type == 'Meeting') {
839 var compareTime = ((endDate == null) ? date.getTime() : endDate.getTime());
840 if (now.getTime()
> compareTime) {
841 log('skipping Meeting (already passed) ' + entry.id);
843 eventIds[entry.id] =
0;
848 // check if anniversary passed (not sure why they are in the list, the query was only for today - nokia?)
849 if (entry.Type == 'Anniversary') {
850 var tmp = new Date(now.getFullYear(), now.getMonth(), now.getDate(),
0,
0,
0);
851 if (date.getTime() < tmp.getTime()) {
852 log('skipping Anniversary (already passed) ' + entry.id);
854 eventIds[entry.id] =
0;
859 // fix DayEvents end time. End times are off by
1 Second. It's possible that the event has already passed
860 if (entry.Type == 'DayEvent' && endDate != null) {
861 endDate.setMinutes(endDate.getMinutes() -
1);
862 log('fixing DayEvent endDate: ' + endDate);
863 if (now.getTime()
> endDate.getTime()) {
864 log('event already passed ' + entry.id);
866 eventIds[entry.id] =
0;
871 // check if the event is currently taking place
872 if (entryStartTime != null && entryEndTime != null && date != null && endDate != null) {
873 // check if we are between start and endtime
874 if ((date.getTime() < now.getTime()) && (now.getTime() < endDate.getTime())) {
875 date = now; // change appointment date/time to now
876 log('event is currently taking place: ' + date);
880 // skip events for the first panel in case this is the second one and we're not in fullscreen mode
881 if (mode ==
0 && panelNum
> 0 && counter < panelNum * config['eventsPerWidget'].Value +
1) {
882 log('skipping (already in first widget) ' + entry.id);
886 // mark overdue todos
888 if (entry.Type == 'ToDo' && date != null) {
889 var tmp1 = new Date(date.getFullYear(), date.getMonth(), date.getDate(),
0,
0,
0);
890 var tmp2 = new Date(now.getFullYear(), now.getMonth(), now.getDate(),
0,
0,
0);
891 if (tmp1.getTime() < tmp2.getTime()) {
896 // generate html output
897 entriesHtml += '
<tr>';
898 if (config['showCalendarIndicator'].Value && calendarList.length - config['excludedCalendars'].Value.length
> 1) {
899 entriesHtml += '
<td><span class=
"calendar' + calendarColors[entry.CalendarName] + '"> </span></td>';
901 if (config['showIcons'].Value)
902 entriesHtml += '
<td><img class=
"icon" align=
"top" src=
"' + entry.Type + '.png" /></td>';
904 entriesHtml += '
<td style=
"padding:0px;"></td>';
906 // some languages have very strange locale date formats, can't parse all those. Also some todos don't have dates at all.
907 entriesHtml += '
<td colspan=
"4"><span class=
"date">' + entryDate + '
</span> ';
909 var weekDay = getWeekdayLocalized(date).substr(
0,config['weekDayLength'].Value);
910 log('date.toLocaleDateString(): ' + date.toLocaleDateString());
911 log('weekDay: ' + weekDay);
912 var time = formatTime(date);
913 var dateStr = formatDate(date, entryDate);
914 if (entry.Type == 'ToDo' && overdue && config['markOverdueTodos'].Value) {
915 dateStr = '
<span class=
"overdue">' + config['overdueText'].Value + '
</span>';
916 entriesHtml += '
<td colspan=
"4" width=
"1px"><span class=
"date">' + dateStr + '
</span> ';
917 } else if (entry.Type == 'ToDo' || entry.Type == 'Anniversary' || entry.Type == 'DayEvent' || entry.Type == 'Reminder') {
918 if ((isToday(date) || isTomorrow(date)) && config['showTodayAsText'].Value) // show weekday if the date string is not text. looks odd otherwise
919 entriesHtml += '
<td colspan=
"4" width=
"1px"><span class=
"date">' + dateStr + '
</span> ';
921 entriesHtml += '
<td class=
"weekDay" width=
"1px">' + weekDay + '
</td><td width=
"1px" class=
"date">' + dateStr + '
</td><td colspan=
"2">';
922 } else if (entry.Type == 'Meeting') {
923 if (config['showCombinedDateTime'].Value) {
925 entriesHtml += '
<td width=
"1px" colspan=
"4"><span class=
"today">' + time + '
</span> ';
926 else if (isTomorrow(date))
927 entriesHtml += '
<td width=
"1px" colspan=
"4"><span class=
"tomorrow">' + dateStr + '
</span> <span class=
"time">' + time + '
</span> ';
929 entriesHtml += '
<td width=
"1px" class=
"weekDay">' + weekDay + '
</td><td width=
"1px" class=
"date">' + dateStr + '
</td><td colspan=
"2">';
931 if ((isToday(date) || isTomorrow(date)) && config['showTodayAsText'].Value)
932 entriesHtml += '
<td colspan=
"4" width=
"1px"><span class=
"today">' + dateStr + '
</span> <span class=
"time">' + time + '
</span> ';
934 entriesHtml += '
<td width=
"1px" class=
"weekDay">' + weekDay + '
</td><td width=
"1px" class=
"date">' + dateStr + '
</td><td width=
"1px" class=
"time">' + time + '
</td><td>';
938 entriesHtml += '
<span class=
"description">' + Summary + '
</span></td></tr>';
941 entriesHtml += '
</table>';
943 entriesHtml = entriesHtml + '
</td></tr></table>';
944 if (config['showNothingText'].Value && entriesHtml == '
<table></table>') {
945 var text = config['nothingText'].Value.replace(/%d/, config['monthRange'].Value);
946 entriesHtml = '
<div style=
"width:295px; height:75px; text-align:center; line-height:75px; overflow:visible;">' + text + '
</div>';
948 log(
"output: " + entriesHtml);
949 if (cacheEntriesHtml != entriesHtml) {
951 document.getElementById('calendarList').innerHTML = entriesHtml;
953 document.getElementById('fullscreenCalendarList').innerHTML = entriesHtml;
954 cacheEntriesHtml = entriesHtml;
957 lastUpdateTime = new Date();
959 error('displaying list:' + e + ', line ' + e.line);
964 // called by handleOnShow() and onResize events
965 function updateScreen()
967 log('updateScreen(): mode=' + mode + ', window.innerHeight=' + window.innerHeight);
969 // check if opening fullscreen
971 // Note: according to Nokia's documentation, an innerHeight of
>91 is an indicator for fullscreen view.
972 // However a bug in E6's firmware causes different window widths and heights (disabled compatibility scaling).
973 // So far, values of
104 and
115 for window.innerHeight were reported, we use a safty margin here and check
975 if( window.innerHeight
> 150 && mode ==
0) {
977 cacheEntriesHtml = '';
978 document.getElementById('body').style.backgroundImage =
"";
981 else if (window.innerHeight <=
150 && mode !=
0) {
983 cacheEntriesHtml = '';
988 updateHomescreen(); // check for screen rotation
993 function handleOnShow()
997 var time = new Date();
998 if (time.getTime() - lastUpdateTime.getTime()
> config['updateDataInterval'].Value *
60 *
1000) {
999 log('updateScreen(): force updateData() because last update was too long ago (' + (time.getTime() - lastUpdateTime.getTime()) /
1000 + 's)');
1002 setUpdateTimer(); // reinitialize update timer
1006 function launchCalendar()
1009 widget.openApplication(config['calendarApp'].Value,
"");
1010 if (config['hideWidgetOnCalendarOpen'].Value)
1013 error('starting Calendar App');
1020 log('New widget instance starting up...');
1023 // call calendar service
1024 if (device !=
"undefined")
1025 calendarService = device.getServiceObject(
"Service.Calendar",
"IDataSource");
1027 throw('device object does not exist');
1029 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>');
1033 calendarList = listCalendars();
1035 updateCalendarColors();
1038 requestNotification();
1039 document.getElementById(
"settingsTitle").innerHTML = getLocalizedText('menu.settings');
1041 if (window.innerHeight
> 91) {
1042 mode =
0; // we're starting fullscreen, we set mode to homescreen in order to let updateScreen() do all the work for us
1047 log(
"init(): updateScreen()");
1049 if (config['useBackgroundImage'].Value)
1050 // check for screen rotation every
1 secs
1051 screenRotationTimer = window.setInterval('checkOrientation()',
1000 *
1);
1053 // call updateScreen() when widget changes from background to forground
1054 window.widget.onshow = handleOnShow;
1056 log(
"init(): finished...");
1058 statupSuccessful = true;
1061 function checkOrientation()
1065 updateHomescreen(); // check for screen rotation
1068 function setUpdateTimer()
1070 updateTimer = window.setInterval('updateTimerCallback()',
1000 *
60 * config['updateDataInterval'].Value);
1073 function clearUpdateTimer()
1075 window.clearInterval(updateTimer);
1078 function updateTimerCallback()
1080 log(
"updateTimerCallback()");
1084 function createMenu()
1086 window.menu.setLeftSoftkeyLabel(
"",null);
1087 window.menu.setRightSoftkeyLabel(
"",null);
1089 var menuSettings = new MenuItem(getLocalizedText('menu.settings'), id++);
1090 var menuCallApp = new MenuItem(getLocalizedText('menu.openCalendarApp'), id++);
1091 var menuHelp = new MenuItem(getLocalizedText('menu.help'), id++);
1092 var menuUpdate = new MenuItem(getLocalizedText('menu.update'), id++);
1093 var menuAbout = new MenuItem(getLocalizedText('menu.about'), id++);
1094 menuSettings.onSelect = showSettings;
1095 menuAbout.onSelect = showAbout;
1096 menuCallApp.onSelect = launchCalendar;
1097 menuUpdate.onSelect = showUpdate;
1098 menuHelp.onSelect = showHelp;
1099 window.menu.clear();
1100 window.menu.append(menuCallApp);
1101 window.menu.append(menuSettings);
1102 window.menu.append(menuHelp);
1103 window.menu.append(menuUpdate);
1104 window.menu.append(menuAbout);
1107 function showSettings()
1111 document.getElementById(
"settingsView").style.display =
"block";
1112 document.onclick = null;
1114 window.menu.setLeftSoftkeyLabel(getLocalizedText('settings.save'), function()
1116 for (var key in config) {
1117 if (config[key].Type == 'String')
1118 config[key].Value = document.forms[
0].elements[
"settings." + key].value;
1119 else if (config[key].Type == 'Int') {
1120 config[key].Value = parseInt(document.forms[
0].elements[
"settings." + key].value);
1121 if (config[key].Value <
0 || isNaN(config[key].Value))
1122 config[key].Value = config[key].Default;
1124 else if (config[key].Type == 'Bool')
1125 config[key].Value = document.forms[
0].elements[
"settings." + key].checked;
1126 else if (config[key].Type == 'UID') {
1127 config[key].Value = parseInt(document.forms[
0].elements[
"settings." + key].value);
1128 if (isNaN(config[key].Value))
1129 config[key].Value = config[key].Default;
1131 else if (config[key].Type == 'Enum') {
1132 config[key].Value = document.forms[
0].elements[
"settings." + key].value;
1133 if (config[key].ValidValues.indexOf(config[key].Value) == -
1)
1134 config[key].Value = config[key].Default;
1136 else if (config[key].Type == 'Array') {
1137 if (key == 'excludedCalendars') {
1138 config[key].Value = new Array();
1139 for(var i=
0; i < calendarList.length; i++) {
1140 var element = document.forms[
0].elements[
"settings." + key +
"." + calendarList[i]];
1141 if (element != null && element.checked == false)
1142 config[key].Value.push(calendarList[i]);
1155 window.menu.setRightSoftkeyLabel(getLocalizedText('settings.cancel'), function()
1161 var settingsHtml = '
<form>';
1162 for (var key in config) {
1163 if (config[key].Type == 'String') {
1165 if (key.substring(
0,
9) ==
"cssStyle_")
1166 prefix = getLocalizedText('settings.cssStyle_prefix');
1167 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 />';
1169 else if (config[key].Type == 'Int')
1170 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 />';
1171 else if (config[key].Type == 'Bool')
1172 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 />';
1173 else if (config[key].Type == 'UID')
1174 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 />';
1175 else if (config[key].Type == 'Enum') {
1176 settingsHtml += '
<table><tr><td>' + getLocalizedText('settings.name.' + key) + '
<br /><select name=
"settings.' + key + '" size=
"1">';
1177 for(var i =
0; i < config[key].ValidValues.length; i++)
1178 settingsHtml += '
<option value=
"' + config[key].ValidValues[i] + '"' + (config[key].Value == config[key].ValidValues[i] ? '
selected=
"selected"' : '') + '
>' + getLocalizedText('settings.validValues.' + key + '.' + config[key].ValidValues[i]) + '
</option>';
1179 settingsHtml += '
</select></div></td>' + printHintBox(getLocalizedText('settings.info.' + key)) + '
<hr />';
1181 else if (config[key].Type == 'Array') {
1182 settingsHtml += '
<table><tr><td>' + getLocalizedText('settings.name.' + key) + '
<br />';
1183 if (key == 'excludedCalendars') {
1184 for(var i=
0; i < calendarList.length; i++) {
1185 var checked = '
checked=
"checked"';
1186 if (config[key].Value.indexOf(calendarList[i]) != -
1)
1188 settingsHtml += '
<input name=
"settings.' + key + '.' + calendarList[i] + '" type=
"checkbox" value=
"' + calendarList[i] + '" ' + checked + '
/> ' + calendarList[i] + '
<br />';
1191 settingsHtml += '
</td>' + printHintBox(getLocalizedText('settings.info.' + key)) + '
<hr />';
1194 settingsHtml += '
<input name=
"reset" type=
"button" value=
"' + getLocalizedText('settings.restoreDefaults') + '" onclick=
"javascript:restoreDefaultSettings();showSettings();" />';
1195 settingsHtml += '
</form>';
1196 document.getElementById(
"settingsList").innerHTML = settingsHtml;
1199 function changeCssClass(classname, properties)
1201 for(var i =
0; i < document.styleSheets[
0]['cssRules'].length; i++)
1203 if (document.styleSheets[
0]['cssRules'][i].selectorText == classname) {
1204 document.styleSheets[
0].deleteRule(i);
1205 document.styleSheets[
0].insertRule(classname + ' { ' + properties + ' }', document.styleSheets[
0]['cssRules'].length);
1211 function updateCssClasses()
1213 for(var key in config) {
1214 changeCssClass(getLocalizedText('settings.name.' + key), config[key].Value);
1218 function getSettingsCalEntryId()
1220 if (settingsCalEntryId == null) {
1221 // check if entry already exists
1222 var listFiltering = {
1223 Type:'CalendarEntry',
1225 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!
1226 EndRange: new Date(
2000,
0,
2),
1227 SearchText: 'ComingNext Settings|',
1233 result = calendarService.IDataSource.GetList(listFiltering);
1234 if (result.ErrorCode)
1235 throw(result.ErrorMessage);
1238 error(
"getSettingsCalEntryId: GetList() failed: " + e + ', line ' + e.line);
1241 var list = result.ReturnValue;
1242 var entry = list.getNext();
1243 if (entry != undefined) {
1244 settingsCalEntryId = entry.LocalId;
1245 log(
"settingsCalEntryId=" + settingsCalEntryId);
1247 else { // create settings item
1248 var item = new Object();
1249 item.Type =
"DayEvent";
1250 item.StartTime = new Date(
2000,
0,
1);
1251 item.Summary =
"ComingNext Settings|";
1253 var criteria = new Object();
1254 criteria.Type =
"CalendarEntry";
1255 criteria.Item = item;
1258 var result = calendarService.IDataSource.Add(criteria);
1259 if (result.ErrorCode)
1260 throw(result.ErrorMessage);
1262 error(
"getSettingsCalEntryId: " + e + ', line ' + e.line);
1265 getSettingsCalEntryId();
1270 function restoreDefaultSettings()
1272 for (var key in config)
1273 config[key].Value = config[key].Default;
1276 function loadSettings()
1278 getSettingsCalEntryId();
1279 var listFiltering = {
1280 Type:'CalendarEntry',
1282 LocalId: settingsCalEntryId
1287 result = calendarService.IDataSource.GetList(listFiltering);
1288 if (result.ErrorCode)
1289 throw(result.ErrorMessage);
1292 error(
"loadSettings: GetList() failed: " + e + ', line ' + e.line);
1295 var entry = result.ReturnValue.getNext();
1296 if (entry != undefined) {
1297 log(
"Loading Settings...");
1298 // only reload settings if they chanced since the last reload
1299 if (settingsCache != entry.Summary)
1301 restoreDefaultSettings();
1302 var stringlist = entry.Summary.split(
"|");
1303 // skip the first two entries, those contain header and version info
1304 for(var i =
2; i < stringlist.length -
1; i++) {
1305 var pair = stringlist[i].split('=');
1307 var value = pair[
1];
1308 if (key == null || value == null || config[key] == null) {
1309 log('Warning: unknown or invalid setting: ' + stringlist[i]);
1312 log('stringlist[' + i + ']: ' + key + '=\'' + value + '\'');
1313 if (config[key].Type == 'Int') {
1314 config[key].Value = Number(value);
1315 if (isNaN(config[key].Value))
1316 config[key].Value = config[key].Default;
1318 else if (config[key].Type == 'String')
1319 config[key].Value = value;
1320 else if (config[key].Type == 'Bool')
1321 config[key].Value = (value == 'true')
1322 else if (config[key].Type == 'Enum')
1323 config[key].Value = value;
1324 else if (config[key].Type == 'UID') {
1325 config[key].Value = Number(value);
1326 if (isNaN(config[key].Value))
1327 config[key].Value = config[key].Default;
1329 else if (config[key].Type == 'Array') {
1330 config[key].Value = value.split(
"^");
1331 if (config[key].Value.length ==
1 && config[key].Value[
0] ==
"") {
1332 config[key].Value = [];
1336 settingsCache = entry.Summary;
1340 log(
"Settings already cached and did not change");
1344 error(
"Failed to load settings, calendar entry could not be found");
1348 function saveSettings()
1350 getSettingsCalEntryId();
1351 var item = new Object();
1352 item.Type =
"DayEvent";
1353 item.StartTime = new Date(
2000,
0,
1);
1354 item.LocalId = settingsCalEntryId;
1355 item.Summary =
"ComingNext Settings|" + version +
"|";
1357 for (var key in config) {
1358 if (config[key].Type == 'Int')
1359 item.Summary += key +
"=" + config[key].Value.toString() +
"|";
1360 else if (config[key].Type == 'String')
1361 item.Summary += key +
"=" + config[key].Value +
"|";
1362 else if (config[key].Type == 'Bool')
1363 item.Summary += key +
"=" + (config[key].Value ? 'true' : 'false') +
"|";
1364 else if (config[key].Type == 'Enum')
1365 item.Summary += key +
"=" + config[key].Value +
"|";
1366 else if (config[key].Type == 'UID')
1367 item.Summary += key +
"=" + config[key].Value.toString() +
"|";
1368 else if (config[key].Type == 'Array')
1369 item.Summary += key +
"=" + config[key].Value.join(
"^") +
"|";
1371 settingsCache = item.Summary;
1373 var criteria = new Object();
1374 criteria.Type =
"CalendarEntry";
1375 criteria.Item = item;
1377 log(
"Saving settings to calendar entry: " + item.Summary);
1379 var result = calendarService.IDataSource.Add(criteria);
1380 if (result.ErrorCode)
1381 throw(result.ErrorMessage);
1383 error(
"saveSettings: " + e + ', line ' + e.line);
1386 lastReloadTime = null; // force calendar data reload on next update
1391 function toggleVisibility(elementId)
1393 if (document.getElementById(elementId).style.display ==
"none")
1394 document.getElementById(elementId).style.display =
"block";
1396 document.getElementById(elementId).style.display =
"none";
1400 function printHintBox(text)
1403 return '
<td width=
"1%" align=
"right" onclick=
"javascript:toggleVisibility(\'info' + uniqueId + '\')">' + getLocalizedText('settings.help') + '
</td></tr></table>'+
1404 '
<div class=
"settingsInfo" id=
"info' + uniqueId + '" style=
"display:none">' + text + '
</div>';
1407 function showAbout()
1411 document.getElementById(
"aboutView").style.display =
"block";
1412 document.onclick = null;
1414 window.menu.setLeftSoftkeyLabel(
" ", function(){});
1415 window.menu.setRightSoftkeyLabel(getLocalizedText('softkey.back'), function()
1421 //document.getElementById(
"aboutView").innerHTML = 'aboutView';
1422 document.getElementById(
"name").innerHTML =
"Coming Next " + version;
1425 function showHelp() {
1426 widget.openURL('http://comingnext.sf.net/help');
1429 function updateFullscreen()
1433 function showFullscreen()
1435 log(
"showFullscreen()");
1437 document.getElementById(
"fullscreenView").style.display =
"block";
1438 document.getElementById('body').className =
"backgroundFullscreen";
1440 document.onclick = launchCalendar;
1445 function getBackgroundImage()
1450 if (config['backgroundImageLocation'].Value == config['backgroundImageLocation'].ValidValues[
0]) // internal
1451 bgImage = 'background_' + orientation + '.png';
1453 bgImage = 'C:/Data/background_' + panelNum + '_' + orientation + '.png';
1457 function updateHomescreen()
1459 if (config['useBackgroundImage'].Value) {
1460 // check if we have a completely unknown screen resolution
1461 var screenHeight = screen.height;
1462 var screenWidth = screen.width;
1463 if (screenHeight !=
640 && screenHeight !=
480 && screenHeight !=
360)
1464 screenHeight =
360; // we can only assume we're in portrait mode, so we set the screen dims as needed for the following code
1465 if (screenWidth !=
640 && screenWidth !=
480 && screenWidth !=
360)
1466 screenWidth =
640; // we can only assume we're in portrait mode, so we set the screen dims as needed for the following code
1468 // check for screen rotation
1469 if (orientation != 'portrait' && ((screenWidth ==
360 && screenHeight ==
640) || (screenWidth ==
640 && screenHeight ==
480))) {
1470 window.widget.prepareForTransition(
"fade");
1471 orientation = 'portrait';
1472 document.getElementById('body').style.backgroundImage = 'url(' + getBackgroundImage() + ')';
1473 document.getElementById('body').style.backgroundColor = 'none';
1474 window.widget.performTransition();
1475 } else if (orientation != 'landscape' && ((screenWidth ==
640 && screenHeight ==
360) || (screenWidth ==
480 && screenHeight ==
640))) {
1476 window.widget.prepareForTransition(
"fade");
1477 orientation = 'landscape';
1478 document.getElementById('body').style.backgroundImage = 'url(' + getBackgroundImage() + ')';
1479 document.getElementById('body').style.backgroundColor = 'none';
1480 window.widget.performTransition();
1482 else if (document.getElementById('body').style.backgroundImage ==
"")
1484 document.getElementById('body').style.backgroundImage = 'url(' + getBackgroundImage() + ')';
1489 function showHomescreen()
1491 log(
"showHomescreen()");
1493 document.getElementById(
"homescreenView").style.display =
"block";
1494 document.getElementById('body').className =
"background";
1495 document.onclick = null;
1499 function getLocalizedText(p_Txt)
1501 if (localizedText[p_Txt])
1502 return localizedText[p_Txt];
1504 return 'ERROR: missing translation for ' + p_Txt;
1507 function showUpdate()
1511 document.getElementById(
"updateView").style.display =
"block";
1512 document.onclick = null;
1514 window.menu.setLeftSoftkeyLabel(getLocalizedText('update.checknow'), function(){
1517 window.menu.setRightSoftkeyLabel(getLocalizedText('softkey.back'), function()
1523 document.getElementById(
"currentVersion").innerHTML = getLocalizedText(
"update.current") + version;
1527 function checkForUpdate()
1529 // asynch XHR to server url
1530 reqV = new XMLHttpRequest();
1531 reqV.onreadystatechange = checkForUpdateCallback;
1532 document.getElementById(
"updateDiv").innerHTML = getLocalizedText(
"update.checking");
1533 reqV.open(
"GET", versionURL, true);
1537 function checkForUpdateCallback()
1539 if (reqV.readyState ==
4) {
1540 if (reqV.status ==
200) {
1541 var resultXml = reqV.responseText;
1543 var div = document.getElementById(
"tmp");
1544 div.innerHTML = resultXml;
1545 var newVersion = div.getElementsByTagName('version')[
0].innerHTML;
1546 var newVersionURL = div.getElementsByTagName('url')[
0].innerHTML;
1548 if (version != newVersion) {
1549 document.getElementById(
"updateDiv").innerHTML = getLocalizedText(
"update.download").replace(/%
1/, newVersion).replace(/%
2/, newVersionURL);
1552 document.getElementById(
"updateDiv").innerHTML = getLocalizedText(
"update.nonewversion");
1557 document.getElementById(
"updateDiv").innerHTML = getLocalizedText(
"update.error") + reqV.status +
" " + reqV.responseText;
1562 function hideViews()
1564 document.getElementById(
"homescreenView").style.display =
"none";
1565 document.getElementById(
"fullscreenView").style.display =
"none";
1566 document.getElementById(
"aboutView").style.display =
"none";
1567 document.getElementById(
"settingsView").style.display =
"none";
1568 document.getElementById(
"updateView").style.display =
"none";
1571 function listCalendars()
1581 DefaultCalendar: false
1585 var calendarsResult = calendarService.IDataSource.GetList(criteria);
1586 if (calendarsResult.ErrorCode !=
0)
1587 throw(
"Error fetching list of calendars: " + calendarsResult.ErrorCode + ': ' + calendarsResult.ErrorMessage);
1588 var calendarListIterator = calendarsResult.ReturnValue;
1593 while (( item = calendarListIterator.getNext()) != undefined ) {
1594 calendars[count++] = item;
1596 log(
"Available Calendars: " + calendars.join(
", "));
1599 error('listing calendars:' + e + ', line ' + e.line);
1604 // Copies all objects and their properties to an array. Data is copied so nothing gets lost when the reference is removed
1605 // 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
1606 function listToArray(list, calendarName)
1608 var array = new Array();
1611 while (( item = list.getNext()) != undefined ) {
1612 var itemCopy = new Object();
1613 for(var i=
0; i < entryFields.length; i++) {
1614 itemCopy[entryFields[i]] = item[entryFields[i]];
1616 // for some reason, the CalendarName property is never correctly queried, so we assign it manually here
1617 if (!itemCopy['CalendarName']) {
1618 itemCopy['CalendarName'] = calendarName;
1620 array.push(itemCopy);
1621 txt += array[array.length -
1].Summary +
", ";
1623 log(
"listToArray(): " + txt);
1627 function sortCalendarEntries(a, b)
1630 log(
"sortCalendarEntries(" + a.Summary +
"," + b.Summary +
")");
1632 if (a.InstanceStartTime != null) {
1633 atime = a.InstanceStartTime;
1635 else if (a.StartTime != null) {
1636 atime = a.StartTime;
1638 else if (a.InstanceEndTime != null) {
1639 atime = a.InstanceEndTime;
1641 else if (a.EndTime != null) {
1645 if (b.InstanceStartTime != null) {
1646 btime = b.InstanceStartTime;
1648 else if (b.StartTime != null) {
1649 btime = b.StartTime;
1651 else if (b.InstanceEndTime != null) {
1652 btime = b.InstanceEndTime;
1654 else if (b.EndTime != null) {
1658 if (atime && btime) {
1660 atime = parseDate(atime);
1661 btime = parseDate(btime);
1663 // sort by date & time
1664 if (atime < btime) {
1667 else if (atime
> btime) {
1671 else if (a.Type != b.Type) {
1672 if (a.Type < b.Type) {
1675 else if (a.Type
> b.Type) {
1679 // sort by description
1680 else if (a.Summary && b.Summary && a.Summary != b.Summary) {
1681 if (a.Summary < b.Summary) {
1684 else if (a.Summary
> b.Summary) {
1689 // NOTE: events my have no date information at all. In that case, we list events without date first
1690 else if (atime && !btime) {
1693 else if (!atime && btime) {
1696 else if (!atime && !btime) {
1698 if (a.Type != b.Type) {
1699 if (a.Type < b.Type) {
1702 else if (a.Type
> b.Type) {
1706 // sort by description
1707 else if (a.Summary && b.Summary && a.Summary != b.Summary) {
1708 if (a.Summary < b.Summary) {
1711 else if (a.Summary
> b.Summary) {
1720 function updateCalendarColors()
1723 calendarColors = [];
1724 if (calendarList.length
> maxColors) {
1725 log(
"updateCalendarColors(): Warning: more calendars than available indicator colors");
1727 for(var i=
0; i < calendarList.length; i++) {
1728 calendarColors[calendarList[i]] = (i % maxColors) +
1;
1732 function log(message)
1734 if (config['enableLogging'].Value) {
1735 console.info(message);
1741 <style type=
"text/css">
1743 table { margin:
0px; padding:
0px; border-spacing:
0px; border-collapse: collapse; }
1744 td { padding:
0px
5px
0px
0px; white-space:nowrap; overflow:hidden; margin:
0px; }
1745 hr { color:#ffffff; background-color:#ffffff; height:
1px; text-align:left; border-style:none; }
1746 .settingsInfo { display:none; font-style:italic; }
1747 .title { font-weight:bold; font-size:
14pt; }
1748 .textInput { width:
90%; }
1749 .credits { margin-left:
40px; text-indent: -
20px; margin-bottom:
0px; }
1750 #homescreenView { width:
312px; height:
82px; overflow:hidden; }
1751 #calendarList { position:absolute; left:
5px; top:
0px; width:
307px; height:
82px; overflow:hidden; }
1752 #name { text-align:center; }
1753 #appicon { display: block; margin-left: auto; margin-right: auto; margin-top:
10px; }
1754 #smallappicon { width:
22px; height:
22px; margin-right:
10px; float:left; }
1759 <body onload=
"javascript:setTimeout('init()', 10)" onresize=
"javascript:updateScreen()" id=
"body" class=
"background">
1760 <div id=
"homescreenView">
1761 <div id=
"calendarList">loading...
</div>
1763 <div id=
"fullscreenView" style=
"display:none;">
1764 <img src=
"Icon.png" id=
"smallappicon">
1765 <h1 class=
"title">Coming Next
</h1>
1767 <div id=
"fullscreenCalendarList">loading...
</div>
1769 <div id=
"settingsView" style=
"display:none">
1770 <img src=
"Icon.png" id=
"smallappicon">
1771 <h1 id=
"settingsTitle" class=
"title">Settings
</h1>
1773 <div id=
"settingsList"></div>
1775 <div id=
"aboutView" style=
"display:none">
1776 <img src=
"Icon.png" id=
"appicon">
1777 <h1 id=
"name">Coming Next
</h1>
1779 <p>Created by Dr. Cochambre and Michael Prager.
</p>
1780 <p>Contributions:
</p>
1781 <p class=
"credits">Paul Moore (bug fixes, new features and code cleanup)
</p>
1782 <p class=
"credits">Manfred Hanselmann (DST support)
</p>
1783 <p class=
"credits">Christophe Milsent (translation support & French translation)
</p>
1784 <p class=
"credits">Flavio Nathan (Portuguese-Brazilian translation)
</p>
1785 <p class=
"credits">Tokeda (Russian translation)
</p>
1786 <p class=
"credits">Marcella Ferrari (Italian translation)
</p>
1787 <p class=
"credits">Venos (Italian translation)
</p>
1788 <p class=
"credits">Francisco Rodero (Catalan translation)
</p>
1789 <p class=
"credits">zbigzbig20 (Polish translation)
</p>
1790 <p class=
"credits">Streamkeskus (Finnish translation)
</p>
1791 <p class=
"credits">renek (Czech translation)
</p>
1792 <p>This software is open source and licensed under the GPLv3.
</p>
1793 <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>
1796 <div id=
"updateView" style=
"display:none">
1797 <img src=
"Icon.png" id=
"smallappicon">
1798 <h1 class=
"title">Check for update
</h1>
1800 <div id=
"currentVersion">Coming Next ??
</div>
1801 <div id=
"updateDiv"></div>
1802 <div id=
"tmp" style=
"display:none;"></div>