1 <?xml version=
"1.0" encoding=
"UTF-8"?>
2 <!DOCTYPE html PUBLIC
"-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
3 <html xmlns=
"http://www.w3.org/1999/xhtml">
6 <title>Coming Next
</title>
8 <style type=
"text/css">
9 /* The following classes can be modified by widget settings */
10 .background { color:#ffffff; background-color:#
000000 }
11 .backgroundFullscreen { color:#ffffff; background-color:#
000000 }
14 .today { color:#ff0000 }
15 .tomorrow { color:#
0000ff }
17 .now { color:#ff00ff }
19 .icon { width:
15px; height:
15px }
20 .overdue { color:#ffff00 }
21 .calendar1 { background-color:#
0757cf }
22 .calendar2 { background-color:#
579f37 }
23 .calendar3 { background-color:#ff9f07 }
24 .calendar4 { background-color:#af8fef }
25 .calendar5 { background-color:#
57afbf }
26 .calendar6 { background-color:#
9fdf57 }
29 <script type=
"text/javascript" src=
"localizedTextStrings.js" charset=
"utf-8"></script>
30 <script type=
"text/javascript" src=
"../debug.js" charset=
"utf-8"></script>
31 <script type=
"text/javascript">
32 // valid types for the config object are 'Int', 'Bool', 'String', 'Enum', 'UID', 'Array'
34 fontsize: { Type: 'Enum', Default: 'auto', Value: 'auto', ValidValues: ['auto', '
8', '
9', '
10', '
11', '
12', '
13', '
14', '
15', '
16', '
17', '
18', '
19', '
20', '
21', '
22', '
23', '
24', '
25', '
26', '
27', '
28'],},
35 eventsPerWidget: { Type: 'Int', Default:
4, Value:
4,},
36 monthRange: { Type: 'Int', Default:
2, Value:
2,},
37 maxNumberOfEventsOnFullscreen: { Type: 'Int', Default:
30, Value:
30,},
38 includeTodos: { Type: 'Bool', Default: true, Value: true,},
39 useBackgroundImage: { Type: 'Bool', Default: true, Value: true,},
40 backgroundImageLocation: { Type: 'Enum', Default: 'internal', Value: 'internal', ValidValues: ['internal', 'external']},
41 showCombinedDateTime: { Type: 'Bool', Default: false, Value: false,},
42 showLocation: { Type: 'Bool', Default: true, Value: true,},
43 showIcons: { Type: 'Bool', Default: true, Value: true,},
44 showTodayAsText: { Type: 'Bool', Default: true, Value: true,},
45 todayText: { Type: 'String', Default: getLocalizedText('settings.default.todayText'), Value: getLocalizedText('settings.default.todayText'),},
46 tomorrowText: { Type: 'String', Default: getLocalizedText('settings.default.tomorrowText'), Value: getLocalizedText('settings.default.tomorrowText'),},
47 showNowAsText: { Type: 'Bool', Default: true, Value: true,},
48 nowText: { Type: 'String', Default: getLocalizedText('settings.default.nowText'), Value: getLocalizedText('settings.default.nowText'),},
49 markOverdueTodos: { Type: 'Bool', Default: true, Value: true,},
50 overdueText: {Type: 'String', Default: getLocalizedText('settings.default.overdueText'), Value: getLocalizedText('settings.default.overdueText'),},
51 dateSeparator: { Type: 'String', Default: getLocalizedText('settings.default.dateSeparator'), Value: getLocalizedText('settings.default.dateSeparator'),},
52 dateFormat: { Type: 'Enum', Default: 'auto', Value: 'auto', ValidValues: ['auto', 'DDMM', 'MMDD'],},
53 weekDayLength: { Type: 'Int', Default:
2, Value:
2,},
54 updateDataInterval: { Type: 'Int', Default:
5, Value:
5,},
55 calendarApp: { Type: 'UID', Default:
0x10005901, Value:
0x10005901,},
56 showNothingText: { Type: 'Bool', Default: true, Value: true,},
57 nothingText: { Type: 'String', Default: getLocalizedText('settings.default.nothingText'), Value: getLocalizedText('settings.default.nothingText'),},
58 enableDaylightSaving: { Type: 'Bool', Default: true, Value: true,},
59 daylightSavingOffset: { Type: 'Int', Default:
1, Value:
1,},
60 hideWidgetOnCalendarOpen: { Type: 'Bool', Default: false, Value: false,},
61 showCalendarIndicator: { Type: 'Bool', Default: true, Value: true,},
62 excludedCalendars: { Type: 'Array', Default: [], Value: [],},
63 enableLogging: { Type: 'Bool', Default: false, Value: false,},
64 anonymizeLogging: { Type: 'Bool', Default: false, Value: false,},
65 cssStyle_background: { Type: 'String', Default: 'color:#ffffff; background-color:#
000000', Value: 'color:#ffffff; background-color:#
000000',},
66 cssStyle_backgroundFullscreen: { Type: 'String', Default: 'color:#ffffff; background-color:#
000000', Value: 'color:#ffffff; background-color:#
000000',},
67 cssStyle_weekDay: { Type: 'String', Default: '', Value: '',},
68 cssStyle_date: { Type: 'String', Default: '', Value: '',},
69 cssStyle_today: { Type: 'String', Default: 'color:#ff0000', Value: 'color:#ff0000',},
70 cssStyle_tomorrow: { Type: 'String', Default: 'color:#
0000ff', Value: 'color:#
0000ff',},
71 cssStyle_time: { Type: 'String', Default: '', Value: '',},
72 cssStyle_now: { Type: 'String', Default: 'color:#ff00ff', Value: 'color:#ff00ff',},
73 cssStyle_description: { Type: 'String', Default: '', Value: '',},
74 cssStyle_icon: { Type: 'String', Default: '', Value: '',},
75 cssStyle_overdue: { Type: 'String', Default: 'color:#ffff00', Value: 'color:#ffff00',},
76 cssStyle_calendar1: { Type: 'String', Default: 'background-color:#
0757cf', Value: 'background-color:#
0757cf',},
77 cssStyle_calendar2: { Type: 'String', Default: 'background-color:#
579f37', Value: 'background-color:#
579f37',},
78 cssStyle_calendar3: { Type: 'String', Default: 'background-color:#ff9f07', Value: 'background-color:#ff9f07',},
79 cssStyle_calendar4: { Type: 'String', Default: 'background-color:#af8fef', Value: 'background-color:#af8fef',},
80 cssStyle_calendar5: { Type: 'String', Default: 'background-color:#
57afbf', Value: 'background-color:#
57afbf',},
81 cssStyle_calendar6: { Type: 'String', Default: 'background-color:#
9fdf57', Value: 'background-color:#
9fdf57',},
86 //-------------------------------------------------------
87 // Nothing of interest from here on...
88 //-------------------------------------------------------
89 var panelNum =
0; // use
1 for second panel
91 var versionURL =
"http://comingnext.sourceforge.net/version.xml";
92 var calendarService = null;
93 var cacheEntriesHtml = [];
94 var months_translated = [];
95 var weekdays_translated = [];
98 var mode =
0; //
0 = homescreen,
1 = fullscreen,
2 = settings,
3 = about,
4 = check for update
100 var settingsCalEntryId = null;
101 var settingsCache = null;
102 var notificationRequests = new Array();
103 var calendarList = [];
104 var calendarColors = [];
105 var updateTimer = null;
106 var screenRotationTimer = null;
107 var lastUpdateTime = now; // last time we updated the display
108 var lastReloadTime = null; // last time we fetched calendar data
109 var reloadInterval =
6 *
60 *
60 *
1000; // =
6 hours; time interval for reloading calendar data
110 var errorOccured = false;
111 var entryLists = null; // stores all fetched calendar entries until data is refreshed
112 var statupSuccessful = false; // indicates if everything started up wihtout errors. If we detect an error after that, it might just be a temporary problem e.g. by a backup process.
113 var use12hoursTimeFormat = false; // defines how time should be formated:
19:
00 or
07:
00 pm
114 var timeFormatSeparator =
":"; // format time
19:
00 or
19.00 depending on system setting
115 var defaultFontSize = null; // default browser font size will be set by init
116 var displayOffset =
0;
118 // vars for daylight saving time
119 var summertime = false; // true, if current date is in summer, false if in winter
120 var daylightSavingDates = new Object(); // caches calculated DST winter and summer time shift dates
122 // this is a list of data fields a calendar event can have
136 function isLeapYear( year ) {
137 if (( year %
4 ==
0 && year %
100 !=
0 ) || year %
400 ==
0 )
143 function calcLeapYear(year, days)
145 if (isLeapYear(year))
151 function subToSunday(myDate, year, days, prevMonthDays)
153 for (i = myDate.getDay(); i
> 0 ;i--)
155 days -= prevMonthDays;
156 days = isLeapYear(year) ? --days : days;
160 function isSummertime(curDate)
165 // if we already calculated DST summer and winter time dates for this year, use cached values
166 var dst = daylightSavingDates[curDate.getFullYear()];
168 var thisYearS = new Date(curDate.getFullYear(),
3,
0,
0,
0,
0 );
169 var thisYearW = new Date(curDate.getFullYear(),
10,
0,
0,
0,
0 );
170 var nextYearS = new Date(curDate.getFullYear() +
1,
3,
0,
0,
0,
0 );
171 var nextYearW = new Date(curDate.getFullYear() +
1,
10,
0,
0,
0,
0 );
173 thisYearSDays = nextYearSDays =
90;
174 thisYearWDays = nextYearWDays =
304;
176 thisYearSDays = calcLeapYear(curDate.getFullYear(), thisYearSDays);
177 thisYearWDays = calcLeapYear(curDate.getFullYear(), thisYearWDays);
178 nextYearSDays = calcLeapYear(curDate.getFullYear() +
1, nextYearSDays);
179 nextYearWDays = calcLeapYear(curDate.getFullYear() +
1, nextYearWDays);
181 thisYearSDays = subToSunday(thisYearS, curDate.getFullYear(), thisYearSDays,
59);
182 thisYearWDays = subToSunday(thisYearW, curDate.getFullYear(), thisYearWDays,
273);
183 nextYearSDays = subToSunday(nextYearS, curDate.getFullYear() +
1, nextYearSDays,
59);
184 nextYearWDays = subToSunday(nextYearW, curDate.getFullYear() +
1, nextYearWDays,
273);
187 Summer: new Date (curDate.getFullYear(),
03-
1, thisYearSDays,
2,
0,
0),
188 Winter: new Date (curDate.getFullYear(),
10-
1, thisYearWDays,
2,
0,
0),
190 daylightSavingDates[curDate.getFullYear()] = dst;
193 if (dst.Summer < curDate)
195 if (dst.Winter < curDate)
197 if (summer && !winter)
203 function error(message)
205 console.info('Error: ' + message);
206 document.getElementById(
"calendarList").innerHTML = 'Error: ' + message;
207 document.getElementById(
"fullscreenCalendarList").innerHTML = 'Error: ' + message;
209 document.onclick = null;
212 function areDatesEqual(date1, date2)
214 return (date1.getFullYear() == date2.getFullYear() &&
215 date1.getMonth() == date2.getMonth() &&
216 date1.getDate() == date2.getDate());
219 function isTomorrow(date)
221 // tommorow = now +
1 day
222 // ToDo: some days can be shorter as
24 hours(daylight saving change day)
223 return areDatesEqual(date, new Date (now.getTime() +
24*
60*
60*
1000));
226 function isToday(date)
228 return areDatesEqual(date, now);
231 function collectLocales()
233 var tmpyear =
2000 + panelNum;
236 if (months_translated.length
> 0)
238 for (month =
0; month <
12; month++) {
239 var startDate = new Date(tmpyear, month,
15);
241 var item = new Object();
242 item.Type =
"DayEvent";
243 item.StartTime = startDate;
244 item.Summary =
"__temp" + month;
246 var criteria = new Object();
247 criteria.Type =
"CalendarEntry";
248 criteria.Item = item;
251 var result = calendarService.IDataSource.Add(criteria);
252 if (result.ErrorCode)
253 throw(result.ErrorMessage);
255 error(
"collectLocales: " + e + ', line ' + e.line);
258 for (weekday =
0; weekday <
7; weekday++) {
259 var startDate = new Date(
2000,
0,
2 + weekday); // date that we know for sure is a sunday
261 var item = new Object();
262 item.Type =
"DayEvent";
263 item.StartTime = startDate;
264 item.Summary =
"__weekday_temp" + weekday;
266 var criteria = new Object();
267 criteria.Type =
"CalendarEntry";
268 criteria.Item = item;
271 var result = calendarService.IDataSource.Add(criteria);
272 if (result.ErrorCode)
273 throw(result.ErrorMessage);
275 error(
"collectLocales: " + e + ', line ' + e.line);
279 var startTime = new Date(tmpyear,
0,
1);
280 var endTime = new Date(tmpyear,
11,
31);
281 var listFiltering = {
282 Type:'CalendarEntry',
284 StartRange: startTime,
286 SearchText: '__temp',
290 var result = calendarService.IDataSource.GetList(listFiltering);
291 if (result.ErrorCode)
292 throw(result.ErrorMessage);
293 var list = result.ReturnValue;
295 error(
"collectLocales: " + e + ', line ' + e.line);
298 var ids = new Array();
304 while (list && (entry = list.getNext()) != undefined) {
305 dateArr = (entry.StartTime + '').replace(/,/g,'').replace(/\./g,':').replace(/ /g,' ').split(' ');
306 var day = dateArr[
1];
307 var month = dateArr[
2];
308 var year = dateArr[
3];
310 // make sure month is set properly
311 if (isNaN(parseInt(day))) {
315 } else if (isNaN(parseInt(year))) {
321 log(entry.StartTime + ' -
> ' + month + ' ' + counter);
322 ids[counter] = entry.id;
323 months_translated[month] = counter +
1;
327 error(
"collectLocales: " + e + ', line ' + e.line);
331 var startTime = new Date(
2000,
0,
2);
332 var endTime = new Date(
2000,
0,
9);
333 var listFiltering = {
334 Type:'CalendarEntry',
336 StartRange: startTime,
338 SearchText: '__weekday_temp',
342 var result = calendarService.IDataSource.GetList(listFiltering);
343 if (result.ErrorCode)
344 throw(result.ErrorMessage);
345 var weekdaylist = result.ReturnValue;
347 error(
"collectLocales: " + e + ', line ' + e.line);
355 while (weekdaylist && (entry = weekdaylist.getNext()) != undefined) {
356 detectTimeFormat(entry.StartTime + '');
357 curWeekday = (entry.StartTime + '').split(',')[
0];
358 log(entry.StartTime + ' -
> ' + curWeekday + ' ' + counter2);
359 ids[counter + counter2] = entry.id;
360 weekdays_translated[counter2] = curWeekday;
364 error(
"collectLocales: " + e + ', line ' + e.line);
369 var criteria = new Object();
370 criteria.Type =
"CalendarEntry";
375 var result = calendarService.IDataSource.Delete(criteria);
376 if (result.ErrorCode)
377 throw(result.ErrorMessage);
379 error('deleting temp calendar entries:' + e + ', line ' + e.line);
384 function stringEndsWith(str, suffix)
386 return str.indexOf(suffix, str.length - suffix.length) !== -
1;
389 // detects the system's current time format by parsing a native calendar timestamp (this is the only reliable formating across all devices and firmwares)
390 function detectTimeFormat(localeTimeString)
392 localeTimeString = localeTimeString.toLowerCase();
393 use12hoursTimeFormat = stringEndsWith(localeTimeString,
"am") || stringEndsWith(localeTimeString,
"pm");
394 timeFormatSeparator = localeTimeString.indexOf(
":") != -
1 ?
":" :
".";
397 function requestNotification()
399 var criteria = new Object();
400 criteria.Type =
"CalendarEntry";
401 criteria.Filter = new Object();
402 for(var i=
0; i < calendarList.length; i++) {
403 criteria.Filter.CalendarName = calendarList[i];
405 var notificationRequest = calendarService.IDataSource.RequestNotification(criteria, callback);
406 if (notificationRequest.ErrorCode)
407 error('requestNotification failed with error code ' + notificationRequest.ErrorCode);
408 notificationRequests.push(notificationRequest);
410 error(
"requestNotification: " + e + ', line ' + e.line);
414 var criteria2 = new Object();
415 criteria2.Type =
"CalendarEntry";
416 criteria2.Filter = new Object();
417 criteria2.Filter.LocalIdList = new Array();
418 criteria2.Filter.LocalIdList[
0] = settingsCalEntryId;
420 var notificationRequest = calendarService.IDataSource.RequestNotification(criteria2, settingsCallback);
421 if (notificationRequest.ErrorCode)
422 error('requestNotification failed with error code ' + notificationRequest.ErrorCode);
423 notificationRequests.push(notificationRequest);
425 error(
"requestNotification: " + e + ', line ' + e.line);
429 function cancelNotification()
431 for(var i=
0; i < notificationRequests.length; i++) {
433 var result = calendarService.IDataSource.Cancel(notificationRequests[i]);
434 if (result.ErrorCode)
435 error('cancelNotification failed with error code ' + result.ErrorCode);
437 error(
"cancelNotification: " + e + ', line ' + e.line);
442 function callback(transId, eventCode, result)
444 log(
"callback(): panelNum: " + panelNum +
" transId: " + transId +
" eventCode: " + eventCode +
" result.ErrorCode: " + result.ErrorCode);
445 lastReloadTime = null; // force calendar data reload on next update
449 function settingsCallback(transId, eventCode, result)
451 log(
"settingsCallback(): panelNum: " + panelNum +
" transId: " + transId +
" eventCode: " + eventCode +
" result.ErrorCode: " + result.ErrorCode);
455 function parseDate(dateString)
458 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:
459 Wednesday,
26 August,
2009 24:
00:
00
460 Wednesday,
26 August,
2009 12:
00:
00 am
461 Wednesday, August
26,
2009 12:
00:
00 am
462 Wednesday,
2009 August,
26 12:
00:
00 am
463 Wednesday,
2009 August,
28 8.00.00 pm
464 Wednesday,
2009 August,
28 08:
00:
00 PM
468 if (dateString ==
"" || dateString == null || dateString == undefined)
470 if (dateString instanceof Date) {
471 // we already have a date object, no need to parse string here
475 var dateArr = (dateString + '').replace(/,/g, '').replace(/\./g, ':').replace(/ /g, ' ').split(' ');
476 if (dateArr.length !=
5 && dateArr.length !=
6)
480 var weekDay = dateArr[
0];
481 var day = dateArr[
1];
482 var month = dateArr[
2];
483 var year = dateArr[
3];
484 // make sure month is set properly
485 if (isNaN(parseInt(day))) {
491 if (isNaN(parseInt(year))) {
496 // make sure day and year are set properly
497 if (Number(day)
> Number(year)) {
502 month = months_translated[month];
505 var timeArr = dateArr[
4].split(':');
506 if (timeArr.length !=
3)
508 var hours = Number(timeArr[
0]);
509 var minutes = Number(timeArr[
1]);
510 var seconds = Number(timeArr[
2]);
511 if (dateArr.length ==
6 && dateArr[
5].toLowerCase() == 'pm' && hours <
12)
513 if (dateArr.length ==
6 && dateArr[
5].toLowerCase() == 'am' && hours ==
12)
516 result = new Date(year, month -
1, day, hours, minutes, seconds);
519 // take care of daylight saving time
520 if (config['enableDaylightSaving'].Value) {
522 // determine if date is in summer or winter time
523 var dateSummerTime = isSummertime(result);
525 // work around bug in Nokias calendar api resulting in dates within a different DST to be off by
1 hour
526 if (summertime && !dateSummerTime) {
527 result = new Date(result.getTime() -
1000 *
60 *
60 * config['daylightSavingOffset'].Value); // -
1 hour
528 log('parseDate(): fixing time -
1h: ' + result);
530 else if (!summertime && dateSummerTime) {
531 result = new Date(result.getTime() +
1000 *
60 *
60 * config['daylightSavingOffset'].Value); // +
1 hour
532 log('parseDate(): fixing time +
1h: ' + result);
535 if (displayOffset !=
0) {
536 result = new Date(result.getTime() + displayOffset);
542 function getWeekdayLocalized(date) {
543 var localizedString = date.toLocaleDateString();
544 if (localizedString.indexOf(
",") == -
1) {
545 return weekdays_translated[date.getDay()];
547 return localizedString.split(',')[
0];
550 // 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"
551 function formatDate(date, format)
553 var day = date.getDate().toString();
554 var month = (date.getMonth() +
1).toString();
555 while (day.length <
2) { day = '
0' + day; }
556 while (month.length <
2) { month = '
0' + month; }
558 if (config['showTodayAsText'].Value && isToday(date))
559 return '
<span class=
"today">' + config['todayText'].Value + '
</span>';
560 if (config['showTodayAsText'].Value && isTomorrow(date))
561 return '
<span class=
"tomorrow">' + config['tomorrowText'].Value + '
</span>';
563 if (format instanceof Date) {
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;
570 var dateArr = format.replace(/,/g,'').replace(/\./g,':').replace(/ /g,' ').split(' ');
571 if (dateArr.length !=
5 && dateArr.length !=
6) {
572 // we don't know how to format this
573 if (config['dateFormat'].Value == 'auto' || config['dateFormat'].Value == 'DDMM')
574 return day + config['dateSeparator'].Value + month;
576 return month + config['dateSeparator'].Value + day;
580 if (config['dateFormat'].Value == 'MMDD')
582 else if (config['dateFormat'].Value == 'DDMM')
585 // config['dateFormat'].Value == 'auto', try to detect system setting
587 var day_ = dateArr[
1];
588 var month_ = dateArr[
2];
589 var year_ = dateArr[
3];
590 // make sure month is set properly
591 if (isNaN(parseInt(day_))) {
596 } else if (isNaN(parseInt(year_))) {
602 // make sure day and year are set properly
603 if (Number(day_)
> Number(year_))
608 return day + config['dateSeparator'].Value + month;
610 return month + config['dateSeparator'].Value + day;
613 function formatTime(date)
615 // date is a Date() object
616 var hour = date.getHours();
617 var minute = date.getMinutes();
619 // don't use Date().toLocaleTimeString() as it is utterly broken on newer firmwares
620 if (use12hoursTimeFormat) {
631 minute =
"0" + minute;
632 time = hour + timeFormatSeparator + minute +
" " + ap;
638 minute =
"0" + minute;
639 time = hour + timeFormatSeparator + minute;
642 if (config['showNowAsText'].Value && date.getTime() == now.getTime())
643 time = '
<span class=
"now">' + config['nowText'].Value + '
</span>';
644 log(
"formatTime(): " + time +
", use12hoursTimeFormat=" + use12hoursTimeFormat +
", timeFormatSeparator=" + timeFormatSeparator +
", date.toLocateTimeString(): " + date.toLocaleTimeString());
648 function updateData()
655 // check if we got additional or less calendars since our last update
656 var newCalendarList = listCalendars();
657 if (newCalendarList == null) {
658 // Something went wrong fetching the calendars list.
659 // This usually happens when a backup is being made.
660 // Retry the next time updateData() is called by
661 // resetting errorOccured
662 log('updateData(): listCalendars() failed, trying again later...');
663 cacheEntriesHtml = ''; // make sure we replace the currently shown error message on the next update
664 errorOccured = false;
667 if (newCalendarList.length != calendarList.length) {
668 calendarList = newCalendarList;
669 updateCalendarColors();
670 cancelNotification();
671 requestNotification();
672 lastReloadTime = null; // force calendar data reload on this update
677 // only reload calendar data every
6 hours, visual updates occure more often
678 if (!lastReloadTime || now.getTime() - lastReloadTime.getTime()
> reloadInterval) {
679 log('updateData(): reloading calendar data');
681 // meetings have time
682 // 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
683 summertime = isSummertime(now); // cache summer time info for today
684 var meetingList = [];
685 for(var i=
0; i < calendarList.length; i++) {
686 // ignore excluded calendars
687 if (config['excludedCalendars'].Value.indexOf(calendarList[i]) != -
1)
689 var meetingListFiltering = {
690 Type:'CalendarEntry',
692 CalendarName: calendarList[i],
693 StartRange: (new Date(now.getFullYear(), now.getMonth(), now.getDate(),
0,
0,
0)),
694 EndRange: (new Date(now.getFullYear(), now.getMonth() + config['monthRange'].Value, now.getDate(),
0,
0,
0))
697 var meetingResult = calendarService.IDataSource.GetList(meetingListFiltering);
698 if (meetingResult.ErrorCode !=
0)
699 throw(
"Error fetching calendar data: " + meetingResult.ErrorCode + ': ' + meetingResult.ErrorMessage);
700 var list = meetingResult.ReturnValue;
701 meetingList = meetingList.concat(listToArray(list, calendarList[i]));
703 log(
"updateData(): meetingList.sort()");
704 meetingList.sort(sortCalendarEntries);
706 // todos don't, they start on
00:
00 hrs., but should be visible anyway
707 // this will generate a list of passed todos. We have to check if they have been marked as
"done" yet
708 if (config['includeTodos'].Value) {
709 var todayTodoList = [];
710 for(var i=
0; i < calendarList.length; i++) {
711 // ignore excluded calendars
712 if (config['excludedCalendars'].Value.indexOf(calendarList[i]) != -
1)
714 var todayTodoListFiltering = {
715 Type:'CalendarEntry',
717 CalendarName: calendarList[i],
719 StartRange: (new Date(now.getFullYear() -
1, now.getMonth(), now.getDate(),
0,
0,
0)),
720 EndRange: (new Date(now.getFullYear(), now.getMonth(), now.getDate(),
0,
0,
1))
723 var todayTodoResult = calendarService.IDataSource.GetList(todayTodoListFiltering);
724 var list = todayTodoResult.ReturnValue;
725 todayTodoList = todayTodoList.concat(listToArray(list, calendarList[i]));
727 log(
"updateData(): todayTodoList.sort()");
728 todayTodoList.sort(sortCalendarEntries);
729 entryLists = [todayTodoList, meetingList];
731 entryLists = [meetingList];
733 lastReloadTime = new Date();
735 error('loading Calendar items list:' + e + ', line ' + e.line);
745 var fontsize = getDefaultFontSize()[
1] + 'px';
746 var lineheight = fontsize;
748 if (config['fontsize'].Value == config['fontsize'].ValidValues[
0]) {
749 fontsize = parseInt(
72 / config['eventsPerWidget'].Value) + 'px';
750 lineheight = parseInt(
72 / config['eventsPerWidget'].Value) + 'px';
753 if (config['fontsize'].Value != config['fontsize'].ValidValues[
0]) {
754 fontsize = config['fontsize'].Value + 'px';
755 lineheight = fontsize;
757 changeCssClass('.icon', config['cssStyle_icon'].Value + '; width:' + fontsize + '; height:' + fontsize + ';');
758 var entriesHtml = '
<table style=
"font-size:' + fontsize + '; line-height:' + lineheight + ';">';
760 entriesHtml = '
<table width=
"307" height=
"82"><tr><td>' + entriesHtml; // this is needed to center the actual content vertically
764 max = (panelNum +
1) * config['eventsPerWidget'].Value;
766 max = config[
"maxNumberOfEventsOnFullscreen"].Value; // we can display a lot more events in fullscreen mode
768 if (config['enableLogging'].Value) {
770 for (var i=
0; i < entryLists.length; i++) {
771 listinfo = listinfo +
" " + entryLists[i].length;
772 var entrieslist =
"";
773 for (var j=
0; j < entryLists[i].length; j++) {
774 entrieslist += entryLists[i][j].Summary +
", ";
776 log(
"updateData(): entrieslist: " + entrieslist);
778 log(
"updateData(): inner loop, " + entryLists.length +
" lists, [" + listinfo +
"] entries");
781 // the first outer loop iteration is for passed ToDos, the second loop is for all upcomming events (may also include ToDos)
782 for (var i=
0; counter < max && i < entryLists.length; i++) {
783 for (var j=
0; (counter < max) && (j < entryLists[i].length); j++) {
784 entry = entryLists[i][j];
787 // output event info for debugging
788 var entryInfo =
"event: ";
789 for(var k=
0; k < entryFields.length; ++k) {
790 if (entry[entryFields[k]] != undefined) {
791 entryInfo += entryFields[k] +
"=" + entry[entryFields[k]] +
",";
796 // we don't want ToDos when includeTodos == false or when they are completed
797 if (entry.Type == 'ToDo' && (entry.Status ==
"TodoCompleted" || !config['includeTodos'].Value)) {
798 log('skipping ' + entry.id );
803 // make sure that we don't include an event twice (useful for ToDos that might come up twice)
804 if (eventIds[entry.id] ==
1 && entry.Type == 'ToDo') {
805 log('skipped (already included) ' + entry.id);
809 eventIds[entry.id] =
1;
811 // summary can be undefined!
812 var Summary = ((entry.Summary == null) ? '' : entry.Summary);
813 if (entry.Location != '' && entry.Location != undefined && config['showLocation'].Value)
814 Summary += ', ' + entry.Location;
816 // fix by yves: determine start and end dates/times
817 entryStartTime = ((entry.InstanceStartTime == null) ? entry.StartTime : entry.InstanceStartTime);
818 entryEndTime = ((entry.InstanceEndTime == null) ? entry.EndTime : entry.InstanceEndTime);
820 // there can be ToDos that have no date at all!
821 if (entry.Type == 'ToDo' && entry.EndTime == null)
822 entryDate =
""; // this will cause parseDate(entryDate) to return null;
824 entryDate = ((entry.Type == 'ToDo') ? entryEndTime : entryStartTime); // ToDo's use their EndTime, the rest use StartTime
826 // Convert date/time string to Date object
827 var date = parseDate(entryDate);
828 log('date: ' + date);
829 var endDate = ((entryEndTime == null) ? null : parseDate(entryEndTime));
830 log('endDate: ' + endDate);
832 // check if Meeting is actually a DayEvent. Bug introduced by
"Anna" updates to various Symbian^
3 devices.
833 // Note that this workaround is not
100% save! It might missinterpret some meetings as dayevents of starting and ending on
00:
00
834 if (entry.Type == 'Meeting' && date.getHours() ==
0 && date.getMinutes() ==
0 &&
835 endDate != null && endDate.getHours() ==
0 && endDate.getMinutes() ==
0) {
836 log('fixing event type: changed from
"Meeting" to
"DayEvent".');
837 entry.Type = 'DayEvent';
840 // check if meeting event has already passed
841 if (entry.Type == 'Meeting') {
842 var compareTime = ((endDate == null) ? date.getTime() : endDate.getTime());
843 if (now.getTime()
> compareTime) {
844 log('skipping Meeting (already passed) ' + entry.id);
846 eventIds[entry.id] =
0;
851 // check if anniversary passed (not sure why they are in the list, the query was only for today - nokia?)
852 if (entry.Type == 'Anniversary') {
853 var tmp = new Date(now.getFullYear(), now.getMonth(), now.getDate(),
0,
0,
0);
854 if (date.getTime() < tmp.getTime()) {
855 log('skipping Anniversary (already passed) ' + entry.id);
857 eventIds[entry.id] =
0;
862 // fix DayEvents end time. End times are off by
1 Second. It's possible that the event has already passed
863 if (entry.Type == 'DayEvent' && endDate != null) {
864 endDate.setMinutes(endDate.getMinutes() -
1);
865 log('fixing DayEvent endDate: ' + endDate);
866 if (now.getTime()
> endDate.getTime()) {
867 log('event already passed ' + entry.id);
869 eventIds[entry.id] =
0;
874 // check if the event is currently taking place
875 if (entryStartTime != null && entryEndTime != null && date != null && endDate != null) {
876 // check if we are between start and endtime
877 if ((date.getTime() < now.getTime()) && (now.getTime() < endDate.getTime())) {
878 date = now; // change appointment date/time to now
879 log('event is currently taking place: ' + date);
883 // skip events for the first panel in case this is the second one and we're not in fullscreen mode
884 if (mode ==
0 && panelNum
> 0 && counter < panelNum * config['eventsPerWidget'].Value +
1) {
885 log('skipping (already in first widget) ' + entry.id);
889 // mark overdue todos
891 if (entry.Type == 'ToDo' && date != null) {
892 var tmp1 = new Date(date.getFullYear(), date.getMonth(), date.getDate(),
0,
0,
0);
893 var tmp2 = new Date(now.getFullYear(), now.getMonth(), now.getDate(),
0,
0,
0);
894 if (tmp1.getTime() < tmp2.getTime()) {
899 // generate html output
900 entriesHtml += '
<tr>';
901 if (config['showCalendarIndicator'].Value && calendarList.length - config['excludedCalendars'].Value.length
> 1) {
902 entriesHtml += '
<td><div class=
"calendar' + calendarColors[entry.CalendarName] + '" style=
"height:' + (lineheight.split("px
")[0] - 1) + 'px; width:4px;"></div></td>';
904 if (config['showIcons'].Value)
905 entriesHtml += '
<td><img class=
"icon" align=
"top" src=
"' + entry.Type + '.png" /></td>';
907 entriesHtml += '
<td style=
"padding:0px;"></td>';
909 // some languages have very strange locale date formats, can't parse all those. Also some todos don't have dates at all.
910 entriesHtml += '
<td colspan=
"4"><span class=
"date">' + entryDate + '
</span> ';
912 var weekDay = getWeekdayLocalized(date).substr(
0,config['weekDayLength'].Value);
913 log('date.toLocaleDateString(): ' + date.toLocaleDateString());
914 log('weekDay: ' + weekDay);
915 var time = formatTime(date);
916 var dateStr = formatDate(date, entryDate);
917 if (entry.Type == 'ToDo' && overdue && config['markOverdueTodos'].Value) {
918 dateStr = '
<span class=
"overdue">' + config['overdueText'].Value + '
</span>';
919 entriesHtml += '
<td colspan=
"4" width=
"1px"><span class=
"date">' + dateStr + '
</span> ';
920 } else if (entry.Type == 'ToDo' || entry.Type == 'Anniversary' || entry.Type == 'DayEvent' || entry.Type == 'Reminder') {
921 if ((isToday(date) || isTomorrow(date)) && config['showTodayAsText'].Value) // show weekday if the date string is not text. looks odd otherwise
922 entriesHtml += '
<td colspan=
"4" width=
"1px"><span class=
"date">' + dateStr + '
</span> ';
924 entriesHtml += '
<td class=
"weekDay" width=
"1px">' + weekDay + '
</td><td width=
"1px" class=
"date">' + dateStr + '
</td><td colspan=
"2">';
925 } else if (entry.Type == 'Meeting') {
926 if (config['showCombinedDateTime'].Value) {
928 entriesHtml += '
<td width=
"1px" colspan=
"4"><span class=
"today">' + time + '
</span> ';
929 else if (isTomorrow(date))
930 entriesHtml += '
<td width=
"1px" colspan=
"4"><span class=
"tomorrow">' + dateStr + '
</span> <span class=
"time">' + time + '
</span> ';
932 entriesHtml += '
<td width=
"1px" class=
"weekDay">' + weekDay + '
</td><td width=
"1px" class=
"date">' + dateStr + '
</td><td colspan=
"2">';
934 if ((isToday(date) || isTomorrow(date)) && config['showTodayAsText'].Value)
935 entriesHtml += '
<td colspan=
"4" width=
"1px"><span class=
"today">' + dateStr + '
</span> <span class=
"time">' + time + '
</span> ';
937 entriesHtml += '
<td width=
"1px" class=
"weekDay">' + weekDay + '
</td><td width=
"1px" class=
"date">' + dateStr + '
</td><td width=
"1px" class=
"time">' + time + '
</td><td>';
941 entriesHtml += '
<span class=
"description">' + Summary + '
</span></td></tr>';
944 entriesHtml += '
</table>';
946 entriesHtml = entriesHtml + '
</td></tr></table>';
947 if (config['showNothingText'].Value && entriesHtml == '
<table></table>') {
948 var text = config['nothingText'].Value.replace(/%d/, config['monthRange'].Value);
949 entriesHtml = '
<div style=
"width:295px; height:75px; text-align:center; line-height:75px; overflow:visible;">' + text + '
</div>';
951 log(
"output: " + entriesHtml);
952 if (cacheEntriesHtml != entriesHtml) {
954 document.getElementById('calendarList').innerHTML = entriesHtml;
956 document.getElementById('fullscreenCalendarList').innerHTML = entriesHtml;
957 cacheEntriesHtml = entriesHtml;
960 lastUpdateTime = new Date();
962 error('displaying list:' + e + ', line ' + e.line);
967 // called by handleOnShow() and onResize events
968 function updateScreen()
970 log('updateScreen(): mode=' + mode + ', window.innerHeight=' + window.innerHeight);
972 // check if opening fullscreen
974 // Note: according to Nokia's documentation, an innerHeight of
>91 is an indicator for fullscreen view.
975 // However a bug in E6's firmware causes different window widths and heights (disabled compatibility scaling).
976 // So far, values of
104 and
115 for window.innerHeight were reported, we use a safty margin here and check
978 if( window.innerHeight
> 150 && mode ==
0) {
980 cacheEntriesHtml = '';
981 document.getElementById('body').style.backgroundImage =
"";
984 else if (window.innerHeight <=
150 && mode !=
0) {
986 cacheEntriesHtml = '';
991 updateHomescreen(); // check for screen rotation
996 function handleOnShow()
1000 var time = new Date();
1001 if (time.getTime() - lastUpdateTime.getTime()
> config['updateDataInterval'].Value *
60 *
1000) {
1002 log('updateScreen(): force updateData() because last update was too long ago (' + (time.getTime() - lastUpdateTime.getTime()) /
1000 + 's)');
1005 setUpdateTimer(); // reinitialize update timer
1009 function launchCalendar()
1012 widget.openApplication(config['calendarApp'].Value,
"");
1013 if (config['hideWidgetOnCalendarOpen'].Value)
1016 error('starting Calendar App');
1023 log('New widget instance starting up...');
1026 // call calendar service
1027 if (device !=
"undefined")
1028 calendarService = device.getServiceObject(
"Service.Calendar",
"IDataSource");
1030 throw('device object does not exist');
1032 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>');
1036 calendarList = listCalendars();
1038 updateCalendarColors();
1041 requestNotification();
1042 document.getElementById(
"settingsTitle").innerHTML = getLocalizedText('menu.settings');
1044 if (window.innerHeight
> 91) {
1045 mode =
0; // we're starting fullscreen, we set mode to homescreen in order to let updateScreen() do all the work for us
1050 checkForOffByOneBug();
1051 log(
"init(): updateScreen()");
1053 if (config['useBackgroundImage'].Value)
1054 // check for screen rotation every
1 secs
1055 screenRotationTimer = window.setInterval('checkOrientation()',
1000 *
1);
1057 // call updateScreen() when widget changes from background to forground
1058 window.widget.onshow = handleOnShow;
1060 log(
"init(): finished...");
1062 statupSuccessful = true;
1065 function checkForOffByOneBug() {
1066 var tz = new Date().getTimezoneOffset();
1072 displayOffset =
24*
3600*
1000;
1076 function checkOrientation()
1080 updateHomescreen(); // check for screen rotation
1083 function setUpdateTimer()
1085 updateTimer = window.setInterval('updateTimerCallback()',
1000 *
60 * config['updateDataInterval'].Value);
1088 function clearUpdateTimer()
1090 window.clearInterval(updateTimer);
1093 function updateTimerCallback()
1095 log(
"updateTimerCallback()");
1099 function createMenu()
1101 window.menu.setLeftSoftkeyLabel(
"",null);
1102 window.menu.setRightSoftkeyLabel(
"",null);
1104 var menuSettings = new MenuItem(getLocalizedText('menu.settings'), id++);
1105 var menuCallApp = new MenuItem(getLocalizedText('menu.openCalendarApp'), id++);
1106 var menuHelp = new MenuItem(getLocalizedText('menu.help'), id++);
1107 var menuUpdate = new MenuItem(getLocalizedText('menu.update'), id++);
1108 var menuAbout = new MenuItem(getLocalizedText('menu.about'), id++);
1109 menuSettings.onSelect = showSettings;
1110 menuAbout.onSelect = showAbout;
1111 menuCallApp.onSelect = launchCalendar;
1112 menuUpdate.onSelect = showUpdate;
1113 menuHelp.onSelect = showHelp;
1114 window.menu.clear();
1115 window.menu.append(menuCallApp);
1116 window.menu.append(menuSettings);
1117 window.menu.append(menuHelp);
1118 window.menu.append(menuUpdate);
1119 window.menu.append(menuAbout);
1122 function showSettings()
1126 document.getElementById(
"settingsView").style.display =
"block";
1127 document.onclick = null;
1129 window.menu.setLeftSoftkeyLabel(getLocalizedText('settings.save'), function()
1131 for (var key in config) {
1132 if (config[key].Type == 'String')
1133 config[key].Value = document.forms[
0].elements[
"settings." + key].value;
1134 else if (config[key].Type == 'Int') {
1135 config[key].Value = parseInt(document.forms[
0].elements[
"settings." + key].value);
1136 if (config[key].Value <
0 || isNaN(config[key].Value))
1137 config[key].Value = config[key].Default;
1139 else if (config[key].Type == 'Bool')
1140 config[key].Value = document.forms[
0].elements[
"settings." + key].checked;
1141 else if (config[key].Type == 'UID') {
1142 config[key].Value = parseInt(document.forms[
0].elements[
"settings." + key].value);
1143 if (isNaN(config[key].Value))
1144 config[key].Value = config[key].Default;
1146 else if (config[key].Type == 'Enum') {
1147 config[key].Value = document.forms[
0].elements[
"settings." + key].value;
1148 if (config[key].ValidValues.indexOf(config[key].Value) == -
1)
1149 config[key].Value = config[key].Default;
1151 else if (config[key].Type == 'Array') {
1152 if (key == 'excludedCalendars') {
1153 config[key].Value = new Array();
1154 for(var i=
0; i < calendarList.length; i++) {
1155 var element = document.forms[
0].elements[
"settings." + key +
"." + calendarList[i]];
1156 if (element != null && element.checked == false)
1157 config[key].Value.push(calendarList[i]);
1170 window.menu.setRightSoftkeyLabel(getLocalizedText('settings.cancel'), function()
1176 var settingsHtml = '
<form>';
1177 for (var key in config) {
1178 if (config[key].Type == 'String') {
1180 if (key.substring(
0,
9) ==
"cssStyle_")
1181 prefix = getLocalizedText('settings.cssStyle_prefix');
1182 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 />';
1184 else if (config[key].Type == 'Int')
1185 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 />';
1186 else if (config[key].Type == 'Bool')
1187 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 />';
1188 else if (config[key].Type == 'UID')
1189 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 />';
1190 else if (config[key].Type == 'Enum') {
1191 settingsHtml += '
<table><tr><td>' + getLocalizedText('settings.name.' + key) + '
<br /><select name=
"settings.' + key + '" size=
"1">';
1192 for(var i =
0; i < config[key].ValidValues.length; i++) {
1193 var text = getLocalizedText('settings.validValues.' + key + '.' + config[key].ValidValues[i]);
1194 if (text.indexOf('ERROR') ==
0)
1195 text = config[key].ValidValues[i];
1196 settingsHtml += '
<option value=
"' + config[key].ValidValues[i] + '"' + (config[key].Value == config[key].ValidValues[i] ? '
selected=
"selected"' : '') + '
>' + text + '
</option>';
1198 settingsHtml += '
</select></div></td>' + printHintBox(getLocalizedText('settings.info.' + key)) + '
<hr />';
1200 else if (config[key].Type == 'Array') {
1201 settingsHtml += '
<table><tr><td>' + getLocalizedText('settings.name.' + key) + '
<br />';
1202 if (key == 'excludedCalendars') {
1203 for(var i=
0; i < calendarList.length; i++) {
1204 var checked = '
checked=
"checked"';
1205 if (config[key].Value.indexOf(calendarList[i]) != -
1)
1207 settingsHtml += '
<input name=
"settings.' + key + '.' + calendarList[i] + '" type=
"checkbox" value=
"' + calendarList[i] + '" ' + checked + '
/> ' + calendarList[i] + '
<br />';
1210 settingsHtml += '
</td>' + printHintBox(getLocalizedText('settings.info.' + key)) + '
<hr />';
1213 settingsHtml += '
<input name=
"reset" type=
"button" value=
"' + getLocalizedText('settings.restoreDefaults') + '" onclick=
"javascript:restoreDefaultSettings();showSettings();" />';
1214 settingsHtml += '
</form>';
1215 document.getElementById(
"settingsList").innerHTML = settingsHtml;
1218 function changeCssClass(classname, properties)
1220 for(var i =
0; i < document.styleSheets[
0]['cssRules'].length; i++)
1222 if (document.styleSheets[
0]['cssRules'][i].selectorText == classname) {
1223 document.styleSheets[
0].deleteRule(i);
1224 document.styleSheets[
0].insertRule(classname + ' { ' + properties + ' }', document.styleSheets[
0]['cssRules'].length);
1230 function updateCssClasses()
1232 for(var key in config) {
1233 changeCssClass(getLocalizedText('settings.name.' + key), config[key].Value);
1237 function getSettingsCalEntryId()
1239 if (settingsCalEntryId == null) {
1240 // check if entry already exists
1241 var listFiltering = {
1242 Type:'CalendarEntry',
1244 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!
1245 EndRange: new Date(
2000,
0,
2),
1246 SearchText: 'ComingNext Settings|',
1252 result = calendarService.IDataSource.GetList(listFiltering);
1253 if (result.ErrorCode)
1254 throw(result.ErrorMessage);
1257 error(
"getSettingsCalEntryId: GetList() failed: " + e + ', line ' + e.line);
1260 var list = result.ReturnValue;
1261 var entry = list.getNext();
1262 if (entry != undefined) {
1263 settingsCalEntryId = entry.LocalId;
1264 log(
"settingsCalEntryId=" + settingsCalEntryId);
1266 else { // create settings item
1267 var item = new Object();
1268 item.Type =
"DayEvent";
1269 item.StartTime = new Date(
2000,
0,
1);
1270 item.Summary =
"ComingNext Settings|";
1272 var criteria = new Object();
1273 criteria.Type =
"CalendarEntry";
1274 criteria.Item = item;
1277 var result = calendarService.IDataSource.Add(criteria);
1278 if (result.ErrorCode)
1279 throw(result.ErrorMessage);
1281 error(
"getSettingsCalEntryId: " + e + ', line ' + e.line);
1284 getSettingsCalEntryId();
1289 function restoreDefaultSettings()
1291 for (var key in config)
1292 config[key].Value = config[key].Default;
1295 function loadSettings()
1297 getSettingsCalEntryId();
1298 var listFiltering = {
1299 Type:'CalendarEntry',
1301 LocalId: settingsCalEntryId
1306 result = calendarService.IDataSource.GetList(listFiltering);
1307 if (result.ErrorCode)
1308 throw(result.ErrorMessage);
1311 error(
"loadSettings: GetList() failed: " + e + ', line ' + e.line);
1314 var entry = result.ReturnValue.getNext();
1315 if (entry != undefined) {
1316 log(
"Loading Settings...");
1317 // only reload settings if they chanced since the last reload
1318 if (settingsCache != entry.Summary)
1320 restoreDefaultSettings();
1321 var stringlist = entry.Summary.split(
"|");
1322 // skip the first two entries, those contain header and version info
1323 for(var i =
2; i < stringlist.length -
1; i++) {
1324 var pair = stringlist[i].split('=');
1326 var value = pair[
1];
1327 if (key == null || value == null || config[key] == null) {
1328 log('Warning: unknown or invalid setting: ' + stringlist[i]);
1331 log('stringlist[' + i + ']: ' + key + '=\'' + value + '\'');
1332 if (config[key].Type == 'Int') {
1333 config[key].Value = Number(value);
1334 if (isNaN(config[key].Value))
1335 config[key].Value = config[key].Default;
1337 else if (config[key].Type == 'String')
1338 config[key].Value = value;
1339 else if (config[key].Type == 'Bool')
1340 config[key].Value = (value == 'true')
1341 else if (config[key].Type == 'Enum')
1342 config[key].Value = value;
1343 else if (config[key].Type == 'UID') {
1344 config[key].Value = Number(value);
1345 if (isNaN(config[key].Value))
1346 config[key].Value = config[key].Default;
1348 else if (config[key].Type == 'Array') {
1349 config[key].Value = value.split(
"^");
1350 if (config[key].Value.length ==
1 && config[key].Value[
0] ==
"") {
1351 config[key].Value = [];
1355 settingsCache = entry.Summary;
1359 log(
"Settings already cached and did not change");
1363 error(
"Failed to load settings, calendar entry could not be found");
1367 function saveSettings()
1369 getSettingsCalEntryId();
1370 var item = new Object();
1371 item.Type =
"DayEvent";
1372 item.StartTime = new Date(
2000,
0,
1);
1373 item.LocalId = settingsCalEntryId;
1374 item.Summary =
"ComingNext Settings|" + version +
"|";
1376 for (var key in config) {
1377 if (config[key].Type == 'Int')
1378 item.Summary += key +
"=" + config[key].Value.toString() +
"|";
1379 else if (config[key].Type == 'String')
1380 item.Summary += key +
"=" + config[key].Value +
"|";
1381 else if (config[key].Type == 'Bool')
1382 item.Summary += key +
"=" + (config[key].Value ? 'true' : 'false') +
"|";
1383 else if (config[key].Type == 'Enum')
1384 item.Summary += key +
"=" + config[key].Value +
"|";
1385 else if (config[key].Type == 'UID')
1386 item.Summary += key +
"=" + config[key].Value.toString() +
"|";
1387 else if (config[key].Type == 'Array')
1388 item.Summary += key +
"=" + config[key].Value.join(
"^") +
"|";
1390 settingsCache = item.Summary;
1392 var criteria = new Object();
1393 criteria.Type =
"CalendarEntry";
1394 criteria.Item = item;
1396 log(
"Saving settings to calendar entry: " + item.Summary);
1398 var result = calendarService.IDataSource.Add(criteria);
1399 if (result.ErrorCode)
1400 throw(result.ErrorMessage);
1402 error(
"saveSettings: " + e + ', line ' + e.line);
1405 lastReloadTime = null; // force calendar data reload on next update
1410 function toggleVisibility(elementId)
1412 if (document.getElementById(elementId).style.display ==
"none")
1413 document.getElementById(elementId).style.display =
"block";
1415 document.getElementById(elementId).style.display =
"none";
1419 function printHintBox(text)
1422 return '
<td width=
"1%" align=
"right" onclick=
"javascript:toggleVisibility(\'info' + uniqueId + '\')">' + getLocalizedText('settings.help') + '
</td></tr></table>'+
1423 '
<div class=
"settingsInfo" id=
"info' + uniqueId + '" style=
"display:none">' + text + '
</div>';
1426 function showAbout()
1430 document.getElementById(
"aboutView").style.display =
"block";
1431 document.onclick = null;
1433 window.menu.setLeftSoftkeyLabel(
" ", function(){});
1434 window.menu.setRightSoftkeyLabel(getLocalizedText('softkey.back'), function()
1440 //document.getElementById(
"aboutView").innerHTML = 'aboutView';
1441 document.getElementById(
"name").innerHTML =
"Coming Next " + version;
1444 function showHelp() {
1445 widget.openURL('http://comingnext.sf.net/help');
1448 function updateFullscreen()
1452 function showFullscreen()
1454 log(
"showFullscreen()");
1456 document.getElementById(
"fullscreenView").style.display =
"block";
1457 document.getElementById('body').className =
"backgroundFullscreen";
1459 document.onclick = launchCalendar;
1464 function getBackgroundImage()
1469 if (config['backgroundImageLocation'].Value == config['backgroundImageLocation'].ValidValues[
0]) // internal
1470 bgImage = 'background_' + orientation + '.png';
1472 bgImage = 'C:/Data/background_' + panelNum + '_' + orientation + '.png';
1476 function updateHomescreen()
1478 if (config['useBackgroundImage'].Value) {
1479 // check if we have a completely unknown screen resolution
1480 var screenHeight = screen.height;
1481 var screenWidth = screen.width;
1482 if (screenHeight !=
640 && screenHeight !=
480 && screenHeight !=
360)
1483 screenHeight =
360; // we can only assume we're in portrait mode, so we set the screen dims as needed for the following code
1484 if (screenWidth !=
640 && screenWidth !=
480 && screenWidth !=
360)
1485 screenWidth =
640; // we can only assume we're in portrait mode, so we set the screen dims as needed for the following code
1487 // check for screen rotation
1488 if (orientation != 'portrait' && ((screenWidth ==
360 && screenHeight ==
640) || (screenWidth ==
640 && screenHeight ==
480))) {
1489 window.widget.prepareForTransition(
"fade");
1490 orientation = 'portrait';
1491 document.getElementById('body').style.backgroundImage = 'url(' + getBackgroundImage() + ')';
1492 document.getElementById('body').style.backgroundColor = 'none';
1493 window.widget.performTransition();
1494 } else if (orientation != 'landscape' && ((screenWidth ==
640 && screenHeight ==
360) || (screenWidth ==
480 && screenHeight ==
640))) {
1495 window.widget.prepareForTransition(
"fade");
1496 orientation = 'landscape';
1497 document.getElementById('body').style.backgroundImage = 'url(' + getBackgroundImage() + ')';
1498 document.getElementById('body').style.backgroundColor = 'none';
1499 window.widget.performTransition();
1501 else if (document.getElementById('body').style.backgroundImage ==
"")
1503 document.getElementById('body').style.backgroundImage = 'url(' + getBackgroundImage() + ')';
1508 function showHomescreen()
1510 log(
"showHomescreen()");
1512 document.getElementById(
"homescreenView").style.display =
"block";
1513 document.getElementById('body').className =
"background";
1514 document.onclick = null;
1518 function getLocalizedText(p_Txt)
1520 if (localizedText[p_Txt])
1521 return localizedText[p_Txt];
1523 return 'ERROR: missing translation for ' + p_Txt;
1526 function showUpdate()
1530 document.getElementById(
"updateView").style.display =
"block";
1531 document.onclick = null;
1533 window.menu.setLeftSoftkeyLabel(getLocalizedText('update.checknow'), function(){
1536 window.menu.setRightSoftkeyLabel(getLocalizedText('softkey.back'), function()
1542 document.getElementById(
"currentVersion").innerHTML = getLocalizedText(
"update.current") + version;
1546 function checkForUpdate()
1548 // asynch XHR to server url
1549 reqV = new XMLHttpRequest();
1550 reqV.onreadystatechange = checkForUpdateCallback;
1551 document.getElementById(
"updateDiv").innerHTML = getLocalizedText(
"update.checking");
1552 reqV.open(
"GET", versionURL, true);
1556 function checkForUpdateCallback()
1558 if (reqV.readyState ==
4) {
1559 if (reqV.status ==
200) {
1560 var resultXml = reqV.responseText;
1562 var div = document.getElementById(
"tmp");
1563 div.innerHTML = resultXml;
1564 var newVersion = div.getElementsByTagName('version')[
0].innerHTML;
1565 var newVersionURL = div.getElementsByTagName('url')[
0].innerHTML;
1567 if (version != newVersion) {
1568 document.getElementById(
"updateDiv").innerHTML = getLocalizedText(
"update.download").replace(/%
1/, newVersion).replace(/%
2/, newVersionURL);
1571 document.getElementById(
"updateDiv").innerHTML = getLocalizedText(
"update.nonewversion");
1576 document.getElementById(
"updateDiv").innerHTML = getLocalizedText(
"update.error") + reqV.status +
" " + reqV.responseText;
1581 function hideViews()
1583 document.getElementById(
"homescreenView").style.display =
"none";
1584 document.getElementById(
"fullscreenView").style.display =
"none";
1585 document.getElementById(
"aboutView").style.display =
"none";
1586 document.getElementById(
"settingsView").style.display =
"none";
1587 document.getElementById(
"updateView").style.display =
"none";
1590 function listCalendars()
1600 DefaultCalendar: false
1604 var calendarsResult = calendarService.IDataSource.GetList(criteria);
1605 if (calendarsResult.ErrorCode !=
0)
1606 throw(
"Error fetching list of calendars: " + calendarsResult.ErrorCode + ': ' + calendarsResult.ErrorMessage);
1607 var calendarListIterator = calendarsResult.ReturnValue;
1612 while (( item = calendarListIterator.getNext()) != undefined ) {
1613 calendars[count++] = item;
1615 log(
"Available Calendars: " + calendars.join(
", "));
1618 error('listing calendars:' + e + ', line ' + e.line);
1623 // Copies all objects and their properties to an array. Data is copied so nothing gets lost when the reference is removed
1624 // 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
1625 function listToArray(list, calendarName)
1627 var array = new Array();
1630 while (( item = list.getNext()) != undefined ) {
1631 var itemCopy = new Object();
1632 for(var i=
0; i < entryFields.length; i++) {
1633 itemCopy[entryFields[i]] = item[entryFields[i]];
1635 // for some reason, the CalendarName property is never correctly queried, so we assign it manually here
1636 if (!itemCopy['CalendarName']) {
1637 itemCopy['CalendarName'] = calendarName;
1639 if (config['anonymizeLogging'].Value && config['enableLogging'].Value) {
1640 if (itemCopy['Summary'])
1641 itemCopy['Summary'] = getHashForString(itemCopy['Summary']);
1642 if (itemCopy['Location'])
1643 itemCopy['Location'] = getHashForString(itemCopy['Location']);
1645 array.push(itemCopy);
1646 txt += array[array.length -
1].Summary +
", ";
1648 log(
"listToArray(): " + txt);
1652 function sortCalendarEntries(a, b)
1655 log(
"sortCalendarEntries(" + a.Summary +
"," + b.Summary +
")");
1657 if (a.InstanceStartTime != null) {
1658 atime = a.InstanceStartTime;
1660 else if (a.StartTime != null) {
1661 atime = a.StartTime;
1663 else if (a.InstanceEndTime != null) {
1664 atime = a.InstanceEndTime;
1666 else if (a.EndTime != null) {
1670 if (b.InstanceStartTime != null) {
1671 btime = b.InstanceStartTime;
1673 else if (b.StartTime != null) {
1674 btime = b.StartTime;
1676 else if (b.InstanceEndTime != null) {
1677 btime = b.InstanceEndTime;
1679 else if (b.EndTime != null) {
1683 if (atime && btime) {
1685 atime = parseDate(atime);
1686 btime = parseDate(btime);
1688 // sort by date & time
1689 if (atime < btime) {
1692 else if (atime
> btime) {
1696 else if (a.Type != b.Type) {
1697 if (a.Type < b.Type) {
1700 else if (a.Type
> b.Type) {
1704 // sort by description
1705 else if (a.Summary && b.Summary && a.Summary != b.Summary) {
1706 if (a.Summary < b.Summary) {
1709 else if (a.Summary
> b.Summary) {
1714 // NOTE: events my have no date information at all. In that case, we list events without date first
1715 else if (atime && !btime) {
1718 else if (!atime && btime) {
1721 else if (!atime && !btime) {
1723 if (a.Type != b.Type) {
1724 if (a.Type < b.Type) {
1727 else if (a.Type
> b.Type) {
1731 // sort by description
1732 else if (a.Summary && b.Summary && a.Summary != b.Summary) {
1733 if (a.Summary < b.Summary) {
1736 else if (a.Summary
> b.Summary) {
1745 function updateCalendarColors()
1748 calendarColors = [];
1749 if (calendarList.length
> maxColors) {
1750 log(
"updateCalendarColors(): Warning: more calendars than available indicator colors");
1752 for(var i=
0; i < calendarList.length; i++) {
1753 calendarColors[calendarList[i]] = (i % maxColors) +
1;
1757 function log(message)
1759 if (config['enableLogging'].Value) {
1760 console.info(message);
1764 function getDefaultFontSize()
1766 if (defaultFontSize == null) {
1767 var pa = document.body;
1768 var who = document.createElement('div');
1769 who.className = 'defaultEm';
1770 who.appendChild(document.createTextNode('M'));
1771 pa.appendChild(who);
1772 var fs = [who.offsetWidth, who.offsetHeight];
1773 pa.removeChild(who);
1774 defaultFontSize = fs;
1776 return defaultFontSize;
1779 function getHashForString(string)
1781 // cheap hashing, loosly based on Java's String.hashCode()
1782 for (var hash =
0, i =
0; i < string.length; i++)
1783 hash = (hash <<
5) - hash + string.charCodeAt(i);
1784 hash = hash & hash; // Convert to
32bit integer
1787 return hash.toString(
16).toUpperCase();
1792 <style type=
"text/css">
1794 table { margin:
0px; padding:
0px; border-spacing:
0px; border-collapse: collapse; }
1795 td { padding:
0px
5px
0px
0px; white-space:nowrap; overflow:visible; margin:
0px; }
1796 hr { color:#ffffff; background-color:#ffffff; height:
1px; text-align:left; border-style:none; }
1797 .settingsInfo { display:none; font-style:italic; }
1798 .title { font-weight:bold; font-size:
14pt; }
1799 .textInput { width:
90%; }
1800 .credits { margin-left:
40px; text-indent: -
20px; margin-bottom:
0px; }
1801 #homescreenView { width:
312px; height:
82px; overflow:hidden; }
1802 #calendarList { position:absolute; left:
5px; top:
0px; width:
307px; height:
82px; overflow:hidden; }
1803 #name { text-align:center; }
1804 #appicon { display: block; margin-left: auto; margin-right: auto; margin-top:
10px; }
1805 #smallappicon { width:
22px; height:
22px; margin-right:
10px; float:left; }
1806 .defaultEm { font-size:
1em; position:absolute; line-height:
1; padding:
0; visibility:hidden; }
1811 <body onload=
"javascript:setTimeout('init()', 10)" onresize=
"javascript:updateScreen()" id=
"body" class=
"background">
1812 <div id=
"homescreenView">
1813 <div id=
"calendarList">loading...
</div>
1815 <div id=
"fullscreenView" style=
"display:none;">
1816 <img src=
"Icon.png" id=
"smallappicon">
1817 <h1 class=
"title">Coming Next
</h1>
1819 <div id=
"fullscreenCalendarList">loading...
</div>
1821 <div id=
"settingsView" style=
"display:none">
1822 <img src=
"Icon.png" id=
"smallappicon">
1823 <h1 id=
"settingsTitle" class=
"title">Settings
</h1>
1825 <div id=
"settingsList"></div>
1827 <div id=
"aboutView" style=
"display:none">
1828 <img src=
"Icon.png" id=
"appicon">
1829 <h1 id=
"name">Coming Next
</h1>
1831 <p>Created by Dr. Cochambre and Michael Prager.
</p>
1832 <p>Contributions:
</p>
1833 <p class=
"credits">Paul Moore (bug fixes, new features and code cleanup)
</p>
1834 <p class=
"credits">Manfred Hanselmann (DST support)
</p>
1835 <p class=
"credits">Christophe Milsent (translation support & French translation)
</p>
1836 <p class=
"credits">Flavio Nathan (Portuguese-Brazilian translation)
</p>
1837 <p class=
"credits">Tokeda (Russian translation)
</p>
1838 <p class=
"credits">Marcella Ferrari (Italian translation)
</p>
1839 <p class=
"credits">Venos (Italian translation)
</p>
1840 <p class=
"credits">Francisco Rodero (Catalan translation)
</p>
1841 <p class=
"credits">zbigzbig20 (Polish translation)
</p>
1842 <p class=
"credits">Streamkeskus (Finnish translation)
</p>
1843 <p class=
"credits">renek (Czech translation)
</p>
1844 <p>This software is open source and licensed under the GPLv3.
</p>
1845 <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>
1848 <div id=
"updateView" style=
"display:none">
1849 <img src=
"Icon.png" id=
"smallappicon">
1850 <h1 class=
"title">Check for update
</h1>
1852 <div id=
"currentVersion">Coming Next ??
</div>
1853 <div id=
"updateDiv"></div>
1854 <div id=
"tmp" style=
"display:none;"></div>