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'
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 cssStyle_background: { Type: 'String', Default: 'color:#ffffff; background-color:#
000000', Value: 'color:#ffffff; background-color:#
000000',},
60 cssStyle_backgroundFullscreen: { Type: 'String', Default: 'color:#ffffff; background-color:#
000000', Value: 'color:#ffffff; background-color:#
000000',},
61 cssStyle_weekDay: { Type: 'String', Default: '', Value: '',},
62 cssStyle_date: { Type: 'String', Default: '', Value: '',},
63 cssStyle_today: { Type: 'String', Default: 'color:#ff0000', Value: 'color:#ff0000',},
64 cssStyle_tomorrow: { Type: 'String', Default: 'color:#
0000ff', Value: 'color:#
0000ff',},
65 cssStyle_time: { Type: 'String', Default: '', Value: '',},
66 cssStyle_now: { Type: 'String', Default: 'color:#ff00ff', Value: 'color:#ff00ff',},
67 cssStyle_description: { Type: 'String', Default: '', Value: '',},
68 cssStyle_icon: { Type: 'String', Default: 'width:
15px; height:
15px', Value: 'width:
15px; height:
15px',},
69 cssStyle_overdue: { Type: 'String', Default: 'color:#ffff00', Value: 'color:#ffff00',},
70 cssStyle_calendar1: { Type: 'String', Default: 'background-color:#
0757cf', Value: 'background-color:#
0757cf',},
71 cssStyle_calendar2: { Type: 'String', Default: 'background-color:#
579f37', Value: 'background-color:#
579f37',},
72 cssStyle_calendar3: { Type: 'String', Default: 'background-color:#ff9f07', Value: 'background-color:#ff9f07',},
73 cssStyle_calendar4: { Type: 'String', Default: 'background-color:#af8fef', Value: 'background-color:#af8fef',},
74 cssStyle_calendar5: { Type: 'String', Default: 'background-color:#
57afbf', Value: 'background-color:#
57afbf',},
75 cssStyle_calendar6: { Type: 'String', Default: 'background-color:#
9fdf57', Value: 'background-color:#
9fdf57',},
80 //-------------------------------------------------------
81 // Nothing of interest from here on...
82 //-------------------------------------------------------
83 var panelNum =
0; // use
1 for second panel
85 var versionURL =
"http://comingnext.sourceforge.net/version.xml";
86 var calendarService = null;
87 var cacheEntriesHtml = [];
88 var months_translated = [];
91 var mode =
0; //
0 = homescreen,
1 = fullscreen,
2 = settings,
3 = about,
4 = check for update
93 var settingsCalEntryId = null;
94 var settingsCache = null;
95 var notificationRequests = new Array();
96 var calendarList = [];
97 var calendarColors = [];
98 var updateTimer = null;
99 var screenRotationTimer = null;
101 // vars for daylight saving time
102 var summertime = false; // true, if current date is in summer, false if in winter
103 var daylightSavingDates = new Object(); // caches calculated DST winter and summer time shift dates
105 // this is a list of data fields a calendar event can have
119 window.onload = init;
120 window.onresize = updateScreen;
121 window.onshow = updateScreen;
123 function isLeapYear( year ) {
124 if (( year %
4 ==
0 && year %
100 !=
0 ) || year %
400 ==
0 )
130 function calcLeapYear(year, days)
132 if (isLeapYear(year))
138 function subToSunday(myDate, year, days, prevMonthDays)
140 for (i = myDate.getDay(); i
> 0 ;i--)
142 days -= prevMonthDays;
143 days = isLeapYear(year) ? --days : days;
147 function isSummertime(curDate)
152 // if we already calculated DST summer and winter time dates for this year, use cached values
153 var dst = daylightSavingDates[curDate.getFullYear()];
155 var thisYearS = new Date(curDate.getFullYear(),
3,
0,
0,
0,
0 );
156 var thisYearW = new Date(curDate.getFullYear(),
10,
0,
0,
0,
0 );
157 var nextYearS = new Date(curDate.getFullYear() +
1,
3,
0,
0,
0,
0 );
158 var nextYearW = new Date(curDate.getFullYear() +
1,
10,
0,
0,
0,
0 );
160 thisYearSDays = nextYearSDays =
90;
161 thisYearWDays = nextYearWDays =
304;
163 thisYearSDays = calcLeapYear(curDate.getFullYear(), thisYearSDays);
164 thisYearWDays = calcLeapYear(curDate.getFullYear(), thisYearWDays);
165 nextYearSDays = calcLeapYear(curDate.getFullYear() +
1, nextYearSDays);
166 nextYearWDays = calcLeapYear(curDate.getFullYear() +
1, nextYearWDays);
168 thisYearSDays = subToSunday(thisYearS, curDate.getFullYear(), thisYearSDays,
59);
169 thisYearWDays = subToSunday(thisYearW, curDate.getFullYear(), thisYearWDays,
273);
170 nextYearSDays = subToSunday(nextYearS, curDate.getFullYear() +
1, nextYearSDays,
59);
171 nextYearWDays = subToSunday(nextYearW, curDate.getFullYear() +
1, nextYearWDays,
273);
174 Summer: new Date (curDate.getFullYear(),
03-
1, thisYearSDays,
2,
0,
0),
175 Winter: new Date (curDate.getFullYear(),
10-
1, thisYearWDays,
2,
0,
0),
177 daylightSavingDates[curDate.getFullYear()] = dst;
180 if (dst.Summer < curDate)
182 if (dst.Winter < curDate)
184 if (summer && !winter)
190 function error(message)
192 console.info('Error: ' + message);
193 document.getElementById(
"calendarList").innerHTML = 'Error: ' + message;
196 function areDatesEqual(date1, date2)
198 return (date1.getFullYear() == date2.getFullYear() &&
199 date1.getMonth() == date2.getMonth() &&
200 date1.getDate() == date2.getDate());
203 function isTomorrow(date)
205 // tommorow = now +
1 day
206 // ToDo: some days can be shorter as
24 hours(daylight saving change day)
207 return areDatesEqual(date, new Date (now.getTime() +
24*
60*
60*
1000));
210 function isToday(date)
212 return areDatesEqual(date, now);
215 function collectLocales()
217 var tmpyear =
2000 + panelNum;
220 if (months_translated.length
> 0)
222 for (month =
0; month <
12; month++) {
223 var startDate = new Date(tmpyear, month,
15);
225 var item = new Object();
226 item.Type =
"DayEvent";
227 item.StartTime = startDate;
228 item.Summary =
"__temp" + month;
230 var criteria = new Object();
231 criteria.Type =
"CalendarEntry";
232 criteria.Item = item;
235 var result = calendarService.IDataSource.Add(criteria);
236 if (result.ErrorCode)
237 error(result.ErrorMessage);
239 error(
"collectLocales: " + e + ', line ' + e.line);
243 var startTime = new Date(tmpyear,
0,
1);
244 var endTime = new Date(tmpyear,
11,
31);
245 var listFiltering = {
246 Type:'CalendarEntry',
248 StartRange: startTime,
250 SearchText: '__temp',
254 var result = calendarService.IDataSource.GetList(listFiltering);
255 if (result.ErrorCode) {
256 error(result.ErrorMessage);
259 var list = result.ReturnValue;
261 error(e + ', line ' + e.line);
264 var ids = new Array();
270 while (list && (entry = list.getNext()) != undefined) {
271 dateArr = entry.StartTime.replace(/,/g,'').replace(/\./g,':').replace(/ /g,' ').split(' ');
272 var day = dateArr[
1];
273 var month = dateArr[
2];
274 var year = dateArr[
3];
276 // make sure month is set properly
277 if (isNaN(parseInt(day))) {
281 } else if (isNaN(parseInt(year))) {
287 console.info(entry.StartTime + ' -
> ' + month + ' ' + counter);
288 ids[counter] = entry.id;
289 months_translated[month] = counter +
1;
293 error(e + ', line ' + e.line);
298 var criteria = new Object();
299 criteria.Type =
"CalendarEntry";
304 var result = calendarService.IDataSource.Delete(criteria);
305 if (result.ErrorCode)
306 error(result.ErrorMessage);
308 error('deleting temp calendar entries:' + e + ', line ' + e.line);
313 function requestNotification()
315 var criteria = new Object();
316 criteria.Type =
"CalendarEntry";
317 criteria.Filter = new Object();
318 for(var i=
0; i < calendarList.length; i++) {
319 criteria.Filter.CalendarName = calendarList[i];
321 var notificationRequest = calendarService.IDataSource.RequestNotification(criteria, callback);
322 if (notificationRequest.ErrorCode)
323 error('requestNotification failed with error code ' + notificationRequest.ErrorCode);
324 notificationRequests.push(notificationRequest);
326 error(
"requestNotification: " + e + ', line ' + e.line);
330 var criteria2 = new Object();
331 criteria2.Type =
"CalendarEntry";
332 criteria2.Filter = new Object();
333 criteria2.Filter.LocalIdList = new Array();
334 criteria2.Filter.LocalIdList[
0] = settingsCalEntryId;
336 var notificationRequest = calendarService.IDataSource.RequestNotification(criteria2, settingsCallback);
337 if (notificationRequest.ErrorCode)
338 error('requestNotification failed with error code ' + notificationRequest.ErrorCode);
339 notificationRequests.push(notificationRequest);
341 error(
"requestNotification: " + e + ', line ' + e.line);
345 function cancelNotification()
347 for(var i=
0; i < notificationRequests.length; i++) {
349 var result = calendarService.IDataSource.Cancel(notificationRequests[i]);
350 if (result.ErrorCode)
351 error('cancelNotification failed with error code ' + result.ErrorCode);
353 error(
"cancelNotification: " + e + ', line ' + e.line);
358 function callback(transId, eventCode, result)
360 console.info(
"callback(): panelNum: %d transId: %d eventCode: %d result.ErrorCode: %d", panelNum, transId, eventCode, result.ErrorCode);
364 function settingsCallback(transId, eventCode, result)
366 console.info(
"settingsCallback(): panelNum: %d transId: %d eventCode: %d result.ErrorCode: %d", panelNum, transId, eventCode, result.ErrorCode);
370 function parseDate(dateString)
373 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:
374 Wednesday,
26 August,
2009 24:
00:
00
375 Wednesday,
26 August,
2009 12:
00:
00 am
376 Wednesday, August
26,
2009 12:
00:
00 am
377 Wednesday,
2009 August,
26 12:
00:
00 am
378 Wednesday,
2009 August,
28 8.00.00 pm
379 Wednesday,
2009 August,
28 08:
00:
00 PM
382 if (dateString ==
"" || dateString == null)
384 var dateArr = dateString.replace(/,/g,'').replace(/\./g,':').replace(/ /g,' ').split(' ');
385 if (dateArr.length !=
5 && dateArr.length !=
6)
389 var weekDay = dateArr[
0];
390 var day = dateArr[
1];
391 var month = dateArr[
2];
392 var year = dateArr[
3];
393 // make sure month is set properly
394 if (isNaN(parseInt(day))) {
398 } else if (isNaN(parseInt(year))) {
403 // make sure day and year are set properly
404 if (Number(day)
> Number(year)) {
409 month = months_translated[month];
412 var timeArr = dateArr[
4].split(':');
413 if (timeArr.length !=
3)
415 var hours = Number(timeArr[
0]);
416 var minutes = Number(timeArr[
1]);
417 var seconds = Number(timeArr[
2]);
418 if (dateArr.length ==
6 && dateArr[
5].toLowerCase() == 'pm' && hours <
12)
420 if (dateArr.length ==
6 && dateArr[
5].toLowerCase() == 'am' && hours ==
12)
423 var result = new Date(year, month -
1, day, hours, minutes, seconds);
425 // take care of daylight saving time
426 if (config['enableDaylightSaving'].Value) {
428 // determine if date is in summer or winter time
429 var dateSummerTime = isSummertime(result);
431 // work around bug in Nokias calendar api resulting in dates within a different DST to be off by
1 hour
432 if (summertime && !dateSummerTime) {
433 result = new Date(result.getTime() -
1000 *
60 *
60 * config['daylightSavingOffset'].Value); // -
1 hour
434 console.info('parseDate(): fixing time -
1h: ' + result);
436 else if (!summertime && dateSummerTime) {
437 result = new Date(result.getTime() +
1000 *
60 *
60 * config['daylightSavingOffset'].Value); // +
1 hour
438 console.info('parseDate(): fixing time +
1h: ' + result);
445 // 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"
446 function formatDate(date, format)
448 var day = date.getDate().toString();
449 var month = (date.getMonth() +
1).toString();
450 while (day.length <
2) { day = '
0' + day; }
451 while (month.length <
2) { month = '
0' + month; }
453 if (config['showTodayAsText'].Value && isToday(date))
454 return '
<span class=
"today">' + config['todayText'].Value + '
</span>';
455 if (config['showTodayAsText'].Value && isTomorrow(date))
456 return '
<span class=
"tomorrow">' + config['tomorrowText'].Value + '
</span>';
458 var dateArr = format.replace(/,/g,'').replace(/\./g,':').replace(/ /g,' ').split(' ');
459 if (dateArr.length !=
5 && dateArr.length !=
6) {
460 // we don't know how to format this
461 if (config['dateFormat'].Value == 'auto' || config['dateFormat'].Value == 'DDMM')
462 return day + config['dateSeparator'].Value + month;
464 return month + config['dateSeparator'].Value + day;
468 if (config['dateFormat'].Value == 'MMDD')
470 else if (config['dateFormat'].Value == 'DDMM')
473 // config['dateFormat'].Value == 'auto', try to detect system setting
475 var day_ = dateArr[
1];
476 var month_ = dateArr[
2];
477 var year_ = dateArr[
3];
478 // make sure month is set properly
479 if (isNaN(parseInt(day_))) {
484 } else if (isNaN(parseInt(year_))) {
490 // make sure day and year are set properly
491 if (Number(day_)
> Number(year_))
496 return day + config['dateSeparator'].Value + month;
498 return month + config['dateSeparator'].Value + day;
501 function formatTime(date)
503 // date is a Date() object
504 date.setSeconds(
0); // we don't care about seconds
505 var time = date.toLocaleTimeString().replace(/[\.:]
00/, ''); // remove seconds from string
506 if (time.replace(/\./, ':').split(':')[
0].length <
2)
508 if (config['showNowAsText'].Value && date.getTime() == now.getTime())
509 time = '
<span class=
"now">' + config['nowText'].Value + '
</span>';
513 function updateData()
515 console.info('updateData()');
517 // check if we got additional or less calendars since our last update
518 var newCalendarList = listCalendars();
519 if (newCalendarList.length != calendarList.length) {
520 calendarList = newCalendarList;
521 updateCalendarColors();
522 cancelNotification();
523 requestNotification();
527 // meetings have time
528 // 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
530 summertime = isSummertime(now); // cache summer time info for today
531 var meetingList = [];
532 for(var i=
0; i < calendarList.length; i++) {
533 var meetingListFiltering = {
534 Type:'CalendarEntry',
536 CalendarName: calendarList[i],
537 StartRange: (new Date(now.getFullYear(), now.getMonth(), now.getDate(),
0,
0,
0)),
538 EndRange: (new Date(now.getFullYear(), now.getMonth() + config['monthRange'].Value, now.getDate(),
0,
0,
0))
541 var meetingResult = calendarService.IDataSource.GetList(meetingListFiltering);
542 if (meetingResult.ErrorCode !=
0)
543 throw(
"Error fetching calendar data: " + meetingResult.ErrorCode + ': ' + meetingResult.ErrorMessage);
544 var list = meetingResult.ReturnValue;
545 meetingList = meetingList.concat(listToArray(list, calendarList[i]));
547 console.info(
"updateData(): meetingList.sort()");
548 meetingList.sort(sortCalendarEntries);
550 // todos don't, they start on
00:
00 hrs., but should be visible anyway
551 // this will generate a list of passed todos. We have to check if they have been marked as
"done" yet
552 if (config['includeTodos'].Value) {
553 var todayTodoList = [];
554 for(var i=
0; i < calendarList.length; i++) {
555 var todayTodoListFiltering = {
556 Type:'CalendarEntry',
558 CalendarName: calendarList[i],
560 StartRange: (new Date(now.getFullYear() -
1, now.getMonth(), now.getDate(),
0,
0,
0)),
561 EndRange: (new Date(now.getFullYear(), now.getMonth(), now.getDate(),
0,
0,
1))
564 var todayTodoResult = calendarService.IDataSource.GetList(todayTodoListFiltering);
565 var list = todayTodoResult.ReturnValue;
566 todayTodoList = todayTodoList.concat(listToArray(list, calendarList[i]));
568 console.info(
"updateData(): todayTodoList.sort()");
569 todayTodoList.sort(sortCalendarEntries);
570 var entryLists = [todayTodoList, meetingList];
572 var entryLists = [meetingList];
575 error('loading Calendar items list:' + e + ', line ' + e.line);
584 var fontsize = 'normal';
586 if (config['eventsPerWidget'].Value ==
3) {
588 changeCssClass('.icon', 'width:
20px; height:
20px');
590 else if (config['eventsPerWidget'].Value ==
5) {
592 changeCssClass('.icon', 'width:
10px; height:
10px');
594 else if (config['eventsPerWidget'].Value ==
6) {
596 changeCssClass('.icon', 'width:
8px; height:
8px');
600 changeCssClass('.icon', config['cssStyle_icon'].Value);
601 var entriesHtml = '
<table style=
"font-size:' + fontsize + ';">';
605 max = (panelNum +
1) * config['eventsPerWidget'].Value;
607 max =
30; // we can display a lot more events in fullscreen mode
610 for (var i=
0; i < entryLists.length; i++) {
611 listinfo = listinfo +
" " + entryLists[i].length;
612 var entrieslist =
"";
613 for (var j=
0; j < entryLists[i].length; j++) {
614 entrieslist += entryLists[i][j].Summary +
", ";
616 console.info(
"updateData(): entrieslist: " + entrieslist);
618 console.info(
"updateData(): inner loop, " + entryLists.length +
" lists, [" + listinfo +
"] entries");
620 // the first outer loop iteration is for passed ToDos, the second loop is for all upcomming events (may also include ToDos)
621 for (var i=
0; counter < max && i < entryLists.length; i++) {
622 for (var j=
0; (counter < max) && (j < entryLists[i].length); j++) {
623 entry = entryLists[i][j];
626 // output event info for debugging
627 var entryInfo =
"event: ";
628 for(var k=
0; k < entryFields.length; ++k) {
629 if (entry[entryFields[k]] != undefined) {
630 entryInfo += entryFields[k] +
"=" + entry[entryFields[k]] +
",";
633 console.info(entryInfo);
635 // we don't want ToDos when includeTodos == false or when they are completed
636 if (entry.Type == 'ToDo' && (entry.Status ==
"TodoCompleted" || !config['includeTodos'].Value)) {
637 console.info('skipping ' + entry.id );
642 // make sure that we don't include an event twice (useful for ToDos that might come up twice)
643 if (eventIds[entry.id] ==
1 && entry.Type == 'ToDo') {
644 console.info('skipped (already included) ' + entry.id);
648 eventIds[entry.id] =
1;
650 // summary can be undefined!
651 var Summary = ((entry.Summary == null) ? '' : entry.Summary);
652 if (entry.Type == 'Meeting' && entry.Location != '' && config['showLocation'].Value)
653 Summary += ', ' + entry.Location;
655 // fix by yves: determine start and end dates/times
656 entryStartTime = ((entry.InstanceStartTime == null) ? entry.StartTime : entry.InstanceStartTime);
657 entryEndTime = ((entry.InstanceEndTime == null) ? entry.EndTime : entry.InstanceEndTime);
659 // there can be ToDos that have no date at all!
660 if (entry.Type == 'ToDo' && entry.EndTime == null)
661 entryDate =
""; // this will cause parseDate(entryDate) to return null;
663 entryDate = ((entry.Type == 'ToDo') ? entryEndTime : entryStartTime); // ToDo's use their EndTime, the rest use StartTime
665 // Convert date/time string to Date object
666 var date = parseDate(entryDate);
667 console.info('date: ' + date);
668 var endDate = ((entryEndTime == null) ? null : parseDate(entryEndTime));
669 console.info('endDate: ' + endDate);
671 // check if meeting event has already passed
672 if (entry.Type == 'Meeting') {
673 var compareTime = ((endDate == null) ? date.getTime() : endDate.getTime());
674 if (now.getTime()
> compareTime) {
675 console.info('skipping Meeting (already passed) ' + entry.id);
677 eventIds[entry.id] =
0;
682 // check if anniversary passed (not sure why they are in the list, the query was only for today - nokia?)
683 if (entry.Type == 'Anniversary') {
684 var tmp = new Date(now.getFullYear(), now.getMonth(), now.getDate(),
0,
0,
0);
685 if (date.getTime() < tmp.getTime()) {
686 console.info('skipping Anniversary (already passed) ' + entry.id);
688 eventIds[entry.id] =
0;
693 // fix DayEvents end time. End times are off by
1 Second. It's possible that the event has already passed
694 if (entry.Type == 'DayEvent' && endDate != null) {
695 endDate.setMinutes(endDate.getMinutes() -
1);
696 console.info('fixing DayEvent endDate: ' + endDate);
697 if (now.getTime()
> endDate.getTime()) {
698 console.info('event already passed ' + entry.id);
700 eventIds[entry.id] =
0;
705 // check if the event is currently taking place
706 if (entryStartTime != null && entryEndTime != null && date != null && endDate != null) {
707 // check if we are between start and endtime
708 if ((date.getTime() < now.getTime()) && (now.getTime() < endDate.getTime())) {
709 date = now; // change appointment date/time to now
710 console.info('event is currently taking place: ' + date);
714 // skip events for the first panel in case this is the second one and we're not in fullscreen mode
715 if (mode ==
0 && panelNum
> 0 && counter < panelNum * config['eventsPerWidget'].Value +
1) {
716 console.info('skipping (already in first widget) ' + entry.id);
720 // mark overdue todos
722 if (entry.Type == 'ToDo' && date != null) {
723 var tmp1 = new Date(date.getFullYear(), date.getMonth(), date.getDate(),
0,
0,
0);
724 var tmp2 = new Date(now.getFullYear(), now.getMonth(), now.getDate(),
0,
0,
0);
725 if (tmp1.getTime() < tmp2.getTime()) {
730 // generate html output
731 entriesHtml += '
<tr>';
732 if (config['showCalendarIndicator'].Value && calendarList.length
> 1) {
733 entriesHtml += '
<td><span class=
"calendar' + calendarColors[entry.CalendarName] + '"> </span></td>';
735 entriesHtml += '
<td><img class=
"icon" src=
"' + entry.Type + '.png" /></td>';
737 // some languages have very strange locale date formats, can't parse all those. Also some todos don't have dates at all.
738 entriesHtml += '
<td colspan=
"4"><span class=
"date">' + entryDate + '
</span> ';
740 var weekDay = date.toLocaleDateString().substr(
0,config['weekDayLength'].Value);
741 var time = formatTime(date);
742 var dateStr = formatDate(date, entryDate);
743 if (entry.Type == 'ToDo' && overdue && config['markOverdueTodos'].Value) {
744 dateStr = '
<span class=
"overdue">' + config['overdueText'].Value + '
</span>';
745 entriesHtml += '
<td colspan=
"4" width=
"1px"><span class=
"date">' + dateStr + '
</span> ';
746 } else if (entry.Type == 'ToDo' || entry.Type == 'Anniversary' || entry.Type == 'DayEvent' || entry.Type == 'Reminder') {
747 if ((isToday(date) || isTomorrow(date)) && config['showTodayAsText'].Value) // show weekday if the date string is not text. looks odd otherwise
748 entriesHtml += '
<td colspan=
"4" width=
"1px"><span class=
"date">' + dateStr + '
</span> ';
750 entriesHtml += '
<td class=
"weekDay" width=
"1px">' + weekDay + '
</td><td width=
"1px" class=
"date">' + dateStr + '
</td><td colspan=
"2">';
751 } else if (entry.Type == 'Meeting') {
752 if (config['showCombinedDateTime'].Value) {
754 entriesHtml += '
<td width=
"1px" colspan=
"4"><span class=
"today">' + time + '
</span> ';
755 else if (isTomorrow(date))
756 entriesHtml += '
<td width=
"1px" colspan=
"4"><span class=
"tomorrow">' + dateStr + '
</span> <span class=
"time">' + time + '
</span> ';
758 entriesHtml += '
<td width=
"1px" class=
"weekDay">' + weekDay + '
</td><td width=
"1px" class=
"date">' + dateStr + '
</td><td colspan=
"2">';
760 if ((isToday(date) || isTomorrow(date)) && config['showTodayAsText'].Value)
761 entriesHtml += '
<td colspan=
"4" width=
"1px"><span class=
"today">' + dateStr + '
</span> <span class=
"time">' + time + '
</span> ';
763 entriesHtml += '
<td width=
"1px" class=
"weekDay">' + weekDay + '
</td><td width=
"1px" class=
"date">' + dateStr + '
</td><td width=
"1px" class=
"time">' + time + '
</td><td>';
767 entriesHtml += '
<span class=
"description">' + Summary + '
</span></td></tr>';
770 entriesHtml += '
</table>';
771 if (config['showNothingText'].Value && entriesHtml == '
<table></table>') {
772 var text = config['nothingText'].Value.replace(/%d/, config['monthRange'].Value);
773 entriesHtml = '
<div style=
"width:295px; height:75px; text-align:center; line-height:75px; overflow:visible;">' + text + '
</div>';
775 if (cacheEntriesHtml != entriesHtml) {
777 document.getElementById('calendarList').innerHTML = entriesHtml;
779 document.getElementById('fullscreenCalendarList').innerHTML = entriesHtml;
780 cacheEntriesHtml = entriesHtml;
783 error('displaying list:' + e + ', line ' + e.line);
788 function updateScreen()
790 // check if opening fullscreen
791 if( window.innerHeight
> 91 && mode ==
0) {
793 cacheEntriesHtml = '';
794 document.getElementById('body').style.backgroundImage =
"";
797 else if (window.innerHeight <=
91 && mode !=
0) {
799 cacheEntriesHtml = '';
809 function launchCalendar()
812 widget.openApplication(config['calendarApp'].Value,
"");
813 if (config['hideWidgetOnCalendarOpen'].Value)
816 error('starting Calendar App');
823 console.info('New widget instance starting up...');
826 // call calendar service
827 if (device !=
"undefined")
828 calendarService = device.getServiceObject(
"Service.Calendar",
"IDataSource");
830 throw('device object does not exist');
832 error('loading Calendar service: ' + e + ', line ' + e.line);
836 calendarList = listCalendars();
838 updateCalendarColors();
841 requestNotification();
842 document.getElementById(
"settingsTitle").innerHTML = getLocalizedText('menu.settings');
844 if (window.innerHeight
> 91) {
845 mode =
0; // we're starting fullscreen, we set mode to homescreen in order to let updateScreen() do all the work for us
850 console.info(
"init(): updateScreen()");
852 if (config['useBackgroundImage'].Value)
853 // check for screen rotation every
1 secs
854 screenRotationTimer = window.setInterval('updateScreen()',
1000 *
1);
855 console.info(
"init(): finished...");
858 function setUpdateTimer()
860 updateTimer = window.setInterval('updateTimerCallback()',
1000 *
60 * config['updateDataInterval'].Value);
863 function clearUpdateTimer()
865 window.clearInterval(updateTimer);
868 function updateTimerCallback()
870 console.info(
"updateTimerCallback()");
874 function createMenu()
876 window.menu.setLeftSoftkeyLabel(
"",null);
877 window.menu.setRightSoftkeyLabel(
"",null);
879 var menuSettings = new MenuItem(getLocalizedText('menu.settings'), id++);
880 var menuCallApp = new MenuItem(getLocalizedText('menu.openCalendarApp'), id++);
881 var menuUpdate = new MenuItem(getLocalizedText('menu.update'), id++);
882 var menuAbout = new MenuItem(getLocalizedText('menu.about'), id++);
883 menuSettings.onSelect = showSettings;
884 menuAbout.onSelect = showAbout;
885 menuCallApp.onSelect = launchCalendar;
886 menuUpdate.onSelect = showUpdate;
888 window.menu.append(menuCallApp);
889 window.menu.append(menuSettings);
890 window.menu.append(menuUpdate);
891 window.menu.append(menuAbout);
894 function showSettings()
898 document.getElementById(
"settingsView").style.display =
"block";
899 document.onclick = null;
901 window.menu.setLeftSoftkeyLabel(getLocalizedText('settings.save'), function()
903 for (var key in config) {
904 if (config[key].Type == 'String')
905 config[key].Value = document.forms[
0].elements[
"settings." + key].value;
906 else if (config[key].Type == 'Int') {
907 config[key].Value = parseInt(document.forms[
0].elements[
"settings." + key].value);
908 if (config[key].Value <
0)
909 config[key].Value = config[key].Default;
911 else if (config[key].Type == 'Bool')
912 config[key].Value = document.forms[
0].elements[
"settings." + key].checked;
913 else if (config[key].Type == 'UID')
914 config[key].Value = parseInt(document.forms[
0].elements[
"settings." + key].value);
915 else if (config[key].Type == 'Enum') {
916 config[key].Value = document.forms[
0].elements[
"settings." + key].value;
917 if (config[key].ValidValues.indexOf(config[key].Value) == -
1)
918 config[key].Value = config[key].Default;
929 window.menu.setRightSoftkeyLabel(getLocalizedText('settings.cancel'), function()
935 var settingsHtml = '
<form>';
936 for (var key in config) {
937 if (config[key].Type == 'String') {
939 if (key.substring(
0,
9) ==
"cssStyle_")
940 prefix = getLocalizedText('settings.cssStyle_prefix');
941 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 />';
943 else if (config[key].Type == 'Int')
944 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 />';
945 else if (config[key].Type == 'Bool')
946 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 />';
947 else if (config[key].Type == 'UID')
948 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 />';
949 else if (config[key].Type == 'Enum') {
950 settingsHtml += '
<table><tr><td>' + getLocalizedText('settings.name.' + key) + '
<br /><select name=
"settings.' + key + '" size=
"1">';
951 for(var i =
0; i < config[key].ValidValues.length; i++)
952 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>';
953 settingsHtml += '
</select></div></td>' + printHintBox(getLocalizedText('settings.info.' + key)) + '
<hr />';
956 settingsHtml += '
<input name=
"reset" type=
"button" value=
"' + getLocalizedText('settings.restoreDefaults') + '" onclick=
"javascript:restoreDefaultSettings();showSettings();" />';
957 settingsHtml += '
</form>';
958 document.getElementById(
"settingsList").innerHTML = settingsHtml;
961 function changeCssClass(classname, properties)
963 for(var i =
0; i < document.styleSheets[
0]['cssRules'].length; i++)
965 if (document.styleSheets[
0]['cssRules'][i].selectorText == classname) {
966 document.styleSheets[
0].deleteRule(i);
967 document.styleSheets[
0].insertRule(classname + ' { ' + properties + ' }', document.styleSheets[
0]['cssRules'].length);
973 function updateCssClasses()
975 for(var key in config) {
976 changeCssClass(getLocalizedText('settings.name.' + key), config[key].Value);
980 function getSettingsCalEntryId()
982 if (settingsCalEntryId == null) {
983 // check if entry already exists
984 var listFiltering = {
985 Type:'CalendarEntry',
987 StartRange: new Date(
2000,
0,
1),
988 EndRange: new Date(
2000,
0,
1),
989 SearchText: 'ComingNext Settings|',
993 var result = calendarService.IDataSource.GetList(listFiltering);
994 if (result.ErrorCode) {
995 error(result.ErrorMessage);
998 var list = result.ReturnValue;
999 var entry = list.getNext();
1000 if (entry != undefined) {
1001 settingsCalEntryId = entry.LocalId;
1002 console.info(
"settingsCalEntryId=" + settingsCalEntryId);
1004 else { // create settings item
1005 var item = new Object();
1006 item.Type =
"DayEvent";
1007 item.StartTime = new Date(
2000,
0,
1);
1008 item.Summary =
"ComingNext Settings|";
1010 var criteria = new Object();
1011 criteria.Type =
"CalendarEntry";
1012 criteria.Item = item;
1015 var result = calendarService.IDataSource.Add(criteria);
1016 if (result.ErrorCode)
1017 error(result.ErrorMessage);
1019 error(
"getSettingsCalEntryId: " + e + ', line ' + e.line);
1022 getSettingsCalEntryId();
1027 function restoreDefaultSettings()
1029 for (var key in config)
1030 config[key].Value = config[key].Default;
1033 function loadSettings()
1035 getSettingsCalEntryId();
1036 var listFiltering = {
1037 Type:'CalendarEntry',
1039 LocalId: settingsCalEntryId
1042 var result = calendarService.IDataSource.GetList(listFiltering);
1043 if (result.ErrorCode) {
1044 error(result.ErrorMessage);
1047 var entry = result.ReturnValue.getNext();
1048 if (entry != undefined) {
1049 console.info(
"Loading Settings...");
1050 // only reload settings if they chanced since the last reload
1051 if (settingsCache != entry.Summary)
1053 restoreDefaultSettings();
1054 var stringlist = entry.Summary.split(
"|");
1055 // skip the first two entries, those contain header and version info
1056 for(var i =
2; i < stringlist.length -
1; i++) {
1057 var pair = stringlist[i].split('=');
1059 var value = pair[
1];
1060 console.info('stringlist: ' + key + '=\'' + value + '\'');
1061 if (config[key].Type == 'Int')
1062 config[key].Value = Number(value);
1063 else if (config[key].Type == 'String')
1064 config[key].Value = value;
1065 else if (config[key].Type == 'Bool')
1066 config[key].Value = (value == 'true')
1067 else if (config[key].Type == 'Enum')
1068 config[key].Value = value;
1069 else if (config[key].Type == 'UID')
1070 config[key].Value = Number(value);
1072 settingsCache = entry.Summary;
1076 console.info(
"Settings already cached and did not change");
1080 error(
"Failed to load settings, calendar entry could not be found");
1084 function saveSettings()
1086 getSettingsCalEntryId();
1087 var item = new Object();
1088 item.Type =
"DayEvent";
1089 item.StartTime = new Date(
2000,
0,
1);
1090 item.LocalId = settingsCalEntryId;
1091 item.Summary =
"ComingNext Settings|" + version +
"|";
1093 for (var key in config) {
1094 if (config[key].Type == 'Int')
1095 item.Summary += key +
"=" + config[key].Value.toString() +
"|";
1096 else if (config[key].Type == 'String')
1097 item.Summary += key +
"=" + config[key].Value +
"|";
1098 else if (config[key].Type == 'Bool')
1099 item.Summary += key +
"=" + (config[key].Value ? 'true' : 'false') +
"|";
1100 else if (config[key].Type == 'Enum')
1101 item.Summary += key +
"=" + config[key].Value +
"|";
1102 else if (config[key].Type == 'UID')
1103 item.Summary += key +
"=" + config[key].Value.toString() +
"|";
1105 settingsCache = item.Summary;
1107 var criteria = new Object();
1108 criteria.Type =
"CalendarEntry";
1109 criteria.Item = item;
1111 console.info(
"Saving settings to calendar entry: " + item.Summary);
1113 var result = calendarService.IDataSource.Add(criteria);
1114 if (result.ErrorCode)
1115 error(result.ErrorMessage);
1117 error(
"saveSettings: " + e + ', line ' + e.line);
1124 function toggleVisibility(elementId)
1126 if (document.getElementById(elementId).style.display ==
"none")
1127 document.getElementById(elementId).style.display =
"block";
1129 document.getElementById(elementId).style.display =
"none";
1133 function printHintBox(text)
1136 return '
<td width=
"1%" align=
"right" onclick=
"javascript:toggleVisibility(\'info' + uniqueId + '\')">' + getLocalizedText('settings.help') + '
</td></tr></table>'+
1137 '
<div class=
"settingsInfo" id=
"info' + uniqueId + '">' + text + '
</div>';
1140 function showAbout()
1144 document.getElementById(
"aboutView").style.display =
"block";
1145 document.onclick = null;
1147 window.menu.setLeftSoftkeyLabel(
" ", function(){});
1148 window.menu.setRightSoftkeyLabel(getLocalizedText('softkey.back'), function()
1154 //document.getElementById(
"aboutView").innerHTML = 'aboutView';
1155 document.getElementById(
"name").innerHTML =
"Coming Next " + version;
1158 function updateFullscreen()
1162 function showFullscreen()
1164 console.info(
"showFullscreen()");
1166 document.getElementById(
"fullscreenView").style.display =
"block";
1167 document.getElementById('body').className =
"backgroundFullscreen";
1168 document.onclick = launchCalendar;
1173 function getBackgroundImage()
1176 if (config['backgroundImageLocation'].Value == config['backgroundImageLocation'].ValidValues[
0]) // internal
1177 bgImage = 'background_' + orientation + '.png';
1179 bgImage = 'C:/Data/background_' + panelNum + '_' + orientation + '.png';
1183 function updateHomescreen()
1185 if (config['useBackgroundImage'].Value) {
1186 // check for screen rotation
1187 if (orientation != 'portrait' && screen.width ==
360 && screen.height ==
640) {
1188 window.widget.prepareForTransition(
"fade");
1189 orientation = 'portrait';
1190 document.getElementById('body').style.backgroundImage = 'url(' + getBackgroundImage() + ')';
1191 document.getElementById('body').style.backgroundColor = 'none';
1192 window.widget.performTransition();
1193 } else if (orientation != 'landscape' && screen.width ==
640 && screen.height ==
360) {
1194 window.widget.prepareForTransition(
"fade");
1195 orientation = 'landscape';
1196 document.getElementById('body').style.backgroundImage = 'url(' + getBackgroundImage() + ')';
1197 document.getElementById('body').style.backgroundColor = 'none';
1198 window.widget.performTransition();
1200 else if (document.getElementById('body').style.backgroundImage ==
"")
1202 document.getElementById('body').style.backgroundImage = 'url(' + getBackgroundImage() + ')';
1207 function showHomescreen()
1209 console.info(
"showHomescreen()");
1211 document.getElementById(
"homescreenView").style.display =
"block";
1212 document.getElementById('body').className =
"background";
1213 document.onclick = null;
1217 function getLocalizedText(p_Txt)
1219 if (localizedText[p_Txt])
1220 return localizedText[p_Txt];
1222 return 'ERROR: missing translation for ' + p_Txt;
1225 function showUpdate()
1229 document.getElementById(
"updateView").style.display =
"block";
1230 document.onclick = null;
1232 window.menu.setLeftSoftkeyLabel(getLocalizedText('update.checknow'), function(){
1235 window.menu.setRightSoftkeyLabel(getLocalizedText('softkey.back'), function()
1241 document.getElementById(
"currentVersion").innerHTML = getLocalizedText(
"update.current") + version;
1245 function checkForUpdate()
1247 // asynch XHR to server url
1248 reqV = new XMLHttpRequest();
1249 reqV.onreadystatechange = checkForUpdateCallback;
1250 document.getElementById(
"updateDiv").innerHTML = getLocalizedText(
"update.checking");
1251 reqV.open(
"GET", versionURL, true);
1252 reqV.setRequestHeader(
"If-Modified-Since",
"Sat, 1 Jan 2000 00:00:00 GMT" ); // disable caching
1256 function checkForUpdateCallback()
1258 if (reqV.readyState ==
4) {
1259 if (reqV.status ==
200) {
1260 var resultXml = reqV.responseText;
1262 var div = document.getElementById(
"tmp");
1263 div.innerHTML = resultXml;
1264 var newVersion = div.getElementsByTagName('version')[
0].innerHTML;
1265 var newVersionURL = div.getElementsByTagName('url')[
0].innerHTML;
1267 if (version != newVersion) {
1268 document.getElementById(
"updateDiv").innerHTML = getLocalizedText(
"update.download").replace(/%
1/, newVersion).replace(/%
2/, newVersionURL);
1271 document.getElementById(
"updateDiv").innerHTML = getLocalizedText(
"update.nonewversion");
1276 document.getElementById(
"updateDiv").innerHTML = getLocalizedText(
"update.error") + reqV.status +
" " + reqV.responseText;
1281 function hideViews()
1283 document.getElementById(
"homescreenView").style.display =
"none";
1284 document.getElementById(
"fullscreenView").style.display =
"none";
1285 document.getElementById(
"aboutView").style.display =
"none";
1286 document.getElementById(
"settingsView").style.display =
"none";
1287 document.getElementById(
"updateView").style.display =
"none";
1290 function listCalendars()
1296 DefaultCalendar: false
1300 var calendarsResult = calendarService.IDataSource.GetList(criteria);
1301 if (calendarsResult.ErrorCode !=
0)
1302 throw(
"Error fetching list of calendars: " + calendarsResult.ErrorCode + ': ' + calendarsResult.ErrorMessage);
1303 var calendarListIterator = calendarsResult.ReturnValue;
1308 while (( item = calendarListIterator.getNext()) != undefined ) {
1309 calendars[count++] = item;
1311 console.info(
"Available Calendars: " + calendars.join(
", "));
1314 error('listing calendars:' + e + ', line ' + e.line);
1319 // Copies all objects and their properties to an array. Data is copied so nothing gets lost when the reference is removed
1320 // 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
1321 function listToArray(list, calendarName)
1323 var array = new Array();
1326 while (( item = list.getNext()) != undefined ) {
1327 var itemCopy = new Object();
1328 for(var i=
0; i < entryFields.length; i++) {
1329 itemCopy[entryFields[i]] = item[entryFields[i]];
1331 // for some reason, the CalendarName property is never correctly queried, so we assign it manually here
1332 if (!itemCopy['CalendarName']) {
1333 itemCopy['CalendarName'] = calendarName;
1335 array.push(itemCopy);
1336 txt += array[array.length -
1].Summary +
", ";
1338 console.info(
"listToArray(): " + txt);
1342 function sortCalendarEntries(a, b)
1345 console.info(
"sortCalendarEntries(" + a.Summary +
"," + b.Summary +
")");
1347 if (a.InstanceStartTime != null) {
1348 atime = a.InstanceStartTime;
1350 else if (a.StartTime != null) {
1351 atime = a.StartTime;
1353 else if (a.InstanceEndTime != null) {
1354 atime = a.InstanceEndTime;
1356 else if (a.EndTime != null) {
1360 if (b.InstanceStartTime != null) {
1361 btime = b.InstanceStartTime;
1363 else if (b.StartTime != null) {
1364 btime = b.StartTime;
1366 else if (b.InstanceEndTime != null) {
1367 btime = b.InstanceEndTime;
1369 else if (b.EndTime != null) {
1373 if (atime && btime) {
1375 atime = parseDate(atime);
1376 btime = parseDate(btime);
1378 // sort by date & time
1379 if (atime < btime) {
1382 else if (atime
> btime) {
1386 else if (a.Type != b.Type) {
1387 if (a.Type < b.Type) {
1390 else if (a.Type
> b.Type) {
1394 // sort by description
1395 else if (a.Summary && b.Summary && a.Summary != b.Summary) {
1396 if (a.Summary < b.Summary) {
1399 else if (a.Summary
> b.Summary) {
1408 function updateCalendarColors()
1411 calendarColors = [];
1412 if (calendarList.length
> maxColors) {
1413 console.info(
"updateCalendarColors(): Warning: more calendars than available indicator colors");
1415 for(var i=
0; i < calendarList.length; i++) {
1416 calendarColors[calendarList[i]] = (i % maxColors) +
1;
1422 <style type=
"text/css">
1423 table { margin:
0px; padding:
0px; border-spacing:
0px; }
1424 td { padding:
0px
5px
0px
0px; white-space:nowrap; overflow:hidden; }
1425 hr { color:#ffffff; background-color:#ffffff; height:
1px; text-align:left; border-style:none; }
1426 .settingsInfo { display:none; font-style:italic; }
1427 .title { font-weight:bold; font-size:
14pt; }
1428 .textInput { width:
90%; }
1429 .credits { margin-left:
40px; text-indent: -
20px; margin-bottom:
0px; }
1430 #homescreenView { width:
315px; height:
91px; overflow:hidden; }
1431 #calendarList { position:absolute; left:
5px; top:
4px; width:
295px; height:
75px; overflow:hidden; }
1432 #name { text-align:center; }
1433 #appicon { display: block; margin-left: auto; margin-right: auto; margin-top:
10px; }
1434 #smallappicon { width:
22px; height:
22px; margin-right:
10px; float:left; }
1439 <body id=
"body" class=
"background">
1440 <div id=
"homescreenView">
1441 <div id=
"calendarList"></div>
1443 <div id=
"fullscreenView" style=
"display:none;">
1444 <img src=
"Icon.png" id=
"smallappicon">
1445 <h1 class=
"title">Coming Next
</h1>
1447 <div id=
"fullscreenCalendarList">loading...
</div>
1449 <div id=
"settingsView" style=
"display:none">
1450 <img src=
"Icon.png" id=
"smallappicon">
1451 <h1 id=
"settingsTitle" class=
"title">Settings
</h1>
1453 <div id=
"settingsList"></div>
1455 <div id=
"aboutView" style=
"display:none">
1456 <img src=
"Icon.png" id=
"appicon">
1457 <h1 id=
"name">Coming Next
</h1>
1459 <p>Created by Dr. Cochambre and Michael Prager.
</p>
1460 <p>Contributions:
</p>
1461 <p class=
"credits">Paul Moore (bug fixes, new features and code cleanup)
</p>
1462 <p class=
"credits">Manfred Hanselmann (DST support)
</p>
1463 <p class=
"credits">Christophe Milsent (translation support & french translation)
</p>
1464 <p class=
"credits">Flavio Nathan (portuguese-brazilian translation)
</p>
1465 <p class=
"credits">Tokeda (russian translation)
</p>
1466 <p>This software is open source and licensed under the GPLv3.
</p>
1467 <p>Visit
<a href=
"http://sourceforge.net/projects/comingnext">sourceforge.net/projects/comingnext
</a> for free updates.
</p>
1470 <div id=
"updateView" style=
"display:none">
1471 <img src=
"Icon.png" id=
"smallappicon">
1472 <h1 class=
"title">Check for update
</h1>
1474 <div id=
"currentVersion">Coming Next ??
</div>
1475 <div id=
"updateDiv"></div>
1476 <div id=
"tmp" style=
"display:none;"></div>