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 */
11 .backgroundFullscreen { }
29 <script type=
"text/javascript" src=
"localizedTextStrings.js" charset=
"utf-8" />
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 showTodayAsText: { Type: 'Bool', Default: true, Value: true,},
41 todayText: { Type: 'String', Default: getLocalizedText('settings.default.todayText'), Value: getLocalizedText('settings.default.todayText'),},
42 tomorrowText: { Type: 'String', Default: getLocalizedText('settings.default.tomorrowText'), Value: getLocalizedText('settings.default.tomorrowText'),},
43 showNowAsText: { Type: 'Bool', Default: true, Value: true,},
44 nowText: { Type: 'String', Default: getLocalizedText('settings.default.nowText'), Value: getLocalizedText('settings.default.nowText'),},
45 markOverdueTodos: { Type: 'Bool', Default: true, Value: true,},
46 overdueText: {Type: 'String', Default: getLocalizedText('settings.default.overdueText'), Value: getLocalizedText('settings.default.overdueText'),},
47 dateSeparator: { Type: 'String', Default: getLocalizedText('settings.default.dateSeparator'), Value: getLocalizedText('settings.default.dateSeparator'),},
48 dateFormat: { Type: 'Enum', Default: 'auto', Value: 'auto', ValidValues: ['auto', 'DDMM', 'MMDD'],},
49 weekDayLength: { Type: 'Int', Default:
2, Value:
2,},
50 updateDataInterval: { Type: 'Int', Default:
5, Value:
5,},
51 calendarApp: { Type: 'UID', Default:
0x10005901, Value:
0x10005901,},
52 eventsPerWidget: { Type: 'Int', Default:
4, Value:
4,},
53 showNothingText: { Type: 'Bool', Default: true, Value: true,},
54 nothingText: { Type: 'String', Default: getLocalizedText('settings.default.nothingText'), Value: getLocalizedText('settings.default.nothingText'),},
55 enableDaylightSaving: { Type: 'Bool', Default: true, Value: true,},
56 daylightSavingOffset: { Type: 'Int', Default:
1, Value:
1,},
57 hideWidgetOnCalendarOpen: { Type: 'Bool', Default: false, Value: false,},
58 showCalendarIndicator: { Type: 'Bool', Default: true, Value: true,},
59 excludedCalendars: { Type: 'Array', Default: [], Value: [],},
60 enableLogging: { Type: 'Bool', Default: false, Value: false,},
61 cssStyle_background: { Type: 'String', Default: 'color:#ffffff; background-color:#
000000', Value: 'color:#ffffff; background-color:#
000000',},
62 cssStyle_backgroundFullscreen: { Type: 'String', Default: 'color:#ffffff; background-color:#
000000', Value: 'color:#ffffff; background-color:#
000000',},
63 cssStyle_weekDay: { Type: 'String', Default: '', Value: '',},
64 cssStyle_date: { Type: 'String', Default: '', Value: '',},
65 cssStyle_today: { Type: 'String', Default: 'color:#ff0000', Value: 'color:#ff0000',},
66 cssStyle_tomorrow: { Type: 'String', Default: 'color:#
0000ff', Value: 'color:#
0000ff',},
67 cssStyle_time: { Type: 'String', Default: '', Value: '',},
68 cssStyle_now: { Type: 'String', Default: 'color:#ff00ff', Value: 'color:#ff00ff',},
69 cssStyle_description: { Type: 'String', Default: '', Value: '',},
70 cssStyle_icon: { Type: 'String', Default: 'width:
15px; height:
15px', Value: 'width:
15px; height:
15px',},
71 cssStyle_overdue: { Type: 'String', Default: 'color:#ffff00', Value: 'color:#ffff00',},
72 cssStyle_calendar1: { Type: 'String', Default: 'background-color:#
0757cf', Value: 'background-color:#
0757cf',},
73 cssStyle_calendar2: { Type: 'String', Default: 'background-color:#
579f37', Value: 'background-color:#
579f37',},
74 cssStyle_calendar3: { Type: 'String', Default: 'background-color:#ff9f07', Value: 'background-color:#ff9f07',},
75 cssStyle_calendar4: { Type: 'String', Default: 'background-color:#af8fef', Value: 'background-color:#af8fef',},
76 cssStyle_calendar5: { Type: 'String', Default: 'background-color:#
57afbf', Value: 'background-color:#
57afbf',},
77 cssStyle_calendar6: { Type: 'String', Default: 'background-color:#
9fdf57', Value: 'background-color:#
9fdf57',},
82 //-------------------------------------------------------
83 // Nothing of interest from here on...
84 //-------------------------------------------------------
85 var panelNum =
0; // use
1 for second panel
87 var versionURL =
"http://comingnext.sourceforge.net/version.xml";
88 var calendarService = null;
89 var cacheEntriesHtml = [];
90 var months_translated = [];
93 var mode =
0; //
0 = homescreen,
1 = fullscreen,
2 = settings,
3 = about,
4 = check for update
95 var settingsCalEntryId = null;
96 var settingsCache = null;
97 var notificationRequests = new Array();
98 var calendarList = [];
99 var calendarColors = [];
100 var updateTimer = null;
101 var screenRotationTimer = null;
102 var lastUpdateTime = now;
103 var errorOccured = false;
105 // vars for daylight saving time
106 var summertime = false; // true, if current date is in summer, false if in winter
107 var daylightSavingDates = new Object(); // caches calculated DST winter and summer time shift dates
109 // this is a list of data fields a calendar event can have
123 window.onload = init;
124 window.onresize = updateScreen;
125 window.onshow = updateScreen;
127 function isLeapYear( year ) {
128 if (( year %
4 ==
0 && year %
100 !=
0 ) || year %
400 ==
0 )
134 function calcLeapYear(year, days)
136 if (isLeapYear(year))
142 function subToSunday(myDate, year, days, prevMonthDays)
144 for (i = myDate.getDay(); i
> 0 ;i--)
146 days -= prevMonthDays;
147 days = isLeapYear(year) ? --days : days;
151 function isSummertime(curDate)
156 // if we already calculated DST summer and winter time dates for this year, use cached values
157 var dst = daylightSavingDates[curDate.getFullYear()];
159 var thisYearS = new Date(curDate.getFullYear(),
3,
0,
0,
0,
0 );
160 var thisYearW = new Date(curDate.getFullYear(),
10,
0,
0,
0,
0 );
161 var nextYearS = new Date(curDate.getFullYear() +
1,
3,
0,
0,
0,
0 );
162 var nextYearW = new Date(curDate.getFullYear() +
1,
10,
0,
0,
0,
0 );
164 thisYearSDays = nextYearSDays =
90;
165 thisYearWDays = nextYearWDays =
304;
167 thisYearSDays = calcLeapYear(curDate.getFullYear(), thisYearSDays);
168 thisYearWDays = calcLeapYear(curDate.getFullYear(), thisYearWDays);
169 nextYearSDays = calcLeapYear(curDate.getFullYear() +
1, nextYearSDays);
170 nextYearWDays = calcLeapYear(curDate.getFullYear() +
1, nextYearWDays);
172 thisYearSDays = subToSunday(thisYearS, curDate.getFullYear(), thisYearSDays,
59);
173 thisYearWDays = subToSunday(thisYearW, curDate.getFullYear(), thisYearWDays,
273);
174 nextYearSDays = subToSunday(nextYearS, curDate.getFullYear() +
1, nextYearSDays,
59);
175 nextYearWDays = subToSunday(nextYearW, curDate.getFullYear() +
1, nextYearWDays,
273);
178 Summer: new Date (curDate.getFullYear(),
03-
1, thisYearSDays,
2,
0,
0),
179 Winter: new Date (curDate.getFullYear(),
10-
1, thisYearWDays,
2,
0,
0),
181 daylightSavingDates[curDate.getFullYear()] = dst;
184 if (dst.Summer < curDate)
186 if (dst.Winter < curDate)
188 if (summer && !winter)
194 function error(message)
196 console.info('Error: ' + message);
197 document.getElementById(
"calendarList").innerHTML = 'Error: ' + message;
198 document.getElementById(
"fullscreenCalendarList").innerHTML = 'Error: ' + message;
200 document.onclick = null;
203 function areDatesEqual(date1, date2)
205 return (date1.getFullYear() == date2.getFullYear() &&
206 date1.getMonth() == date2.getMonth() &&
207 date1.getDate() == date2.getDate());
210 function isTomorrow(date)
212 // tommorow = now +
1 day
213 // ToDo: some days can be shorter as
24 hours(daylight saving change day)
214 return areDatesEqual(date, new Date (now.getTime() +
24*
60*
60*
1000));
217 function isToday(date)
219 return areDatesEqual(date, now);
222 function collectLocales()
224 var tmpyear =
2000 + panelNum;
227 if (months_translated.length
> 0)
229 for (month =
0; month <
12; month++) {
230 var startDate = new Date(tmpyear, month,
15);
232 var item = new Object();
233 item.Type =
"DayEvent";
234 item.StartTime = startDate;
235 item.Summary =
"__temp" + month;
237 var criteria = new Object();
238 criteria.Type =
"CalendarEntry";
239 criteria.Item = item;
242 var result = calendarService.IDataSource.Add(criteria);
243 if (result.ErrorCode)
244 error(result.ErrorMessage);
246 error(
"collectLocales: " + e + ', line ' + e.line);
250 var startTime = new Date(tmpyear,
0,
1);
251 var endTime = new Date(tmpyear,
11,
31);
252 var listFiltering = {
253 Type:'CalendarEntry',
255 StartRange: startTime,
257 SearchText: '__temp',
261 var result = calendarService.IDataSource.GetList(listFiltering);
262 if (result.ErrorCode) {
263 error(result.ErrorMessage);
266 var list = result.ReturnValue;
268 error(e + ', line ' + e.line);
271 var ids = new Array();
277 while (list && (entry = list.getNext()) != undefined) {
278 dateArr = entry.StartTime.replace(/,/g,'').replace(/\./g,':').replace(/ /g,' ').split(' ');
279 var day = dateArr[
1];
280 var month = dateArr[
2];
281 var year = dateArr[
3];
283 // make sure month is set properly
284 if (isNaN(parseInt(day))) {
288 } else if (isNaN(parseInt(year))) {
294 log(entry.StartTime + ' -
> ' + month + ' ' + counter);
295 ids[counter] = entry.id;
296 months_translated[month] = counter +
1;
300 error(e + ', line ' + e.line);
305 var criteria = new Object();
306 criteria.Type =
"CalendarEntry";
311 var result = calendarService.IDataSource.Delete(criteria);
312 if (result.ErrorCode)
313 error(result.ErrorMessage);
315 error('deleting temp calendar entries:' + e + ', line ' + e.line);
320 function requestNotification()
322 var criteria = new Object();
323 criteria.Type =
"CalendarEntry";
324 criteria.Filter = new Object();
325 for(var i=
0; i < calendarList.length; i++) {
326 criteria.Filter.CalendarName = calendarList[i];
328 var notificationRequest = calendarService.IDataSource.RequestNotification(criteria, callback);
329 if (notificationRequest.ErrorCode)
330 error('requestNotification failed with error code ' + notificationRequest.ErrorCode);
331 notificationRequests.push(notificationRequest);
333 error(
"requestNotification: " + e + ', line ' + e.line);
337 var criteria2 = new Object();
338 criteria2.Type =
"CalendarEntry";
339 criteria2.Filter = new Object();
340 criteria2.Filter.LocalIdList = new Array();
341 criteria2.Filter.LocalIdList[
0] = settingsCalEntryId;
343 var notificationRequest = calendarService.IDataSource.RequestNotification(criteria2, settingsCallback);
344 if (notificationRequest.ErrorCode)
345 error('requestNotification failed with error code ' + notificationRequest.ErrorCode);
346 notificationRequests.push(notificationRequest);
348 error(
"requestNotification: " + e + ', line ' + e.line);
352 function cancelNotification()
354 for(var i=
0; i < notificationRequests.length; i++) {
356 var result = calendarService.IDataSource.Cancel(notificationRequests[i]);
357 if (result.ErrorCode)
358 error('cancelNotification failed with error code ' + result.ErrorCode);
360 error(
"cancelNotification: " + e + ', line ' + e.line);
365 function callback(transId, eventCode, result)
367 log(
"callback(): panelNum: %d transId: %d eventCode: %d result.ErrorCode: %d", panelNum, transId, eventCode, result.ErrorCode);
371 function settingsCallback(transId, eventCode, result)
373 log(
"settingsCallback(): panelNum: %d transId: %d eventCode: %d result.ErrorCode: %d", panelNum, transId, eventCode, result.ErrorCode);
377 function parseDate(dateString)
380 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:
381 Wednesday,
26 August,
2009 24:
00:
00
382 Wednesday,
26 August,
2009 12:
00:
00 am
383 Wednesday, August
26,
2009 12:
00:
00 am
384 Wednesday,
2009 August,
26 12:
00:
00 am
385 Wednesday,
2009 August,
28 8.00.00 pm
386 Wednesday,
2009 August,
28 08:
00:
00 PM
389 if (dateString ==
"" || dateString == null)
391 var dateArr = dateString.replace(/,/g,'').replace(/\./g,':').replace(/ /g,' ').split(' ');
392 if (dateArr.length !=
5 && dateArr.length !=
6)
396 var weekDay = dateArr[
0];
397 var day = dateArr[
1];
398 var month = dateArr[
2];
399 var year = dateArr[
3];
400 // make sure month is set properly
401 if (isNaN(parseInt(day))) {
405 } else if (isNaN(parseInt(year))) {
410 // make sure day and year are set properly
411 if (Number(day)
> Number(year)) {
416 month = months_translated[month];
419 var timeArr = dateArr[
4].split(':');
420 if (timeArr.length !=
3)
422 var hours = Number(timeArr[
0]);
423 var minutes = Number(timeArr[
1]);
424 var seconds = Number(timeArr[
2]);
425 if (dateArr.length ==
6 && dateArr[
5].toLowerCase() == 'pm' && hours <
12)
427 if (dateArr.length ==
6 && dateArr[
5].toLowerCase() == 'am' && hours ==
12)
430 var result = new Date(year, month -
1, day, hours, minutes, seconds);
432 // take care of daylight saving time
433 if (config['enableDaylightSaving'].Value) {
435 // determine if date is in summer or winter time
436 var dateSummerTime = isSummertime(result);
438 // work around bug in Nokias calendar api resulting in dates within a different DST to be off by
1 hour
439 if (summertime && !dateSummerTime) {
440 result = new Date(result.getTime() -
1000 *
60 *
60 * config['daylightSavingOffset'].Value); // -
1 hour
441 log('parseDate(): fixing time -
1h: ' + result);
443 else if (!summertime && dateSummerTime) {
444 result = new Date(result.getTime() +
1000 *
60 *
60 * config['daylightSavingOffset'].Value); // +
1 hour
445 log('parseDate(): fixing time +
1h: ' + result);
452 // 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"
453 function formatDate(date, format)
455 var day = date.getDate().toString();
456 var month = (date.getMonth() +
1).toString();
457 while (day.length <
2) { day = '
0' + day; }
458 while (month.length <
2) { month = '
0' + month; }
460 if (config['showTodayAsText'].Value && isToday(date))
461 return '
<span class=
"today">' + config['todayText'].Value + '
</span>';
462 if (config['showTodayAsText'].Value && isTomorrow(date))
463 return '
<span class=
"tomorrow">' + config['tomorrowText'].Value + '
</span>';
465 var dateArr = format.replace(/,/g,'').replace(/\./g,':').replace(/ /g,' ').split(' ');
466 if (dateArr.length !=
5 && dateArr.length !=
6) {
467 // we don't know how to format this
468 if (config['dateFormat'].Value == 'auto' || config['dateFormat'].Value == 'DDMM')
469 return day + config['dateSeparator'].Value + month;
471 return month + config['dateSeparator'].Value + day;
475 if (config['dateFormat'].Value == 'MMDD')
477 else if (config['dateFormat'].Value == 'DDMM')
480 // config['dateFormat'].Value == 'auto', try to detect system setting
482 var day_ = dateArr[
1];
483 var month_ = dateArr[
2];
484 var year_ = dateArr[
3];
485 // make sure month is set properly
486 if (isNaN(parseInt(day_))) {
491 } else if (isNaN(parseInt(year_))) {
497 // make sure day and year are set properly
498 if (Number(day_)
> Number(year_))
503 return day + config['dateSeparator'].Value + month;
505 return month + config['dateSeparator'].Value + day;
508 function formatTime(date)
510 // date is a Date() object
511 date.setSeconds(
0); // we don't care about seconds
512 var time = date.toLocaleTimeString().replace(/[\.:]
00/, ''); // remove seconds from string
513 if (time.replace(/\./, ':').split(':')[
0].length <
2)
515 if (config['showNowAsText'].Value && date.getTime() == now.getTime())
516 time = '
<span class=
"now">' + config['nowText'].Value + '
</span>';
520 function updateData()
527 // check if we got additional or less calendars since our last update
528 var newCalendarList = listCalendars();
529 if (newCalendarList.length != calendarList.length) {
530 calendarList = newCalendarList;
531 updateCalendarColors();
532 cancelNotification();
533 requestNotification();
537 // meetings have time
538 // 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
540 summertime = isSummertime(now); // cache summer time info for today
541 var meetingList = [];
542 for(var i=
0; i < calendarList.length; i++) {
543 // ignore excluded calendars
544 if (config['excludedCalendars'].Value.indexOf(calendarList[i]) != -
1)
546 var meetingListFiltering = {
547 Type:'CalendarEntry',
549 CalendarName: calendarList[i],
550 StartRange: (new Date(now.getFullYear(), now.getMonth(), now.getDate(),
0,
0,
0)),
551 EndRange: (new Date(now.getFullYear(), now.getMonth() + config['monthRange'].Value, now.getDate(),
0,
0,
0))
554 var meetingResult = calendarService.IDataSource.GetList(meetingListFiltering);
555 if (meetingResult.ErrorCode !=
0)
556 throw(
"Error fetching calendar data: " + meetingResult.ErrorCode + ': ' + meetingResult.ErrorMessage);
557 var list = meetingResult.ReturnValue;
558 meetingList = meetingList.concat(listToArray(list, calendarList[i]));
560 console.info(
"updateData(): meetingList.sort()");
561 meetingList.sort(sortCalendarEntries);
563 // todos don't, they start on
00:
00 hrs., but should be visible anyway
564 // this will generate a list of passed todos. We have to check if they have been marked as
"done" yet
565 if (config['includeTodos'].Value) {
566 var todayTodoList = [];
567 for(var i=
0; i < calendarList.length; i++) {
568 // ignore excluded calendars
569 if (config['excludedCalendars'].Value.indexOf(calendarList[i]) != -
1)
571 var todayTodoListFiltering = {
572 Type:'CalendarEntry',
574 CalendarName: calendarList[i],
576 StartRange: (new Date(now.getFullYear() -
1, now.getMonth(), now.getDate(),
0,
0,
0)),
577 EndRange: (new Date(now.getFullYear(), now.getMonth(), now.getDate(),
0,
0,
1))
580 var todayTodoResult = calendarService.IDataSource.GetList(todayTodoListFiltering);
581 var list = todayTodoResult.ReturnValue;
582 todayTodoList = todayTodoList.concat(listToArray(list, calendarList[i]));
584 console.info(
"updateData(): todayTodoList.sort()");
585 todayTodoList.sort(sortCalendarEntries);
586 var entryLists = [todayTodoList, meetingList];
588 var entryLists = [meetingList];
591 error('loading Calendar items list:' + e + ', line ' + e.line);
600 var fontsize = 'normal';
602 if (config['eventsPerWidget'].Value ==
3) {
604 changeCssClass('.icon', 'width:
20px; height:
20px');
606 else if (config['eventsPerWidget'].Value ==
5) {
608 changeCssClass('.icon', 'width:
10px; height:
10px');
610 else if (config['eventsPerWidget'].Value ==
6) {
612 changeCssClass('.icon', 'width:
8px; height:
8px');
616 changeCssClass('.icon', config['cssStyle_icon'].Value);
617 var entriesHtml = '
<table style=
"font-size:' + fontsize + ';">';
621 max = (panelNum +
1) * config['eventsPerWidget'].Value;
623 max =
30; // we can display a lot more events in fullscreen mode
626 for (var i=
0; i < entryLists.length; i++) {
627 listinfo = listinfo +
" " + entryLists[i].length;
628 var entrieslist =
"";
629 for (var j=
0; j < entryLists[i].length; j++) {
630 entrieslist += entryLists[i][j].Summary +
", ";
632 console.info(
"updateData(): entrieslist: " + entrieslist);
634 console.info(
"updateData(): inner loop, " + entryLists.length +
" lists, [" + listinfo +
"] entries");
636 // the first outer loop iteration is for passed ToDos, the second loop is for all upcomming events (may also include ToDos)
637 for (var i=
0; counter < max && i < entryLists.length; i++) {
638 for (var j=
0; (counter < max) && (j < entryLists[i].length); j++) {
639 entry = entryLists[i][j];
642 // output event info for debugging
643 var entryInfo =
"event: ";
644 for(var k=
0; k < entryFields.length; ++k) {
645 if (entry[entryFields[k]] != undefined) {
646 entryInfo += entryFields[k] +
"=" + entry[entryFields[k]] +
",";
651 // we don't want ToDos when includeTodos == false or when they are completed
652 if (entry.Type == 'ToDo' && (entry.Status ==
"TodoCompleted" || !config['includeTodos'].Value)) {
653 log('skipping ' + entry.id );
658 // make sure that we don't include an event twice (useful for ToDos that might come up twice)
659 if (eventIds[entry.id] ==
1 && entry.Type == 'ToDo') {
660 log('skipped (already included) ' + entry.id);
664 eventIds[entry.id] =
1;
666 // summary can be undefined!
667 var Summary = ((entry.Summary == null) ? '' : entry.Summary);
668 if (entry.Type == 'Meeting' && entry.Location != '' && config['showLocation'].Value)
669 Summary += ', ' + entry.Location;
671 // fix by yves: determine start and end dates/times
672 entryStartTime = ((entry.InstanceStartTime == null) ? entry.StartTime : entry.InstanceStartTime);
673 entryEndTime = ((entry.InstanceEndTime == null) ? entry.EndTime : entry.InstanceEndTime);
675 // there can be ToDos that have no date at all!
676 if (entry.Type == 'ToDo' && entry.EndTime == null)
677 entryDate =
""; // this will cause parseDate(entryDate) to return null;
679 entryDate = ((entry.Type == 'ToDo') ? entryEndTime : entryStartTime); // ToDo's use their EndTime, the rest use StartTime
681 // Convert date/time string to Date object
682 var date = parseDate(entryDate);
683 log('date: ' + date);
684 var endDate = ((entryEndTime == null) ? null : parseDate(entryEndTime));
685 log('endDate: ' + endDate);
687 // check if meeting event has already passed
688 if (entry.Type == 'Meeting') {
689 var compareTime = ((endDate == null) ? date.getTime() : endDate.getTime());
690 if (now.getTime()
> compareTime) {
691 log('skipping Meeting (already passed) ' + entry.id);
693 eventIds[entry.id] =
0;
698 // check if anniversary passed (not sure why they are in the list, the query was only for today - nokia?)
699 if (entry.Type == 'Anniversary') {
700 var tmp = new Date(now.getFullYear(), now.getMonth(), now.getDate(),
0,
0,
0);
701 if (date.getTime() < tmp.getTime()) {
702 log('skipping Anniversary (already passed) ' + entry.id);
704 eventIds[entry.id] =
0;
709 // fix DayEvents end time. End times are off by
1 Second. It's possible that the event has already passed
710 if (entry.Type == 'DayEvent' && endDate != null) {
711 endDate.setMinutes(endDate.getMinutes() -
1);
712 log('fixing DayEvent endDate: ' + endDate);
713 if (now.getTime()
> endDate.getTime()) {
714 log('event already passed ' + entry.id);
716 eventIds[entry.id] =
0;
721 // check if the event is currently taking place
722 if (entryStartTime != null && entryEndTime != null && date != null && endDate != null) {
723 // check if we are between start and endtime
724 if ((date.getTime() < now.getTime()) && (now.getTime() < endDate.getTime())) {
725 date = now; // change appointment date/time to now
726 log('event is currently taking place: ' + date);
730 // skip events for the first panel in case this is the second one and we're not in fullscreen mode
731 if (mode ==
0 && panelNum
> 0 && counter < panelNum * config['eventsPerWidget'].Value +
1) {
732 log('skipping (already in first widget) ' + entry.id);
736 // mark overdue todos
738 if (entry.Type == 'ToDo' && date != null) {
739 var tmp1 = new Date(date.getFullYear(), date.getMonth(), date.getDate(),
0,
0,
0);
740 var tmp2 = new Date(now.getFullYear(), now.getMonth(), now.getDate(),
0,
0,
0);
741 if (tmp1.getTime() < tmp2.getTime()) {
746 // generate html output
747 entriesHtml += '
<tr>';
748 if (config['showCalendarIndicator'].Value && calendarList.length - config['excludedCalendars'].Value.length
> 1) {
749 entriesHtml += '
<td><span class=
"calendar' + calendarColors[entry.CalendarName] + '"> </span></td>';
751 entriesHtml += '
<td><img class=
"icon" src=
"' + entry.Type + '.png" /></td>';
753 // some languages have very strange locale date formats, can't parse all those. Also some todos don't have dates at all.
754 entriesHtml += '
<td colspan=
"4"><span class=
"date">' + entryDate + '
</span> ';
756 var weekDay = date.toLocaleDateString().substr(
0,config['weekDayLength'].Value);
757 var time = formatTime(date);
758 var dateStr = formatDate(date, entryDate);
759 if (entry.Type == 'ToDo' && overdue && config['markOverdueTodos'].Value) {
760 dateStr = '
<span class=
"overdue">' + config['overdueText'].Value + '
</span>';
761 entriesHtml += '
<td colspan=
"4" width=
"1px"><span class=
"date">' + dateStr + '
</span> ';
762 } else if (entry.Type == 'ToDo' || entry.Type == 'Anniversary' || entry.Type == 'DayEvent' || entry.Type == 'Reminder') {
763 if ((isToday(date) || isTomorrow(date)) && config['showTodayAsText'].Value) // show weekday if the date string is not text. looks odd otherwise
764 entriesHtml += '
<td colspan=
"4" width=
"1px"><span class=
"date">' + dateStr + '
</span> ';
766 entriesHtml += '
<td class=
"weekDay" width=
"1px">' + weekDay + '
</td><td width=
"1px" class=
"date">' + dateStr + '
</td><td colspan=
"2">';
767 } else if (entry.Type == 'Meeting') {
768 if (config['showCombinedDateTime'].Value) {
770 entriesHtml += '
<td width=
"1px" colspan=
"4"><span class=
"today">' + time + '
</span> ';
771 else if (isTomorrow(date))
772 entriesHtml += '
<td width=
"1px" colspan=
"4"><span class=
"tomorrow">' + dateStr + '
</span> <span class=
"time">' + time + '
</span> ';
774 entriesHtml += '
<td width=
"1px" class=
"weekDay">' + weekDay + '
</td><td width=
"1px" class=
"date">' + dateStr + '
</td><td colspan=
"2">';
776 if ((isToday(date) || isTomorrow(date)) && config['showTodayAsText'].Value)
777 entriesHtml += '
<td colspan=
"4" width=
"1px"><span class=
"today">' + dateStr + '
</span> <span class=
"time">' + time + '
</span> ';
779 entriesHtml += '
<td width=
"1px" class=
"weekDay">' + weekDay + '
</td><td width=
"1px" class=
"date">' + dateStr + '
</td><td width=
"1px" class=
"time">' + time + '
</td><td>';
783 entriesHtml += '
<span class=
"description">' + Summary + '
</span></td></tr>';
786 entriesHtml += '
</table>';
787 if (config['showNothingText'].Value && entriesHtml == '
<table></table>') {
788 var text = config['nothingText'].Value.replace(/%d/, config['monthRange'].Value);
789 entriesHtml = '
<div style=
"width:295px; height:75px; text-align:center; line-height:75px; overflow:visible;">' + text + '
</div>';
791 if (cacheEntriesHtml != entriesHtml) {
793 document.getElementById('calendarList').innerHTML = entriesHtml;
795 document.getElementById('fullscreenCalendarList').innerHTML = entriesHtml;
796 cacheEntriesHtml = entriesHtml;
799 lastUpdateTime = new Date();
801 error('displaying list:' + e + ', line ' + e.line);
806 function updateScreen()
808 // check if opening fullscreen
809 if( window.innerHeight
> 91 && mode ==
0) {
811 cacheEntriesHtml = '';
812 document.getElementById('body').style.backgroundImage =
"";
815 else if (window.innerHeight <=
91 && mode !=
0) {
817 cacheEntriesHtml = '';
826 var time = new Date();
827 if (time.getTime() - lastUpdateTime.getTime()
> config['updateDataInterval'].Value *
60 *
1000) {
828 log('updateScreen(): force updateData() because last update was too long ago (' + (time.getTime() - lastUpdateTime.getTime()) /
1000 + 's)');
831 setUpdateTimer(); // reinitialize update timer
835 function launchCalendar()
838 widget.openApplication(config['calendarApp'].Value,
"");
839 if (config['hideWidgetOnCalendarOpen'].Value)
842 error('starting Calendar App');
849 log('New widget instance starting up...');
852 // call calendar service
853 if (device !=
"undefined")
854 calendarService = device.getServiceObject(
"Service.Calendar",
"IDataSource");
856 throw('device object does not exist');
858 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>');
862 calendarList = listCalendars();
864 updateCalendarColors();
867 requestNotification();
868 document.getElementById(
"settingsTitle").innerHTML = getLocalizedText('menu.settings');
870 if (window.innerHeight
> 91) {
871 mode =
0; // we're starting fullscreen, we set mode to homescreen in order to let updateScreen() do all the work for us
876 log(
"init(): updateScreen()");
878 if (config['useBackgroundImage'].Value)
879 // check for screen rotation every
1 secs
880 screenRotationTimer = window.setInterval('updateScreen()',
1000 *
1);
881 log(
"init(): finished...");
884 function setUpdateTimer()
886 updateTimer = window.setInterval('updateTimerCallback()',
1000 *
60 * config['updateDataInterval'].Value);
889 function clearUpdateTimer()
891 window.clearInterval(updateTimer);
894 function updateTimerCallback()
896 log(
"updateTimerCallback()");
900 function createMenu()
902 window.menu.setLeftSoftkeyLabel(
"",null);
903 window.menu.setRightSoftkeyLabel(
"",null);
905 var menuSettings = new MenuItem(getLocalizedText('menu.settings'), id++);
906 var menuCallApp = new MenuItem(getLocalizedText('menu.openCalendarApp'), id++);
907 var menuHelp = new MenuItem(getLocalizedText('menu.help'), id++);
908 var menuUpdate = new MenuItem(getLocalizedText('menu.update'), id++);
909 var menuAbout = new MenuItem(getLocalizedText('menu.about'), id++);
910 menuSettings.onSelect = showSettings;
911 menuAbout.onSelect = showAbout;
912 menuCallApp.onSelect = launchCalendar;
913 menuUpdate.onSelect = showUpdate;
914 menuHelp.onSelect = showHelp;
916 window.menu.append(menuCallApp);
917 window.menu.append(menuSettings);
918 window.menu.append(menuHelp);
919 window.menu.append(menuUpdate);
920 window.menu.append(menuAbout);
923 function showSettings()
927 document.getElementById(
"settingsView").style.display =
"block";
928 document.onclick = null;
930 window.menu.setLeftSoftkeyLabel(getLocalizedText('settings.save'), function()
932 for (var key in config) {
933 if (config[key].Type == 'String')
934 config[key].Value = document.forms[
0].elements[
"settings." + key].value;
935 else if (config[key].Type == 'Int') {
936 config[key].Value = parseInt(document.forms[
0].elements[
"settings." + key].value);
937 if (config[key].Value <
0)
938 config[key].Value = config[key].Default;
940 else if (config[key].Type == 'Bool')
941 config[key].Value = document.forms[
0].elements[
"settings." + key].checked;
942 else if (config[key].Type == 'UID')
943 config[key].Value = parseInt(document.forms[
0].elements[
"settings." + key].value);
944 else if (config[key].Type == 'Enum') {
945 config[key].Value = document.forms[
0].elements[
"settings." + key].value;
946 if (config[key].ValidValues.indexOf(config[key].Value) == -
1)
947 config[key].Value = config[key].Default;
949 else if (config[key].Type == 'Array') {
950 if (key == 'excludedCalendars') {
951 config[key].Value = new Array();
952 for(var i=
0; i < calendarList.length; i++) {
953 var element = document.forms[
0].elements[
"settings." + key +
"." + calendarList[i]];
954 if (element != null && element.checked == false)
955 config[key].Value.push(calendarList[i]);
968 window.menu.setRightSoftkeyLabel(getLocalizedText('settings.cancel'), function()
974 var settingsHtml = '
<form>';
975 for (var key in config) {
976 if (config[key].Type == 'String') {
978 if (key.substring(
0,
9) ==
"cssStyle_")
979 prefix = getLocalizedText('settings.cssStyle_prefix');
980 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 />';
982 else if (config[key].Type == 'Int')
983 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 />';
984 else if (config[key].Type == 'Bool')
985 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 />';
986 else if (config[key].Type == 'UID')
987 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 />';
988 else if (config[key].Type == 'Enum') {
989 settingsHtml += '
<table><tr><td>' + getLocalizedText('settings.name.' + key) + '
<br /><select name=
"settings.' + key + '" size=
"1">';
990 for(var i =
0; i < config[key].ValidValues.length; i++)
991 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>';
992 settingsHtml += '
</select></div></td>' + printHintBox(getLocalizedText('settings.info.' + key)) + '
<hr />';
994 else if (config[key].Type == 'Array') {
995 settingsHtml += '
<table><tr><td>' + getLocalizedText('settings.name.' + key) + '
<br />';
996 if (key == 'excludedCalendars') {
997 for(var i=
0; i < calendarList.length; i++) {
998 var checked = '
checked=
"checked"';
999 if (config[key].Value.indexOf(calendarList[i]) != -
1)
1001 settingsHtml += '
<input name=
"settings.' + key + '.' + calendarList[i] + '" type=
"checkbox" value=
"' + calendarList[i] + '" ' + checked + '
/> ' + calendarList[i] + '
<br />';
1004 settingsHtml += '
</td>' + printHintBox(getLocalizedText('settings.info.' + key)) + '
<hr />';
1007 settingsHtml += '
<input name=
"reset" type=
"button" value=
"' + getLocalizedText('settings.restoreDefaults') + '" onclick=
"javascript:restoreDefaultSettings();showSettings();" />';
1008 settingsHtml += '
</form>';
1009 document.getElementById(
"settingsList").innerHTML = settingsHtml;
1012 function changeCssClass(classname, properties)
1014 for(var i =
0; i < document.styleSheets[
0]['cssRules'].length; i++)
1016 if (document.styleSheets[
0]['cssRules'][i].selectorText == classname) {
1017 document.styleSheets[
0].deleteRule(i);
1018 document.styleSheets[
0].insertRule(classname + ' { ' + properties + ' }', document.styleSheets[
0]['cssRules'].length);
1024 function updateCssClasses()
1026 for(var key in config) {
1027 changeCssClass(getLocalizedText('settings.name.' + key), config[key].Value);
1031 function getSettingsCalEntryId()
1033 if (settingsCalEntryId == null) {
1034 // check if entry already exists
1035 var listFiltering = {
1036 Type:'CalendarEntry',
1038 StartRange: new Date(
2000,
0,
1),
1039 EndRange: new Date(
2000,
0,
1),
1040 SearchText: 'ComingNext Settings|',
1044 var result = calendarService.IDataSource.GetList(listFiltering);
1045 if (result.ErrorCode) {
1046 error(result.ErrorMessage);
1049 var list = result.ReturnValue;
1050 var entry = list.getNext();
1051 if (entry != undefined) {
1052 settingsCalEntryId = entry.LocalId;
1053 log(
"settingsCalEntryId=" + settingsCalEntryId);
1055 else { // create settings item
1056 var item = new Object();
1057 item.Type =
"DayEvent";
1058 item.StartTime = new Date(
2000,
0,
1);
1059 item.Summary =
"ComingNext Settings|";
1061 var criteria = new Object();
1062 criteria.Type =
"CalendarEntry";
1063 criteria.Item = item;
1066 var result = calendarService.IDataSource.Add(criteria);
1067 if (result.ErrorCode)
1068 error(result.ErrorMessage);
1070 error(
"getSettingsCalEntryId: " + e + ', line ' + e.line);
1073 getSettingsCalEntryId();
1078 function restoreDefaultSettings()
1080 for (var key in config)
1081 config[key].Value = config[key].Default;
1084 function loadSettings()
1086 getSettingsCalEntryId();
1087 var listFiltering = {
1088 Type:'CalendarEntry',
1090 LocalId: settingsCalEntryId
1093 var result = calendarService.IDataSource.GetList(listFiltering);
1094 if (result.ErrorCode) {
1095 error(result.ErrorMessage);
1098 var entry = result.ReturnValue.getNext();
1099 if (entry != undefined) {
1100 log(
"Loading Settings...");
1101 // only reload settings if they chanced since the last reload
1102 if (settingsCache != entry.Summary)
1104 restoreDefaultSettings();
1105 var stringlist = entry.Summary.split(
"|");
1106 // skip the first two entries, those contain header and version info
1107 for(var i =
2; i < stringlist.length -
1; i++) {
1108 var pair = stringlist[i].split('=');
1110 var value = pair[
1];
1111 log('stringlist: ' + key + '=\'' + value + '\'');
1112 if (config[key].Type == 'Int')
1113 config[key].Value = Number(value);
1114 else if (config[key].Type == 'String')
1115 config[key].Value = value;
1116 else if (config[key].Type == 'Bool')
1117 config[key].Value = (value == 'true')
1118 else if (config[key].Type == 'Enum')
1119 config[key].Value = value;
1120 else if (config[key].Type == 'UID')
1121 config[key].Value = Number(value);
1122 else if (config[key].Type == 'Array') {
1123 config[key].Value = value.split(
"^");
1124 if (config[key].Value.length ==
1 && config[key].Value[
0] ==
"") {
1125 config[key].Value = [];
1129 settingsCache = entry.Summary;
1133 log(
"Settings already cached and did not change");
1137 error(
"Failed to load settings, calendar entry could not be found");
1141 function saveSettings()
1143 getSettingsCalEntryId();
1144 var item = new Object();
1145 item.Type =
"DayEvent";
1146 item.StartTime = new Date(
2000,
0,
1);
1147 item.LocalId = settingsCalEntryId;
1148 item.Summary =
"ComingNext Settings|" + version +
"|";
1150 for (var key in config) {
1151 if (config[key].Type == 'Int')
1152 item.Summary += key +
"=" + config[key].Value.toString() +
"|";
1153 else if (config[key].Type == 'String')
1154 item.Summary += key +
"=" + config[key].Value +
"|";
1155 else if (config[key].Type == 'Bool')
1156 item.Summary += key +
"=" + (config[key].Value ? 'true' : 'false') +
"|";
1157 else if (config[key].Type == 'Enum')
1158 item.Summary += key +
"=" + config[key].Value +
"|";
1159 else if (config[key].Type == 'UID')
1160 item.Summary += key +
"=" + config[key].Value.toString() +
"|";
1161 else if (config[key].Type == 'Array')
1162 item.Summary += key +
"=" + config[key].Value.join(
"^") +
"|";
1164 settingsCache = item.Summary;
1166 var criteria = new Object();
1167 criteria.Type =
"CalendarEntry";
1168 criteria.Item = item;
1170 log(
"Saving settings to calendar entry: " + item.Summary);
1172 var result = calendarService.IDataSource.Add(criteria);
1173 if (result.ErrorCode)
1174 error(result.ErrorMessage);
1176 error(
"saveSettings: " + e + ', line ' + e.line);
1183 function toggleVisibility(elementId)
1185 if (document.getElementById(elementId).style.display ==
"none")
1186 document.getElementById(elementId).style.display =
"block";
1188 document.getElementById(elementId).style.display =
"none";
1192 function printHintBox(text)
1195 return '
<td width=
"1%" align=
"right" onclick=
"javascript:toggleVisibility(\'info' + uniqueId + '\')">' + getLocalizedText('settings.help') + '
</td></tr></table>'+
1196 '
<div class=
"settingsInfo" id=
"info' + uniqueId + '">' + text + '
</div>';
1199 function showAbout()
1203 document.getElementById(
"aboutView").style.display =
"block";
1204 document.onclick = null;
1206 window.menu.setLeftSoftkeyLabel(
" ", function(){});
1207 window.menu.setRightSoftkeyLabel(getLocalizedText('softkey.back'), function()
1213 //document.getElementById(
"aboutView").innerHTML = 'aboutView';
1214 document.getElementById(
"name").innerHTML =
"Coming Next " + version;
1217 function showHelp() {
1218 widget.openURL('http://comingnext.sf.net/help');
1221 function updateFullscreen()
1225 function showFullscreen()
1227 log(
"showFullscreen()");
1229 document.getElementById(
"fullscreenView").style.display =
"block";
1230 document.getElementById('body').className =
"backgroundFullscreen";
1232 document.onclick = launchCalendar;
1237 function getBackgroundImage()
1242 if (config['backgroundImageLocation'].Value == config['backgroundImageLocation'].ValidValues[
0]) // internal
1243 bgImage = 'background_' + orientation + '.png';
1245 bgImage = 'C:/Data/background_' + panelNum + '_' + orientation + '.png';
1249 function updateHomescreen()
1251 if (config['useBackgroundImage'].Value) {
1252 // check for screen rotation
1253 if (orientation != 'portrait' && screen.width ==
360 && screen.height ==
640) {
1254 window.widget.prepareForTransition(
"fade");
1255 orientation = 'portrait';
1256 document.getElementById('body').style.backgroundImage = 'url(' + getBackgroundImage() + ')';
1257 document.getElementById('body').style.backgroundColor = 'none';
1258 window.widget.performTransition();
1259 } else if (orientation != 'landscape' && screen.width ==
640 && screen.height ==
360) {
1260 window.widget.prepareForTransition(
"fade");
1261 orientation = 'landscape';
1262 document.getElementById('body').style.backgroundImage = 'url(' + getBackgroundImage() + ')';
1263 document.getElementById('body').style.backgroundColor = 'none';
1264 window.widget.performTransition();
1266 else if (document.getElementById('body').style.backgroundImage ==
"")
1268 document.getElementById('body').style.backgroundImage = 'url(' + getBackgroundImage() + ')';
1273 function showHomescreen()
1275 log(
"showHomescreen()");
1277 document.getElementById(
"homescreenView").style.display =
"block";
1278 document.getElementById('body').className =
"background";
1279 document.onclick = null;
1283 function getLocalizedText(p_Txt)
1285 if (localizedText[p_Txt])
1286 return localizedText[p_Txt];
1288 return 'ERROR: missing translation for ' + p_Txt;
1291 function showUpdate()
1295 document.getElementById(
"updateView").style.display =
"block";
1296 document.onclick = null;
1298 window.menu.setLeftSoftkeyLabel(getLocalizedText('update.checknow'), function(){
1301 window.menu.setRightSoftkeyLabel(getLocalizedText('softkey.back'), function()
1307 document.getElementById(
"currentVersion").innerHTML = getLocalizedText(
"update.current") + version;
1311 function checkForUpdate()
1313 // asynch XHR to server url
1314 reqV = new XMLHttpRequest();
1315 reqV.onreadystatechange = checkForUpdateCallback;
1316 document.getElementById(
"updateDiv").innerHTML = getLocalizedText(
"update.checking");
1317 reqV.open(
"GET", versionURL, true);
1318 reqV.setRequestHeader(
"If-Modified-Since",
"Sat, 1 Jan 2000 00:00:00 GMT" ); // disable caching
1322 function checkForUpdateCallback()
1324 if (reqV.readyState ==
4) {
1325 if (reqV.status ==
200) {
1326 var resultXml = reqV.responseText;
1328 var div = document.getElementById(
"tmp");
1329 div.innerHTML = resultXml;
1330 var newVersion = div.getElementsByTagName('version')[
0].innerHTML;
1331 var newVersionURL = div.getElementsByTagName('url')[
0].innerHTML;
1333 if (version != newVersion) {
1334 document.getElementById(
"updateDiv").innerHTML = getLocalizedText(
"update.download").replace(/%
1/, newVersion).replace(/%
2/, newVersionURL);
1337 document.getElementById(
"updateDiv").innerHTML = getLocalizedText(
"update.nonewversion");
1342 document.getElementById(
"updateDiv").innerHTML = getLocalizedText(
"update.error") + reqV.status +
" " + reqV.responseText;
1347 function hideViews()
1349 document.getElementById(
"homescreenView").style.display =
"none";
1350 document.getElementById(
"fullscreenView").style.display =
"none";
1351 document.getElementById(
"aboutView").style.display =
"none";
1352 document.getElementById(
"settingsView").style.display =
"none";
1353 document.getElementById(
"updateView").style.display =
"none";
1356 function listCalendars()
1362 DefaultCalendar: false
1366 var calendarsResult = calendarService.IDataSource.GetList(criteria);
1367 if (calendarsResult.ErrorCode !=
0)
1368 throw(
"Error fetching list of calendars: " + calendarsResult.ErrorCode + ': ' + calendarsResult.ErrorMessage);
1369 var calendarListIterator = calendarsResult.ReturnValue;
1374 while (( item = calendarListIterator.getNext()) != undefined ) {
1375 calendars[count++] = item;
1377 log(
"Available Calendars: " + calendars.join(
", "));
1380 error('listing calendars:' + e + ', line ' + e.line);
1385 // Copies all objects and their properties to an array. Data is copied so nothing gets lost when the reference is removed
1386 // 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
1387 function listToArray(list, calendarName)
1389 var array = new Array();
1392 while (( item = list.getNext()) != undefined ) {
1393 var itemCopy = new Object();
1394 for(var i=
0; i < entryFields.length; i++) {
1395 itemCopy[entryFields[i]] = item[entryFields[i]];
1397 // for some reason, the CalendarName property is never correctly queried, so we assign it manually here
1398 if (!itemCopy['CalendarName']) {
1399 itemCopy['CalendarName'] = calendarName;
1401 array.push(itemCopy);
1402 txt += array[array.length -
1].Summary +
", ";
1404 log(
"listToArray(): " + txt);
1408 function sortCalendarEntries(a, b)
1411 log(
"sortCalendarEntries(" + a.Summary +
"," + b.Summary +
")");
1413 if (a.InstanceStartTime != null) {
1414 atime = a.InstanceStartTime;
1416 else if (a.StartTime != null) {
1417 atime = a.StartTime;
1419 else if (a.InstanceEndTime != null) {
1420 atime = a.InstanceEndTime;
1422 else if (a.EndTime != null) {
1426 if (b.InstanceStartTime != null) {
1427 btime = b.InstanceStartTime;
1429 else if (b.StartTime != null) {
1430 btime = b.StartTime;
1432 else if (b.InstanceEndTime != null) {
1433 btime = b.InstanceEndTime;
1435 else if (b.EndTime != null) {
1439 if (atime && btime) {
1441 atime = parseDate(atime);
1442 btime = parseDate(btime);
1444 // sort by date & time
1445 if (atime < btime) {
1448 else if (atime
> btime) {
1452 else if (a.Type != b.Type) {
1453 if (a.Type < b.Type) {
1456 else if (a.Type
> b.Type) {
1460 // sort by description
1461 else if (a.Summary && b.Summary && a.Summary != b.Summary) {
1462 if (a.Summary < b.Summary) {
1465 else if (a.Summary
> b.Summary) {
1470 // NOTE: events my have no date information at all. In that case, we list events without date first
1471 else if (atime && !btime) {
1474 else if (!atime && btime) {
1477 else if (!atime && !btime) {
1479 if (a.Type != b.Type) {
1480 if (a.Type < b.Type) {
1483 else if (a.Type
> b.Type) {
1487 // sort by description
1488 else if (a.Summary && b.Summary && a.Summary != b.Summary) {
1489 if (a.Summary < b.Summary) {
1492 else if (a.Summary
> b.Summary) {
1501 function updateCalendarColors()
1504 calendarColors = [];
1505 if (calendarList.length
> maxColors) {
1506 log(
"updateCalendarColors(): Warning: more calendars than available indicator colors");
1508 for(var i=
0; i < calendarList.length; i++) {
1509 calendarColors[calendarList[i]] = (i % maxColors) +
1;
1513 function log(message) {
1514 if (config['enableLogging'].Value) {
1515 console.info(message);
1521 <style type=
"text/css">
1523 table { margin:
0px; padding:
0px; border-spacing:
0px; }
1524 td { padding:
0px
5px
0px
0px; white-space:nowrap; overflow:hidden; }
1525 hr { color:#ffffff; background-color:#ffffff; height:
1px; text-align:left; border-style:none; }
1526 .settingsInfo { display:none; font-style:italic; }
1527 .title { font-weight:bold; font-size:
14pt; }
1528 .textInput { width:
90%; }
1529 .credits { margin-left:
40px; text-indent: -
20px; margin-bottom:
0px; }
1530 #homescreenView { width:
315px; height:
91px; overflow:hidden; }
1531 #calendarList { position:absolute; left:
5px; top:
4px; width:
295px; height:
75px; overflow:hidden; }
1532 #name { text-align:center; }
1533 #appicon { display: block; margin-left: auto; margin-right: auto; margin-top:
10px; }
1534 #smallappicon { width:
22px; height:
22px; margin-right:
10px; float:left; }
1539 <body id=
"body" class=
"background">
1540 <div id=
"homescreenView">
1541 <div id=
"calendarList"></div>
1543 <div id=
"fullscreenView" style=
"display:none;">
1544 <img src=
"Icon.png" id=
"smallappicon">
1545 <h1 class=
"title">Coming Next
</h1>
1547 <div id=
"fullscreenCalendarList">loading...
</div>
1549 <div id=
"settingsView" style=
"display:none">
1550 <img src=
"Icon.png" id=
"smallappicon">
1551 <h1 id=
"settingsTitle" class=
"title">Settings
</h1>
1553 <div id=
"settingsList"></div>
1555 <div id=
"aboutView" style=
"display:none">
1556 <img src=
"Icon.png" id=
"appicon">
1557 <h1 id=
"name">Coming Next
</h1>
1559 <p>Created by Dr. Cochambre and Michael Prager.
</p>
1560 <p>Contributions:
</p>
1561 <p class=
"credits">Paul Moore (bug fixes, new features and code cleanup)
</p>
1562 <p class=
"credits">Manfred Hanselmann (DST support)
</p>
1563 <p class=
"credits">Christophe Milsent (translation support & french translation)
</p>
1564 <p class=
"credits">Flavio Nathan (portuguese-brazilian translation)
</p>
1565 <p class=
"credits">Tokeda (russian translation)
</p>
1566 <p class=
"credits">Marcella Ferrari (italian translation)
</p>
1567 <p class=
"credits">Venos (italian translation)
</p>
1568 <p>This software is open source and licensed under the GPLv3.
</p>
1569 <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>
1572 <div id=
"updateView" style=
"display:none">
1573 <img src=
"Icon.png" id=
"smallappicon">
1574 <h1 class=
"title">Check for update
</h1>
1576 <div id=
"currentVersion">Coming Next ??
</div>
1577 <div id=
"updateDiv"></div>
1578 <div id=
"tmp" style=
"display:none;"></div>