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 cssStyle_background: { Type: 'String', Default: 'color:#ffffff; background-color:#
000000', Value: 'color:#ffffff; background-color:#
000000',},
61 cssStyle_backgroundFullscreen: { Type: 'String', Default: 'color:#ffffff; background-color:#
000000', Value: 'color:#ffffff; background-color:#
000000',},
62 cssStyle_weekDay: { Type: 'String', Default: '', Value: '',},
63 cssStyle_date: { Type: 'String', Default: '', Value: '',},
64 cssStyle_today: { Type: 'String', Default: 'color:#ff0000', Value: 'color:#ff0000',},
65 cssStyle_tomorrow: { Type: 'String', Default: 'color:#
0000ff', Value: 'color:#
0000ff',},
66 cssStyle_time: { Type: 'String', Default: '', Value: '',},
67 cssStyle_now: { Type: 'String', Default: 'color:#ff00ff', Value: 'color:#ff00ff',},
68 cssStyle_description: { Type: 'String', Default: '', Value: '',},
69 cssStyle_icon: { Type: 'String', Default: 'width:
15px; height:
15px', Value: 'width:
15px; height:
15px',},
70 cssStyle_overdue: { Type: 'String', Default: 'color:#ffff00', Value: 'color:#ffff00',},
71 cssStyle_calendar1: { Type: 'String', Default: 'background-color:#
0757cf', Value: 'background-color:#
0757cf',},
72 cssStyle_calendar2: { Type: 'String', Default: 'background-color:#
579f37', Value: 'background-color:#
579f37',},
73 cssStyle_calendar3: { Type: 'String', Default: 'background-color:#ff9f07', Value: 'background-color:#ff9f07',},
74 cssStyle_calendar4: { Type: 'String', Default: 'background-color:#af8fef', Value: 'background-color:#af8fef',},
75 cssStyle_calendar5: { Type: 'String', Default: 'background-color:#
57afbf', Value: 'background-color:#
57afbf',},
76 cssStyle_calendar6: { Type: 'String', Default: 'background-color:#
9fdf57', Value: 'background-color:#
9fdf57',},
81 //-------------------------------------------------------
82 // Nothing of interest from here on...
83 //-------------------------------------------------------
84 var panelNum =
0; // use
1 for second panel
86 var versionURL =
"http://comingnext.sourceforge.net/version.xml";
87 var calendarService = null;
88 var cacheEntriesHtml = [];
89 var months_translated = [];
92 var mode =
0; //
0 = homescreen,
1 = fullscreen,
2 = settings,
3 = about,
4 = check for update
94 var settingsCalEntryId = null;
95 var settingsCache = null;
96 var notificationRequests = new Array();
97 var calendarList = [];
98 var calendarColors = [];
99 var updateTimer = null;
100 var screenRotationTimer = null;
101 var lastUpdateTime = now;
103 // vars for daylight saving time
104 var summertime = false; // true, if current date is in summer, false if in winter
105 var daylightSavingDates = new Object(); // caches calculated DST winter and summer time shift dates
107 // this is a list of data fields a calendar event can have
121 window.onload = init;
122 window.onresize = updateScreen;
123 window.onshow = updateScreen;
125 function isLeapYear( year ) {
126 if (( year %
4 ==
0 && year %
100 !=
0 ) || year %
400 ==
0 )
132 function calcLeapYear(year, days)
134 if (isLeapYear(year))
140 function subToSunday(myDate, year, days, prevMonthDays)
142 for (i = myDate.getDay(); i
> 0 ;i--)
144 days -= prevMonthDays;
145 days = isLeapYear(year) ? --days : days;
149 function isSummertime(curDate)
154 // if we already calculated DST summer and winter time dates for this year, use cached values
155 var dst = daylightSavingDates[curDate.getFullYear()];
157 var thisYearS = new Date(curDate.getFullYear(),
3,
0,
0,
0,
0 );
158 var thisYearW = new Date(curDate.getFullYear(),
10,
0,
0,
0,
0 );
159 var nextYearS = new Date(curDate.getFullYear() +
1,
3,
0,
0,
0,
0 );
160 var nextYearW = new Date(curDate.getFullYear() +
1,
10,
0,
0,
0,
0 );
162 thisYearSDays = nextYearSDays =
90;
163 thisYearWDays = nextYearWDays =
304;
165 thisYearSDays = calcLeapYear(curDate.getFullYear(), thisYearSDays);
166 thisYearWDays = calcLeapYear(curDate.getFullYear(), thisYearWDays);
167 nextYearSDays = calcLeapYear(curDate.getFullYear() +
1, nextYearSDays);
168 nextYearWDays = calcLeapYear(curDate.getFullYear() +
1, nextYearWDays);
170 thisYearSDays = subToSunday(thisYearS, curDate.getFullYear(), thisYearSDays,
59);
171 thisYearWDays = subToSunday(thisYearW, curDate.getFullYear(), thisYearWDays,
273);
172 nextYearSDays = subToSunday(nextYearS, curDate.getFullYear() +
1, nextYearSDays,
59);
173 nextYearWDays = subToSunday(nextYearW, curDate.getFullYear() +
1, nextYearWDays,
273);
176 Summer: new Date (curDate.getFullYear(),
03-
1, thisYearSDays,
2,
0,
0),
177 Winter: new Date (curDate.getFullYear(),
10-
1, thisYearWDays,
2,
0,
0),
179 daylightSavingDates[curDate.getFullYear()] = dst;
182 if (dst.Summer < curDate)
184 if (dst.Winter < curDate)
186 if (summer && !winter)
192 function error(message)
194 console.info('Error: ' + message);
195 document.getElementById(
"calendarList").innerHTML = 'Error: ' + message;
198 function areDatesEqual(date1, date2)
200 return (date1.getFullYear() == date2.getFullYear() &&
201 date1.getMonth() == date2.getMonth() &&
202 date1.getDate() == date2.getDate());
205 function isTomorrow(date)
207 // tommorow = now +
1 day
208 // ToDo: some days can be shorter as
24 hours(daylight saving change day)
209 return areDatesEqual(date, new Date (now.getTime() +
24*
60*
60*
1000));
212 function isToday(date)
214 return areDatesEqual(date, now);
217 function collectLocales()
219 var tmpyear =
2000 + panelNum;
222 if (months_translated.length
> 0)
224 for (month =
0; month <
12; month++) {
225 var startDate = new Date(tmpyear, month,
15);
227 var item = new Object();
228 item.Type =
"DayEvent";
229 item.StartTime = startDate;
230 item.Summary =
"__temp" + month;
232 var criteria = new Object();
233 criteria.Type =
"CalendarEntry";
234 criteria.Item = item;
237 var result = calendarService.IDataSource.Add(criteria);
238 if (result.ErrorCode)
239 error(result.ErrorMessage);
241 error(
"collectLocales: " + e + ', line ' + e.line);
245 var startTime = new Date(tmpyear,
0,
1);
246 var endTime = new Date(tmpyear,
11,
31);
247 var listFiltering = {
248 Type:'CalendarEntry',
250 StartRange: startTime,
252 SearchText: '__temp',
256 var result = calendarService.IDataSource.GetList(listFiltering);
257 if (result.ErrorCode) {
258 error(result.ErrorMessage);
261 var list = result.ReturnValue;
263 error(e + ', line ' + e.line);
266 var ids = new Array();
272 while (list && (entry = list.getNext()) != undefined) {
273 dateArr = entry.StartTime.replace(/,/g,'').replace(/\./g,':').replace(/ /g,' ').split(' ');
274 var day = dateArr[
1];
275 var month = dateArr[
2];
276 var year = dateArr[
3];
278 // make sure month is set properly
279 if (isNaN(parseInt(day))) {
283 } else if (isNaN(parseInt(year))) {
289 console.info(entry.StartTime + ' -
> ' + month + ' ' + counter);
290 ids[counter] = entry.id;
291 months_translated[month] = counter +
1;
295 error(e + ', line ' + e.line);
300 var criteria = new Object();
301 criteria.Type =
"CalendarEntry";
306 var result = calendarService.IDataSource.Delete(criteria);
307 if (result.ErrorCode)
308 error(result.ErrorMessage);
310 error('deleting temp calendar entries:' + e + ', line ' + e.line);
315 function requestNotification()
317 var criteria = new Object();
318 criteria.Type =
"CalendarEntry";
319 criteria.Filter = new Object();
320 for(var i=
0; i < calendarList.length; i++) {
321 criteria.Filter.CalendarName = calendarList[i];
323 var notificationRequest = calendarService.IDataSource.RequestNotification(criteria, callback);
324 if (notificationRequest.ErrorCode)
325 error('requestNotification failed with error code ' + notificationRequest.ErrorCode);
326 notificationRequests.push(notificationRequest);
328 error(
"requestNotification: " + e + ', line ' + e.line);
332 var criteria2 = new Object();
333 criteria2.Type =
"CalendarEntry";
334 criteria2.Filter = new Object();
335 criteria2.Filter.LocalIdList = new Array();
336 criteria2.Filter.LocalIdList[
0] = settingsCalEntryId;
338 var notificationRequest = calendarService.IDataSource.RequestNotification(criteria2, settingsCallback);
339 if (notificationRequest.ErrorCode)
340 error('requestNotification failed with error code ' + notificationRequest.ErrorCode);
341 notificationRequests.push(notificationRequest);
343 error(
"requestNotification: " + e + ', line ' + e.line);
347 function cancelNotification()
349 for(var i=
0; i < notificationRequests.length; i++) {
351 var result = calendarService.IDataSource.Cancel(notificationRequests[i]);
352 if (result.ErrorCode)
353 error('cancelNotification failed with error code ' + result.ErrorCode);
355 error(
"cancelNotification: " + e + ', line ' + e.line);
360 function callback(transId, eventCode, result)
362 console.info(
"callback(): panelNum: %d transId: %d eventCode: %d result.ErrorCode: %d", panelNum, transId, eventCode, result.ErrorCode);
366 function settingsCallback(transId, eventCode, result)
368 console.info(
"settingsCallback(): panelNum: %d transId: %d eventCode: %d result.ErrorCode: %d", panelNum, transId, eventCode, result.ErrorCode);
372 function parseDate(dateString)
375 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:
376 Wednesday,
26 August,
2009 24:
00:
00
377 Wednesday,
26 August,
2009 12:
00:
00 am
378 Wednesday, August
26,
2009 12:
00:
00 am
379 Wednesday,
2009 August,
26 12:
00:
00 am
380 Wednesday,
2009 August,
28 8.00.00 pm
381 Wednesday,
2009 August,
28 08:
00:
00 PM
384 if (dateString ==
"" || dateString == null)
386 var dateArr = dateString.replace(/,/g,'').replace(/\./g,':').replace(/ /g,' ').split(' ');
387 if (dateArr.length !=
5 && dateArr.length !=
6)
391 var weekDay = dateArr[
0];
392 var day = dateArr[
1];
393 var month = dateArr[
2];
394 var year = dateArr[
3];
395 // make sure month is set properly
396 if (isNaN(parseInt(day))) {
400 } else if (isNaN(parseInt(year))) {
405 // make sure day and year are set properly
406 if (Number(day)
> Number(year)) {
411 month = months_translated[month];
414 var timeArr = dateArr[
4].split(':');
415 if (timeArr.length !=
3)
417 var hours = Number(timeArr[
0]);
418 var minutes = Number(timeArr[
1]);
419 var seconds = Number(timeArr[
2]);
420 if (dateArr.length ==
6 && dateArr[
5].toLowerCase() == 'pm' && hours <
12)
422 if (dateArr.length ==
6 && dateArr[
5].toLowerCase() == 'am' && hours ==
12)
425 var result = new Date(year, month -
1, day, hours, minutes, seconds);
427 // take care of daylight saving time
428 if (config['enableDaylightSaving'].Value) {
430 // determine if date is in summer or winter time
431 var dateSummerTime = isSummertime(result);
433 // work around bug in Nokias calendar api resulting in dates within a different DST to be off by
1 hour
434 if (summertime && !dateSummerTime) {
435 result = new Date(result.getTime() -
1000 *
60 *
60 * config['daylightSavingOffset'].Value); // -
1 hour
436 console.info('parseDate(): fixing time -
1h: ' + result);
438 else if (!summertime && dateSummerTime) {
439 result = new Date(result.getTime() +
1000 *
60 *
60 * config['daylightSavingOffset'].Value); // +
1 hour
440 console.info('parseDate(): fixing time +
1h: ' + result);
447 // 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"
448 function formatDate(date, format)
450 var day = date.getDate().toString();
451 var month = (date.getMonth() +
1).toString();
452 while (day.length <
2) { day = '
0' + day; }
453 while (month.length <
2) { month = '
0' + month; }
455 if (config['showTodayAsText'].Value && isToday(date))
456 return '
<span class=
"today">' + config['todayText'].Value + '
</span>';
457 if (config['showTodayAsText'].Value && isTomorrow(date))
458 return '
<span class=
"tomorrow">' + config['tomorrowText'].Value + '
</span>';
460 var dateArr = format.replace(/,/g,'').replace(/\./g,':').replace(/ /g,' ').split(' ');
461 if (dateArr.length !=
5 && dateArr.length !=
6) {
462 // we don't know how to format this
463 if (config['dateFormat'].Value == 'auto' || config['dateFormat'].Value == 'DDMM')
464 return day + config['dateSeparator'].Value + month;
466 return month + config['dateSeparator'].Value + day;
470 if (config['dateFormat'].Value == 'MMDD')
472 else if (config['dateFormat'].Value == 'DDMM')
475 // config['dateFormat'].Value == 'auto', try to detect system setting
477 var day_ = dateArr[
1];
478 var month_ = dateArr[
2];
479 var year_ = dateArr[
3];
480 // make sure month is set properly
481 if (isNaN(parseInt(day_))) {
486 } else if (isNaN(parseInt(year_))) {
492 // make sure day and year are set properly
493 if (Number(day_)
> Number(year_))
498 return day + config['dateSeparator'].Value + month;
500 return month + config['dateSeparator'].Value + day;
503 function formatTime(date)
505 // date is a Date() object
506 date.setSeconds(
0); // we don't care about seconds
507 var time = date.toLocaleTimeString().replace(/[\.:]
00/, ''); // remove seconds from string
508 if (time.replace(/\./, ':').split(':')[
0].length <
2)
510 if (config['showNowAsText'].Value && date.getTime() == now.getTime())
511 time = '
<span class=
"now">' + config['nowText'].Value + '
</span>';
515 function updateData()
517 console.info('updateData()');
519 // check if we got additional or less calendars since our last update
520 var newCalendarList = listCalendars();
521 if (newCalendarList.length != calendarList.length) {
522 calendarList = newCalendarList;
523 updateCalendarColors();
524 cancelNotification();
525 requestNotification();
529 // meetings have time
530 // 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
532 summertime = isSummertime(now); // cache summer time info for today
533 var meetingList = [];
534 for(var i=
0; i < calendarList.length; i++) {
535 // ignore excluded calendars
536 if (config['excludedCalendars'].Value.indexOf(calendarList[i]) != -
1)
538 var meetingListFiltering = {
539 Type:'CalendarEntry',
541 CalendarName: calendarList[i],
542 StartRange: (new Date(now.getFullYear(), now.getMonth(), now.getDate(),
0,
0,
0)),
543 EndRange: (new Date(now.getFullYear(), now.getMonth() + config['monthRange'].Value, now.getDate(),
0,
0,
0))
546 var meetingResult = calendarService.IDataSource.GetList(meetingListFiltering);
547 if (meetingResult.ErrorCode !=
0)
548 throw(
"Error fetching calendar data: " + meetingResult.ErrorCode + ': ' + meetingResult.ErrorMessage);
549 var list = meetingResult.ReturnValue;
550 meetingList = meetingList.concat(listToArray(list, calendarList[i]));
552 console.info(
"updateData(): meetingList.sort()");
553 meetingList.sort(sortCalendarEntries);
555 // todos don't, they start on
00:
00 hrs., but should be visible anyway
556 // this will generate a list of passed todos. We have to check if they have been marked as
"done" yet
557 if (config['includeTodos'].Value) {
558 var todayTodoList = [];
559 for(var i=
0; i < calendarList.length; i++) {
560 // ignore excluded calendars
561 if (config['excludedCalendars'].Value.indexOf(calendarList[i]) != -
1)
563 var todayTodoListFiltering = {
564 Type:'CalendarEntry',
566 CalendarName: calendarList[i],
568 StartRange: (new Date(now.getFullYear() -
1, now.getMonth(), now.getDate(),
0,
0,
0)),
569 EndRange: (new Date(now.getFullYear(), now.getMonth(), now.getDate(),
0,
0,
1))
572 var todayTodoResult = calendarService.IDataSource.GetList(todayTodoListFiltering);
573 var list = todayTodoResult.ReturnValue;
574 todayTodoList = todayTodoList.concat(listToArray(list, calendarList[i]));
576 console.info(
"updateData(): todayTodoList.sort()");
577 todayTodoList.sort(sortCalendarEntries);
578 var entryLists = [todayTodoList, meetingList];
580 var entryLists = [meetingList];
583 error('loading Calendar items list:' + e + ', line ' + e.line);
592 var fontsize = 'normal';
594 if (config['eventsPerWidget'].Value ==
3) {
596 changeCssClass('.icon', 'width:
20px; height:
20px');
598 else if (config['eventsPerWidget'].Value ==
5) {
600 changeCssClass('.icon', 'width:
10px; height:
10px');
602 else if (config['eventsPerWidget'].Value ==
6) {
604 changeCssClass('.icon', 'width:
8px; height:
8px');
608 changeCssClass('.icon', config['cssStyle_icon'].Value);
609 var entriesHtml = '
<table style=
"font-size:' + fontsize + ';">';
613 max = (panelNum +
1) * config['eventsPerWidget'].Value;
615 max =
30; // we can display a lot more events in fullscreen mode
618 for (var i=
0; i < entryLists.length; i++) {
619 listinfo = listinfo +
" " + entryLists[i].length;
620 var entrieslist =
"";
621 for (var j=
0; j < entryLists[i].length; j++) {
622 entrieslist += entryLists[i][j].Summary +
", ";
624 console.info(
"updateData(): entrieslist: " + entrieslist);
626 console.info(
"updateData(): inner loop, " + entryLists.length +
" lists, [" + listinfo +
"] entries");
628 // the first outer loop iteration is for passed ToDos, the second loop is for all upcomming events (may also include ToDos)
629 for (var i=
0; counter < max && i < entryLists.length; i++) {
630 for (var j=
0; (counter < max) && (j < entryLists[i].length); j++) {
631 entry = entryLists[i][j];
634 // output event info for debugging
635 var entryInfo =
"event: ";
636 for(var k=
0; k < entryFields.length; ++k) {
637 if (entry[entryFields[k]] != undefined) {
638 entryInfo += entryFields[k] +
"=" + entry[entryFields[k]] +
",";
641 console.info(entryInfo);
643 // we don't want ToDos when includeTodos == false or when they are completed
644 if (entry.Type == 'ToDo' && (entry.Status ==
"TodoCompleted" || !config['includeTodos'].Value)) {
645 console.info('skipping ' + entry.id );
650 // make sure that we don't include an event twice (useful for ToDos that might come up twice)
651 if (eventIds[entry.id] ==
1 && entry.Type == 'ToDo') {
652 console.info('skipped (already included) ' + entry.id);
656 eventIds[entry.id] =
1;
658 // summary can be undefined!
659 var Summary = ((entry.Summary == null) ? '' : entry.Summary);
660 if (entry.Type == 'Meeting' && entry.Location != '' && config['showLocation'].Value)
661 Summary += ', ' + entry.Location;
663 // fix by yves: determine start and end dates/times
664 entryStartTime = ((entry.InstanceStartTime == null) ? entry.StartTime : entry.InstanceStartTime);
665 entryEndTime = ((entry.InstanceEndTime == null) ? entry.EndTime : entry.InstanceEndTime);
667 // there can be ToDos that have no date at all!
668 if (entry.Type == 'ToDo' && entry.EndTime == null)
669 entryDate =
""; // this will cause parseDate(entryDate) to return null;
671 entryDate = ((entry.Type == 'ToDo') ? entryEndTime : entryStartTime); // ToDo's use their EndTime, the rest use StartTime
673 // Convert date/time string to Date object
674 var date = parseDate(entryDate);
675 console.info('date: ' + date);
676 var endDate = ((entryEndTime == null) ? null : parseDate(entryEndTime));
677 console.info('endDate: ' + endDate);
679 // check if meeting event has already passed
680 if (entry.Type == 'Meeting') {
681 var compareTime = ((endDate == null) ? date.getTime() : endDate.getTime());
682 if (now.getTime()
> compareTime) {
683 console.info('skipping Meeting (already passed) ' + entry.id);
685 eventIds[entry.id] =
0;
690 // check if anniversary passed (not sure why they are in the list, the query was only for today - nokia?)
691 if (entry.Type == 'Anniversary') {
692 var tmp = new Date(now.getFullYear(), now.getMonth(), now.getDate(),
0,
0,
0);
693 if (date.getTime() < tmp.getTime()) {
694 console.info('skipping Anniversary (already passed) ' + entry.id);
696 eventIds[entry.id] =
0;
701 // fix DayEvents end time. End times are off by
1 Second. It's possible that the event has already passed
702 if (entry.Type == 'DayEvent' && endDate != null) {
703 endDate.setMinutes(endDate.getMinutes() -
1);
704 console.info('fixing DayEvent endDate: ' + endDate);
705 if (now.getTime()
> endDate.getTime()) {
706 console.info('event already passed ' + entry.id);
708 eventIds[entry.id] =
0;
713 // check if the event is currently taking place
714 if (entryStartTime != null && entryEndTime != null && date != null && endDate != null) {
715 // check if we are between start and endtime
716 if ((date.getTime() < now.getTime()) && (now.getTime() < endDate.getTime())) {
717 date = now; // change appointment date/time to now
718 console.info('event is currently taking place: ' + date);
722 // skip events for the first panel in case this is the second one and we're not in fullscreen mode
723 if (mode ==
0 && panelNum
> 0 && counter < panelNum * config['eventsPerWidget'].Value +
1) {
724 console.info('skipping (already in first widget) ' + entry.id);
728 // mark overdue todos
730 if (entry.Type == 'ToDo' && date != null) {
731 var tmp1 = new Date(date.getFullYear(), date.getMonth(), date.getDate(),
0,
0,
0);
732 var tmp2 = new Date(now.getFullYear(), now.getMonth(), now.getDate(),
0,
0,
0);
733 if (tmp1.getTime() < tmp2.getTime()) {
738 // generate html output
739 entriesHtml += '
<tr>';
740 if (config['showCalendarIndicator'].Value && calendarList.length - config['excludedCalendars'].Value.length
> 1) {
741 entriesHtml += '
<td><span class=
"calendar' + calendarColors[entry.CalendarName] + '"> </span></td>';
743 entriesHtml += '
<td><img class=
"icon" src=
"' + entry.Type + '.png" /></td>';
745 // some languages have very strange locale date formats, can't parse all those. Also some todos don't have dates at all.
746 entriesHtml += '
<td colspan=
"4"><span class=
"date">' + entryDate + '
</span> ';
748 var weekDay = date.toLocaleDateString().substr(
0,config['weekDayLength'].Value);
749 var time = formatTime(date);
750 var dateStr = formatDate(date, entryDate);
751 if (entry.Type == 'ToDo' && overdue && config['markOverdueTodos'].Value) {
752 dateStr = '
<span class=
"overdue">' + config['overdueText'].Value + '
</span>';
753 entriesHtml += '
<td colspan=
"4" width=
"1px"><span class=
"date">' + dateStr + '
</span> ';
754 } else if (entry.Type == 'ToDo' || entry.Type == 'Anniversary' || entry.Type == 'DayEvent' || entry.Type == 'Reminder') {
755 if ((isToday(date) || isTomorrow(date)) && config['showTodayAsText'].Value) // show weekday if the date string is not text. looks odd otherwise
756 entriesHtml += '
<td colspan=
"4" width=
"1px"><span class=
"date">' + dateStr + '
</span> ';
758 entriesHtml += '
<td class=
"weekDay" width=
"1px">' + weekDay + '
</td><td width=
"1px" class=
"date">' + dateStr + '
</td><td colspan=
"2">';
759 } else if (entry.Type == 'Meeting') {
760 if (config['showCombinedDateTime'].Value) {
762 entriesHtml += '
<td width=
"1px" colspan=
"4"><span class=
"today">' + time + '
</span> ';
763 else if (isTomorrow(date))
764 entriesHtml += '
<td width=
"1px" colspan=
"4"><span class=
"tomorrow">' + dateStr + '
</span> <span class=
"time">' + time + '
</span> ';
766 entriesHtml += '
<td width=
"1px" class=
"weekDay">' + weekDay + '
</td><td width=
"1px" class=
"date">' + dateStr + '
</td><td colspan=
"2">';
768 if ((isToday(date) || isTomorrow(date)) && config['showTodayAsText'].Value)
769 entriesHtml += '
<td colspan=
"4" width=
"1px"><span class=
"today">' + dateStr + '
</span> <span class=
"time">' + time + '
</span> ';
771 entriesHtml += '
<td width=
"1px" class=
"weekDay">' + weekDay + '
</td><td width=
"1px" class=
"date">' + dateStr + '
</td><td width=
"1px" class=
"time">' + time + '
</td><td>';
775 entriesHtml += '
<span class=
"description">' + Summary + '
</span></td></tr>';
778 entriesHtml += '
</table>';
779 if (config['showNothingText'].Value && entriesHtml == '
<table></table>') {
780 var text = config['nothingText'].Value.replace(/%d/, config['monthRange'].Value);
781 entriesHtml = '
<div style=
"width:295px; height:75px; text-align:center; line-height:75px; overflow:visible;">' + text + '
</div>';
783 if (cacheEntriesHtml != entriesHtml) {
785 document.getElementById('calendarList').innerHTML = entriesHtml;
787 document.getElementById('fullscreenCalendarList').innerHTML = entriesHtml;
788 cacheEntriesHtml = entriesHtml;
791 lastUpdateTime = new Date();
793 error('displaying list:' + e + ', line ' + e.line);
798 function updateScreen()
800 // check if opening fullscreen
801 if( window.innerHeight
> 91 && mode ==
0) {
803 cacheEntriesHtml = '';
804 document.getElementById('body').style.backgroundImage =
"";
807 else if (window.innerHeight <=
91 && mode !=
0) {
809 cacheEntriesHtml = '';
818 var time = new Date();
819 if (time.getTime() - lastUpdateTime.getTime()
> config['updateDataInterval'].Value *
60 *
1000) {
820 console.info('updateScreen(): force updateData() because last update was too long ago (' + (time.getTime() - lastUpdateTime.getTime()) /
1000 + 's)');
823 setUpdateTimer(); // reinitialize update timer
827 function launchCalendar()
830 widget.openApplication(config['calendarApp'].Value,
"");
831 if (config['hideWidgetOnCalendarOpen'].Value)
834 error('starting Calendar App');
841 console.info('New widget instance starting up...');
844 // call calendar service
845 if (device !=
"undefined")
846 calendarService = device.getServiceObject(
"Service.Calendar",
"IDataSource");
848 throw('device object does not exist');
850 error('loading Calendar service: ' + e + ', line ' + e.line);
854 calendarList = listCalendars();
856 updateCalendarColors();
859 requestNotification();
860 document.getElementById(
"settingsTitle").innerHTML = getLocalizedText('menu.settings');
862 if (window.innerHeight
> 91) {
863 mode =
0; // we're starting fullscreen, we set mode to homescreen in order to let updateScreen() do all the work for us
868 console.info(
"init(): updateScreen()");
870 if (config['useBackgroundImage'].Value)
871 // check for screen rotation every
1 secs
872 screenRotationTimer = window.setInterval('updateScreen()',
1000 *
1);
873 console.info(
"init(): finished...");
876 function setUpdateTimer()
878 updateTimer = window.setInterval('updateTimerCallback()',
1000 *
60 * config['updateDataInterval'].Value);
881 function clearUpdateTimer()
883 window.clearInterval(updateTimer);
886 function updateTimerCallback()
888 console.info(
"updateTimerCallback()");
892 function createMenu()
894 window.menu.setLeftSoftkeyLabel(
"",null);
895 window.menu.setRightSoftkeyLabel(
"",null);
897 var menuSettings = new MenuItem(getLocalizedText('menu.settings'), id++);
898 var menuCallApp = new MenuItem(getLocalizedText('menu.openCalendarApp'), id++);
899 var menuUpdate = new MenuItem(getLocalizedText('menu.update'), id++);
900 var menuAbout = new MenuItem(getLocalizedText('menu.about'), id++);
901 menuSettings.onSelect = showSettings;
902 menuAbout.onSelect = showAbout;
903 menuCallApp.onSelect = launchCalendar;
904 menuUpdate.onSelect = showUpdate;
906 window.menu.append(menuCallApp);
907 window.menu.append(menuSettings);
908 window.menu.append(menuUpdate);
909 window.menu.append(menuAbout);
912 function showSettings()
916 document.getElementById(
"settingsView").style.display =
"block";
917 document.onclick = null;
919 window.menu.setLeftSoftkeyLabel(getLocalizedText('settings.save'), function()
921 for (var key in config) {
922 if (config[key].Type == 'String')
923 config[key].Value = document.forms[
0].elements[
"settings." + key].value;
924 else if (config[key].Type == 'Int') {
925 config[key].Value = parseInt(document.forms[
0].elements[
"settings." + key].value);
926 if (config[key].Value <
0)
927 config[key].Value = config[key].Default;
929 else if (config[key].Type == 'Bool')
930 config[key].Value = document.forms[
0].elements[
"settings." + key].checked;
931 else if (config[key].Type == 'UID')
932 config[key].Value = parseInt(document.forms[
0].elements[
"settings." + key].value);
933 else if (config[key].Type == 'Enum') {
934 config[key].Value = document.forms[
0].elements[
"settings." + key].value;
935 if (config[key].ValidValues.indexOf(config[key].Value) == -
1)
936 config[key].Value = config[key].Default;
938 else if (config[key].Type == 'Array') {
939 if (key == 'excludedCalendars') {
940 config[key].Value = new Array();
941 for(var i=
0; i < calendarList.length; i++) {
942 var element = document.forms[
0].elements[
"settings." + key +
"." + calendarList[i]];
943 if (element != null && element.checked == false)
944 config[key].Value.push(calendarList[i]);
957 window.menu.setRightSoftkeyLabel(getLocalizedText('settings.cancel'), function()
963 var settingsHtml = '
<form>';
964 for (var key in config) {
965 if (config[key].Type == 'String') {
967 if (key.substring(
0,
9) ==
"cssStyle_")
968 prefix = getLocalizedText('settings.cssStyle_prefix');
969 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 />';
971 else if (config[key].Type == 'Int')
972 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 />';
973 else if (config[key].Type == 'Bool')
974 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 />';
975 else if (config[key].Type == 'UID')
976 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 />';
977 else if (config[key].Type == 'Enum') {
978 settingsHtml += '
<table><tr><td>' + getLocalizedText('settings.name.' + key) + '
<br /><select name=
"settings.' + key + '" size=
"1">';
979 for(var i =
0; i < config[key].ValidValues.length; i++)
980 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>';
981 settingsHtml += '
</select></div></td>' + printHintBox(getLocalizedText('settings.info.' + key)) + '
<hr />';
983 else if (config[key].Type == 'Array') {
984 settingsHtml += '
<table><tr><td>' + getLocalizedText('settings.name.' + key) + '
<br />';
985 if (key == 'excludedCalendars') {
986 for(var i=
0; i < calendarList.length; i++) {
987 var checked = '
checked=
"checked"';
988 if (config[key].Value.indexOf(calendarList[i]) != -
1)
990 settingsHtml += '
<input name=
"settings.' + key + '.' + calendarList[i] + '" type=
"checkbox" value=
"' + calendarList[i] + '" ' + checked + '
/> ' + calendarList[i] + '
<br />';
993 settingsHtml += '
</td>' + printHintBox(getLocalizedText('settings.info.' + key)) + '
<hr />';
996 settingsHtml += '
<input name=
"reset" type=
"button" value=
"' + getLocalizedText('settings.restoreDefaults') + '" onclick=
"javascript:restoreDefaultSettings();showSettings();" />';
997 settingsHtml += '
</form>';
998 document.getElementById(
"settingsList").innerHTML = settingsHtml;
1001 function changeCssClass(classname, properties)
1003 for(var i =
0; i < document.styleSheets[
0]['cssRules'].length; i++)
1005 if (document.styleSheets[
0]['cssRules'][i].selectorText == classname) {
1006 document.styleSheets[
0].deleteRule(i);
1007 document.styleSheets[
0].insertRule(classname + ' { ' + properties + ' }', document.styleSheets[
0]['cssRules'].length);
1013 function updateCssClasses()
1015 for(var key in config) {
1016 changeCssClass(getLocalizedText('settings.name.' + key), config[key].Value);
1020 function getSettingsCalEntryId()
1022 if (settingsCalEntryId == null) {
1023 // check if entry already exists
1024 var listFiltering = {
1025 Type:'CalendarEntry',
1027 StartRange: new Date(
2000,
0,
1),
1028 EndRange: new Date(
2000,
0,
1),
1029 SearchText: 'ComingNext Settings|',
1033 var result = calendarService.IDataSource.GetList(listFiltering);
1034 if (result.ErrorCode) {
1035 error(result.ErrorMessage);
1038 var list = result.ReturnValue;
1039 var entry = list.getNext();
1040 if (entry != undefined) {
1041 settingsCalEntryId = entry.LocalId;
1042 console.info(
"settingsCalEntryId=" + settingsCalEntryId);
1044 else { // create settings item
1045 var item = new Object();
1046 item.Type =
"DayEvent";
1047 item.StartTime = new Date(
2000,
0,
1);
1048 item.Summary =
"ComingNext Settings|";
1050 var criteria = new Object();
1051 criteria.Type =
"CalendarEntry";
1052 criteria.Item = item;
1055 var result = calendarService.IDataSource.Add(criteria);
1056 if (result.ErrorCode)
1057 error(result.ErrorMessage);
1059 error(
"getSettingsCalEntryId: " + e + ', line ' + e.line);
1062 getSettingsCalEntryId();
1067 function restoreDefaultSettings()
1069 for (var key in config)
1070 config[key].Value = config[key].Default;
1073 function loadSettings()
1075 getSettingsCalEntryId();
1076 var listFiltering = {
1077 Type:'CalendarEntry',
1079 LocalId: settingsCalEntryId
1082 var result = calendarService.IDataSource.GetList(listFiltering);
1083 if (result.ErrorCode) {
1084 error(result.ErrorMessage);
1087 var entry = result.ReturnValue.getNext();
1088 if (entry != undefined) {
1089 console.info(
"Loading Settings...");
1090 // only reload settings if they chanced since the last reload
1091 if (settingsCache != entry.Summary)
1093 restoreDefaultSettings();
1094 var stringlist = entry.Summary.split(
"|");
1095 // skip the first two entries, those contain header and version info
1096 for(var i =
2; i < stringlist.length -
1; i++) {
1097 var pair = stringlist[i].split('=');
1099 var value = pair[
1];
1100 console.info('stringlist: ' + key + '=\'' + value + '\'');
1101 if (config[key].Type == 'Int')
1102 config[key].Value = Number(value);
1103 else if (config[key].Type == 'String')
1104 config[key].Value = value;
1105 else if (config[key].Type == 'Bool')
1106 config[key].Value = (value == 'true')
1107 else if (config[key].Type == 'Enum')
1108 config[key].Value = value;
1109 else if (config[key].Type == 'UID')
1110 config[key].Value = Number(value);
1111 else if (config[key].Type == 'Array') {
1112 config[key].Value = value.split(
"^");
1113 if (config[key].Value.length ==
1 && config[key].Value[
0] ==
"") {
1114 config[key].Value = [];
1118 settingsCache = entry.Summary;
1122 console.info(
"Settings already cached and did not change");
1126 error(
"Failed to load settings, calendar entry could not be found");
1130 function saveSettings()
1132 getSettingsCalEntryId();
1133 var item = new Object();
1134 item.Type =
"DayEvent";
1135 item.StartTime = new Date(
2000,
0,
1);
1136 item.LocalId = settingsCalEntryId;
1137 item.Summary =
"ComingNext Settings|" + version +
"|";
1139 for (var key in config) {
1140 if (config[key].Type == 'Int')
1141 item.Summary += key +
"=" + config[key].Value.toString() +
"|";
1142 else if (config[key].Type == 'String')
1143 item.Summary += key +
"=" + config[key].Value +
"|";
1144 else if (config[key].Type == 'Bool')
1145 item.Summary += key +
"=" + (config[key].Value ? 'true' : 'false') +
"|";
1146 else if (config[key].Type == 'Enum')
1147 item.Summary += key +
"=" + config[key].Value +
"|";
1148 else if (config[key].Type == 'UID')
1149 item.Summary += key +
"=" + config[key].Value.toString() +
"|";
1150 else if (config[key].Type == 'Array')
1151 item.Summary += key +
"=" + config[key].Value.join(
"^") +
"|";
1153 settingsCache = item.Summary;
1155 var criteria = new Object();
1156 criteria.Type =
"CalendarEntry";
1157 criteria.Item = item;
1159 console.info(
"Saving settings to calendar entry: " + item.Summary);
1161 var result = calendarService.IDataSource.Add(criteria);
1162 if (result.ErrorCode)
1163 error(result.ErrorMessage);
1165 error(
"saveSettings: " + e + ', line ' + e.line);
1172 function toggleVisibility(elementId)
1174 if (document.getElementById(elementId).style.display ==
"none")
1175 document.getElementById(elementId).style.display =
"block";
1177 document.getElementById(elementId).style.display =
"none";
1181 function printHintBox(text)
1184 return '
<td width=
"1%" align=
"right" onclick=
"javascript:toggleVisibility(\'info' + uniqueId + '\')">' + getLocalizedText('settings.help') + '
</td></tr></table>'+
1185 '
<div class=
"settingsInfo" id=
"info' + uniqueId + '">' + text + '
</div>';
1188 function showAbout()
1192 document.getElementById(
"aboutView").style.display =
"block";
1193 document.onclick = null;
1195 window.menu.setLeftSoftkeyLabel(
" ", function(){});
1196 window.menu.setRightSoftkeyLabel(getLocalizedText('softkey.back'), function()
1202 //document.getElementById(
"aboutView").innerHTML = 'aboutView';
1203 document.getElementById(
"name").innerHTML =
"Coming Next " + version;
1206 function updateFullscreen()
1210 function showFullscreen()
1212 console.info(
"showFullscreen()");
1214 document.getElementById(
"fullscreenView").style.display =
"block";
1215 document.getElementById('body').className =
"backgroundFullscreen";
1216 document.onclick = launchCalendar;
1221 function getBackgroundImage()
1224 if (config['backgroundImageLocation'].Value == config['backgroundImageLocation'].ValidValues[
0]) // internal
1225 bgImage = 'background_' + orientation + '.png';
1227 bgImage = 'C:/Data/background_' + panelNum + '_' + orientation + '.png';
1231 function updateHomescreen()
1233 if (config['useBackgroundImage'].Value) {
1234 // check for screen rotation
1235 if (orientation != 'portrait' && screen.width ==
360 && screen.height ==
640) {
1236 window.widget.prepareForTransition(
"fade");
1237 orientation = 'portrait';
1238 document.getElementById('body').style.backgroundImage = 'url(' + getBackgroundImage() + ')';
1239 document.getElementById('body').style.backgroundColor = 'none';
1240 window.widget.performTransition();
1241 } else if (orientation != 'landscape' && screen.width ==
640 && screen.height ==
360) {
1242 window.widget.prepareForTransition(
"fade");
1243 orientation = 'landscape';
1244 document.getElementById('body').style.backgroundImage = 'url(' + getBackgroundImage() + ')';
1245 document.getElementById('body').style.backgroundColor = 'none';
1246 window.widget.performTransition();
1248 else if (document.getElementById('body').style.backgroundImage ==
"")
1250 document.getElementById('body').style.backgroundImage = 'url(' + getBackgroundImage() + ')';
1255 function showHomescreen()
1257 console.info(
"showHomescreen()");
1259 document.getElementById(
"homescreenView").style.display =
"block";
1260 document.getElementById('body').className =
"background";
1261 document.onclick = null;
1265 function getLocalizedText(p_Txt)
1267 if (localizedText[p_Txt])
1268 return localizedText[p_Txt];
1270 return 'ERROR: missing translation for ' + p_Txt;
1273 function showUpdate()
1277 document.getElementById(
"updateView").style.display =
"block";
1278 document.onclick = null;
1280 window.menu.setLeftSoftkeyLabel(getLocalizedText('update.checknow'), function(){
1283 window.menu.setRightSoftkeyLabel(getLocalizedText('softkey.back'), function()
1289 document.getElementById(
"currentVersion").innerHTML = getLocalizedText(
"update.current") + version;
1293 function checkForUpdate()
1295 // asynch XHR to server url
1296 reqV = new XMLHttpRequest();
1297 reqV.onreadystatechange = checkForUpdateCallback;
1298 document.getElementById(
"updateDiv").innerHTML = getLocalizedText(
"update.checking");
1299 reqV.open(
"GET", versionURL, true);
1300 reqV.setRequestHeader(
"If-Modified-Since",
"Sat, 1 Jan 2000 00:00:00 GMT" ); // disable caching
1304 function checkForUpdateCallback()
1306 if (reqV.readyState ==
4) {
1307 if (reqV.status ==
200) {
1308 var resultXml = reqV.responseText;
1310 var div = document.getElementById(
"tmp");
1311 div.innerHTML = resultXml;
1312 var newVersion = div.getElementsByTagName('version')[
0].innerHTML;
1313 var newVersionURL = div.getElementsByTagName('url')[
0].innerHTML;
1315 if (version != newVersion) {
1316 document.getElementById(
"updateDiv").innerHTML = getLocalizedText(
"update.download").replace(/%
1/, newVersion).replace(/%
2/, newVersionURL);
1319 document.getElementById(
"updateDiv").innerHTML = getLocalizedText(
"update.nonewversion");
1324 document.getElementById(
"updateDiv").innerHTML = getLocalizedText(
"update.error") + reqV.status +
" " + reqV.responseText;
1329 function hideViews()
1331 document.getElementById(
"homescreenView").style.display =
"none";
1332 document.getElementById(
"fullscreenView").style.display =
"none";
1333 document.getElementById(
"aboutView").style.display =
"none";
1334 document.getElementById(
"settingsView").style.display =
"none";
1335 document.getElementById(
"updateView").style.display =
"none";
1338 function listCalendars()
1344 DefaultCalendar: false
1348 var calendarsResult = calendarService.IDataSource.GetList(criteria);
1349 if (calendarsResult.ErrorCode !=
0)
1350 throw(
"Error fetching list of calendars: " + calendarsResult.ErrorCode + ': ' + calendarsResult.ErrorMessage);
1351 var calendarListIterator = calendarsResult.ReturnValue;
1356 while (( item = calendarListIterator.getNext()) != undefined ) {
1357 calendars[count++] = item;
1359 console.info(
"Available Calendars: " + calendars.join(
", "));
1362 error('listing calendars:' + e + ', line ' + e.line);
1367 // Copies all objects and their properties to an array. Data is copied so nothing gets lost when the reference is removed
1368 // 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
1369 function listToArray(list, calendarName)
1371 var array = new Array();
1374 while (( item = list.getNext()) != undefined ) {
1375 var itemCopy = new Object();
1376 for(var i=
0; i < entryFields.length; i++) {
1377 itemCopy[entryFields[i]] = item[entryFields[i]];
1379 // for some reason, the CalendarName property is never correctly queried, so we assign it manually here
1380 if (!itemCopy['CalendarName']) {
1381 itemCopy['CalendarName'] = calendarName;
1383 array.push(itemCopy);
1384 txt += array[array.length -
1].Summary +
", ";
1386 console.info(
"listToArray(): " + txt);
1390 function sortCalendarEntries(a, b)
1393 console.info(
"sortCalendarEntries(" + a.Summary +
"," + b.Summary +
")");
1395 if (a.InstanceStartTime != null) {
1396 atime = a.InstanceStartTime;
1398 else if (a.StartTime != null) {
1399 atime = a.StartTime;
1401 else if (a.InstanceEndTime != null) {
1402 atime = a.InstanceEndTime;
1404 else if (a.EndTime != null) {
1408 if (b.InstanceStartTime != null) {
1409 btime = b.InstanceStartTime;
1411 else if (b.StartTime != null) {
1412 btime = b.StartTime;
1414 else if (b.InstanceEndTime != null) {
1415 btime = b.InstanceEndTime;
1417 else if (b.EndTime != null) {
1421 if (atime && btime) {
1423 atime = parseDate(atime);
1424 btime = parseDate(btime);
1426 // sort by date & time
1427 if (atime < btime) {
1430 else if (atime
> btime) {
1434 else if (a.Type != b.Type) {
1435 if (a.Type < b.Type) {
1438 else if (a.Type
> b.Type) {
1442 // sort by description
1443 else if (a.Summary && b.Summary && a.Summary != b.Summary) {
1444 if (a.Summary < b.Summary) {
1447 else if (a.Summary
> b.Summary) {
1452 // NOTE: events my have no date information at all. In that case, we list events without date first
1453 else if (atime && !btime) {
1456 else if (!atime && btime) {
1459 else if (!atime && !btime) {
1461 if (a.Type != b.Type) {
1462 if (a.Type < b.Type) {
1465 else if (a.Type
> b.Type) {
1469 // sort by description
1470 else if (a.Summary && b.Summary && a.Summary != b.Summary) {
1471 if (a.Summary < b.Summary) {
1474 else if (a.Summary
> b.Summary) {
1483 function updateCalendarColors()
1486 calendarColors = [];
1487 if (calendarList.length
> maxColors) {
1488 console.info(
"updateCalendarColors(): Warning: more calendars than available indicator colors");
1490 for(var i=
0; i < calendarList.length; i++) {
1491 calendarColors[calendarList[i]] = (i % maxColors) +
1;
1497 <style type=
"text/css">
1498 table { margin:
0px; padding:
0px; border-spacing:
0px; }
1499 td { padding:
0px
5px
0px
0px; white-space:nowrap; overflow:hidden; }
1500 hr { color:#ffffff; background-color:#ffffff; height:
1px; text-align:left; border-style:none; }
1501 .settingsInfo { display:none; font-style:italic; }
1502 .title { font-weight:bold; font-size:
14pt; }
1503 .textInput { width:
90%; }
1504 .credits { margin-left:
40px; text-indent: -
20px; margin-bottom:
0px; }
1505 #homescreenView { width:
315px; height:
91px; overflow:hidden; }
1506 #calendarList { position:absolute; left:
5px; top:
4px; width:
295px; height:
75px; overflow:hidden; }
1507 #name { text-align:center; }
1508 #appicon { display: block; margin-left: auto; margin-right: auto; margin-top:
10px; }
1509 #smallappicon { width:
22px; height:
22px; margin-right:
10px; float:left; }
1514 <body id=
"body" class=
"background">
1515 <div id=
"homescreenView">
1516 <div id=
"calendarList"></div>
1518 <div id=
"fullscreenView" style=
"display:none;">
1519 <img src=
"Icon.png" id=
"smallappicon">
1520 <h1 class=
"title">Coming Next
</h1>
1522 <div id=
"fullscreenCalendarList">loading...
</div>
1524 <div id=
"settingsView" style=
"display:none">
1525 <img src=
"Icon.png" id=
"smallappicon">
1526 <h1 id=
"settingsTitle" class=
"title">Settings
</h1>
1528 <div id=
"settingsList"></div>
1530 <div id=
"aboutView" style=
"display:none">
1531 <img src=
"Icon.png" id=
"appicon">
1532 <h1 id=
"name">Coming Next
</h1>
1534 <p>Created by Dr. Cochambre and Michael Prager.
</p>
1535 <p>Contributions:
</p>
1536 <p class=
"credits">Paul Moore (bug fixes, new features and code cleanup)
</p>
1537 <p class=
"credits">Manfred Hanselmann (DST support)
</p>
1538 <p class=
"credits">Christophe Milsent (translation support & french translation)
</p>
1539 <p class=
"credits">Flavio Nathan (portuguese-brazilian translation)
</p>
1540 <p class=
"credits">Tokeda (russian translation)
</p>
1541 <p class=
"credits">Marcella Ferrari (italian translation)
</p>
1542 <p class=
"credits">Venos (italian translation)
</p>
1543 <p>This software is open source and licensed under the GPLv3.
</p>
1544 <p>Visit
<a onclick=
"widget.openURL('http://sourceforge.net/projects/comingnext'); return false;" href=
"http://sourceforge.net/projects/comingnext">sourceforge.net/projects/comingnext
</a> for free updates.
</p>
1547 <div id=
"updateView" style=
"display:none">
1548 <img src=
"Icon.png" id=
"smallappicon">
1549 <h1 class=
"title">Check for update
</h1>
1551 <div id=
"currentVersion">Coming Next ??
</div>
1552 <div id=
"updateDiv"></div>
1553 <div id=
"tmp" style=
"display:none;"></div>