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 = [];
99 // vars for daylight saving time
100 var summertime = false; // true, if current date is in summer, false if in winter
101 var daylightSavingDates = new Object(); // caches calculated DST winter and summer time shift dates
103 // this is a list of data fields a calendar event can have
117 window.onload = init;
118 window.onresize = updateScreen;
119 window.onshow = updateScreen;
121 function isLeapYear( year ) {
122 if (( year %
4 ==
0 && year %
100 !=
0 ) || year %
400 ==
0 )
128 function calcLeapYear(year, days)
130 if (isLeapYear(year))
136 function subToSunday(myDate, year, days, prevMonthDays)
138 for (i = myDate.getDay(); i
> 0 ;i--)
140 days -= prevMonthDays;
141 days = isLeapYear(year) ? --days : days;
145 function isSummertime(curDate)
150 // if we already calculated DST summer and winter time dates for this year, use cached values
151 var dst = daylightSavingDates[curDate.getFullYear()];
153 var thisYearS = new Date(curDate.getFullYear(),
3,
0,
0,
0,
0 );
154 var thisYearW = new Date(curDate.getFullYear(),
10,
0,
0,
0,
0 );
155 var nextYearS = new Date(curDate.getFullYear() +
1,
3,
0,
0,
0,
0 );
156 var nextYearW = new Date(curDate.getFullYear() +
1,
10,
0,
0,
0,
0 );
158 thisYearSDays = nextYearSDays =
90;
159 thisYearWDays = nextYearWDays =
304;
161 thisYearSDays = calcLeapYear(curDate.getFullYear(), thisYearSDays);
162 thisYearWDays = calcLeapYear(curDate.getFullYear(), thisYearWDays);
163 nextYearSDays = calcLeapYear(curDate.getFullYear() +
1, nextYearSDays);
164 nextYearWDays = calcLeapYear(curDate.getFullYear() +
1, nextYearWDays);
166 thisYearSDays = subToSunday(thisYearS, curDate.getFullYear(), thisYearSDays,
59);
167 thisYearWDays = subToSunday(thisYearW, curDate.getFullYear(), thisYearWDays,
273);
168 nextYearSDays = subToSunday(nextYearS, curDate.getFullYear() +
1, nextYearSDays,
59);
169 nextYearWDays = subToSunday(nextYearW, curDate.getFullYear() +
1, nextYearWDays,
273);
172 Summer: new Date (curDate.getFullYear(),
03-
1, thisYearSDays,
2,
0,
0),
173 Winter: new Date (curDate.getFullYear(),
10-
1, thisYearWDays,
2,
0,
0),
175 daylightSavingDates[curDate.getFullYear()] = dst;
178 if (dst.Summer < curDate)
180 if (dst.Winter < curDate)
182 if (summer && !winter)
188 function error(message)
190 console.info('Error: ' + message);
191 document.getElementById(
"calendarList").innerHTML = 'Error: ' + message;
194 function areDatesEqual(date1, date2)
196 return (date1.getFullYear() == date2.getFullYear() &&
197 date1.getMonth() == date2.getMonth() &&
198 date1.getDate() == date2.getDate());
201 function isTomorrow(date)
203 // tommorow = now +
1 day
204 // ToDo: some days can be shorter as
24 hours(daylight saving change day)
205 return areDatesEqual(date, new Date (now.getTime() +
24*
60*
60*
1000));
208 function isToday(date)
210 return areDatesEqual(date, now);
213 function collectLocales()
215 var tmpyear =
2000 + panelNum;
218 if (months_translated.length
> 0)
220 for (month =
0; month <
12; month++) {
221 var startDate = new Date(tmpyear, month,
15);
223 var item = new Object();
224 item.Type =
"DayEvent";
225 item.StartTime = startDate;
226 item.Summary =
"__temp" + month;
228 var criteria = new Object();
229 criteria.Type =
"CalendarEntry";
230 criteria.Item = item;
233 var result = calendarService.IDataSource.Add(criteria);
234 if (result.ErrorCode)
235 error(result.ErrorMessage);
237 error(
"collectLocales: " + e + ', line ' + e.line);
241 var startTime = new Date(tmpyear,
0,
1);
242 var endTime = new Date(tmpyear,
11,
31);
243 var listFiltering = {
244 Type:'CalendarEntry',
246 StartRange: startTime,
248 SearchText: '__temp',
252 var result = calendarService.IDataSource.GetList(listFiltering);
253 if (result.ErrorCode) {
254 error(result.ErrorMessage);
257 var list = result.ReturnValue;
259 error(e + ', line ' + e.line);
262 var ids = new Array();
268 while (list && (entry = list.getNext()) != undefined) {
269 dateArr = entry.StartTime.replace(/,/g,'').replace(/\./g,':').replace(/ /g,' ').split(' ');
270 var day = dateArr[
1];
271 var month = dateArr[
2];
272 var year = dateArr[
3];
274 // make sure month is set properly
275 if (isNaN(parseInt(day))) {
279 } else if (isNaN(parseInt(year))) {
285 console.info(entry.StartTime + ' -
> ' + month + ' ' + counter);
286 ids[counter] = entry.id;
287 months_translated[month] = counter +
1;
291 error(e + ', line ' + e.line);
296 var criteria = new Object();
297 criteria.Type =
"CalendarEntry";
302 var result = calendarService.IDataSource.Delete(criteria);
303 if (result.ErrorCode)
304 error(result.ErrorMessage);
306 error('deleting temp calendar entries:' + e + ', line ' + e.line);
311 function requestNotification()
313 var criteria = new Object();
314 criteria.Type =
"CalendarEntry";
315 criteria.Filter = new Object();
316 for(var i=
0; i < calendarList.length; i++) {
317 criteria.Filter.CalendarName = calendarList[i];
319 var notificationRequest = calendarService.IDataSource.RequestNotification(criteria, callback);
320 if (notificationRequest.ErrorCode)
321 error('requestNotification failed with error code ' + notificationRequest.ErrorCode);
322 notificationRequests.push(notificationRequest);
324 error(
"requestNotification: " + e + ', line ' + e.line);
328 var criteria2 = new Object();
329 criteria2.Type =
"CalendarEntry";
330 criteria2.Filter = new Object();
331 criteria2.Filter.LocalIdList = new Array();
332 criteria2.Filter.LocalIdList[
0] = settingsCalEntryId;
334 var notificationRequest = calendarService.IDataSource.RequestNotification(criteria2, settingsCallback);
335 if (notificationRequest.ErrorCode)
336 error('requestNotification failed with error code ' + notificationRequest.ErrorCode);
337 notificationRequests.push(notificationRequest);
339 error(
"requestNotification: " + e + ', line ' + e.line);
343 function cancelNotification()
345 for(var i=
0; i < notificationRequests.length; i++) {
347 var result = calendarService.IDataSource.Cancel(notificationRequests[i]);
348 if (result.ErrorCode)
349 error('cancelNotification failed with error code ' + result.ErrorCode);
351 error(
"cancelNotification: " + e + ', line ' + e.line);
356 function callback(transId, eventCode, result)
358 console.info(
"callback(): panelNum: %d transId: %d eventCode: %d result.ErrorCode: %d", panelNum, transId, eventCode, result.ErrorCode);
362 function settingsCallback(transId, eventCode, result)
364 console.info(
"settingsCallback(): panelNum: %d transId: %d eventCode: %d result.ErrorCode: %d", panelNum, transId, eventCode, result.ErrorCode);
368 function parseDate(dateString)
371 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:
372 Wednesday,
26 August,
2009 24:
00:
00
373 Wednesday,
26 August,
2009 12:
00:
00 am
374 Wednesday, August
26,
2009 12:
00:
00 am
375 Wednesday,
2009 August,
26 12:
00:
00 am
376 Wednesday,
2009 August,
28 8.00.00 pm
377 Wednesday,
2009 August,
28 08:
00:
00 PM
380 if (dateString ==
"" || dateString == null)
382 var dateArr = dateString.replace(/,/g,'').replace(/\./g,':').replace(/ /g,' ').split(' ');
383 if (dateArr.length !=
5 && dateArr.length !=
6)
387 var weekDay = dateArr[
0];
388 var day = dateArr[
1];
389 var month = dateArr[
2];
390 var year = dateArr[
3];
391 // make sure month is set properly
392 if (isNaN(parseInt(day))) {
396 } else if (isNaN(parseInt(year))) {
401 // make sure day and year are set properly
402 if (Number(day)
> Number(year)) {
407 month = months_translated[month];
410 var timeArr = dateArr[
4].split(':');
411 if (timeArr.length !=
3)
413 var hours = Number(timeArr[
0]);
414 var minutes = Number(timeArr[
1]);
415 var seconds = Number(timeArr[
2]);
416 if (dateArr.length ==
6 && dateArr[
5].toLowerCase() == 'pm' && hours <
12)
418 if (dateArr.length ==
6 && dateArr[
5].toLowerCase() == 'am' && hours ==
12)
421 var result = new Date(year, month -
1, day, hours, minutes, seconds);
423 // take care of daylight saving time
424 if (config['enableDaylightSaving'].Value) {
426 // determine if date is in summer or winter time
427 var dateSummerTime = isSummertime(result);
429 // work around bug in Nokias calendar api resulting in dates within a different DST to be off by
1 hour
430 if (summertime && !dateSummerTime) {
431 result = new Date(result.getTime() -
1000 *
60 *
60 * config['daylightSavingOffset'].Value); // -
1 hour
432 console.info('parseDate(): fixing time -
1h: ' + result);
434 else 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);
443 // 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"
444 function formatDate(date, format)
446 var day = date.getDate().toString();
447 var month = (date.getMonth() +
1).toString();
448 while (day.length <
2) { day = '
0' + day; }
449 while (month.length <
2) { month = '
0' + month; }
451 if (config['showTodayAsText'].Value && isToday(date))
452 return '
<span class=
"today">' + config['todayText'].Value + '
</span>';
453 if (config['showTodayAsText'].Value && isTomorrow(date))
454 return '
<span class=
"tomorrow">' + config['tomorrowText'].Value + '
</span>';
456 var dateArr = format.replace(/,/g,'').replace(/\./g,':').replace(/ /g,' ').split(' ');
457 if (dateArr.length !=
5 && dateArr.length !=
6) {
458 // we don't know how to format this
459 if (config['dateFormat'].Value == 'auto' || config['dateFormat'].Value == 'DDMM')
460 return day + config['dateSeparator'].Value + month;
462 return month + config['dateSeparator'].Value + day;
466 if (config['dateFormat'].Value == 'MMDD')
468 else if (config['dateFormat'].Value == 'DDMM')
471 // config['dateFormat'].Value == 'auto', try to detect system setting
473 var day_ = dateArr[
1];
474 var month_ = dateArr[
2];
475 var year_ = dateArr[
3];
476 // make sure month is set properly
477 if (isNaN(parseInt(day_))) {
482 } else if (isNaN(parseInt(year_))) {
488 // make sure day and year are set properly
489 if (Number(day_)
> Number(year_))
494 return day + config['dateSeparator'].Value + month;
496 return month + config['dateSeparator'].Value + day;
499 function formatTime(date)
501 // date is a Date() object
502 date.setSeconds(
0); // we don't care about seconds
503 var time = date.toLocaleTimeString().replace(/[\.:]
00/, ''); // remove seconds from string
504 if (time.replace(/\./, ':').split(':')[
0].length <
2)
506 if (config['showNowAsText'].Value && date.getTime() == now.getTime())
507 time = '
<span class=
"now">' + config['nowText'].Value + '
</span>';
511 function updateData()
513 console.info('updateData()');
515 // check if we got additional or less calendars since our last update
516 var newCalendarList = listCalendars();
517 if (newCalendarList.length != calendarList.length) {
518 calendarList = newCalendarList;
519 updateCalendarColors();
520 cancelNotification();
521 requestNotification();
525 // meetings have time
526 // 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
528 summertime = isSummertime(now); // cache summer time info for today
529 var meetingList = [];
530 for(var i=
0; i < calendarList.length; i++) {
531 var meetingListFiltering = {
532 Type:'CalendarEntry',
534 CalendarName: calendarList[i],
535 StartRange: (new Date(now.getFullYear(), now.getMonth(), now.getDate(),
0,
0,
0)),
536 EndRange: (new Date(now.getFullYear(), now.getMonth() + config['monthRange'].Value, now.getDate(),
0,
0,
0))
539 var meetingResult = calendarService.IDataSource.GetList(meetingListFiltering);
540 if (meetingResult.ErrorCode !=
0)
541 throw(
"Error fetching calendar data: " + meetingResult.ErrorCode + ': ' + meetingResult.ErrorMessage);
542 var list = meetingResult.ReturnValue;
543 meetingList = meetingList.concat(listToArray(list, calendarList[i]));
545 console.info(
"updateData(): meetingList.sort()");
546 meetingList.sort(sortCalendarEntries);
548 // todos don't, they start on
00:
00 hrs., but should be visible anyway
549 // this will generate a list of passed todos. We have to check if they have been marked as
"done" yet
550 if (config['includeTodos'].Value) {
551 var todayTodoList = [];
552 for(var i=
0; i < calendarList.length; i++) {
553 var todayTodoListFiltering = {
554 Type:'CalendarEntry',
556 CalendarName: calendarList[i],
558 StartRange: (new Date(now.getFullYear() -
1, now.getMonth(), now.getDate(),
0,
0,
0)),
559 EndRange: (new Date(now.getFullYear(), now.getMonth(), now.getDate(),
0,
0,
1))
562 var todayTodoResult = calendarService.IDataSource.GetList(todayTodoListFiltering);
563 var list = todayTodoResult.ReturnValue;
564 todayTodoList = todayTodoList.concat(listToArray(list, calendarList[i]));
566 console.info(
"updateData(): todayTodoList.sort()");
567 todayTodoList.sort(sortCalendarEntries);
568 var entryLists = [todayTodoList, meetingList];
570 var entryLists = [meetingList];
573 error('loading Calendar items list:' + e + ', line ' + e.line);
582 var fontsize = 'normal';
584 if (config['eventsPerWidget'].Value ==
3) {
586 changeCssClass('.icon', 'width:
20px; height:
20px');
588 else if (config['eventsPerWidget'].Value ==
5) {
590 changeCssClass('.icon', 'width:
10px; height:
10px');
592 else if (config['eventsPerWidget'].Value ==
6) {
594 changeCssClass('.icon', 'width:
8px; height:
8px');
598 changeCssClass('.icon', config['cssStyle_icon'].Value);
599 var entriesHtml = '
<table style=
"font-size:' + fontsize + ';">';
603 max = (panelNum +
1) * config['eventsPerWidget'].Value;
605 max =
30; // we can display a lot more events in fullscreen mode
608 for (var i=
0; i < entryLists.length; i++) {
609 listinfo = listinfo +
" " + entryLists[i].length;
610 var entrieslist =
"";
611 for (var j=
0; j < entryLists[i].length; j++) {
612 entrieslist += entryLists[i][j].Summary +
", ";
614 console.info(
"updateData(): entrieslist: " + entrieslist);
616 console.info(
"updateData(): inner loop, " + entryLists.length +
" lists, [" + listinfo +
"] entries");
618 // the first outer loop iteration is for passed ToDos, the second loop is for all upcomming events (may also include ToDos)
619 for (var i=
0; counter < max && i < entryLists.length; i++) {
620 for (var j=
0; (counter < max) && (j < entryLists[i].length); j++) {
621 entry = entryLists[i][j];
624 // output event info for debugging
625 var entryInfo =
"event: ";
626 for(var k=
0; k < entryFields.length; ++k) {
627 if (entry[entryFields[k]] != undefined) {
628 entryInfo += entryFields[k] +
"=" + entry[entryFields[k]] +
",";
631 console.info(entryInfo);
633 // we don't want ToDos when includeTodos == false or when they are completed
634 if (entry.Type == 'ToDo' && (entry.Status ==
"TodoCompleted" || !config['includeTodos'].Value)) {
635 console.info('skipping ' + entry.id );
640 // make sure that we don't include an event twice (useful for ToDos that might come up twice)
641 if (eventIds[entry.id] ==
1 && entry.Type == 'ToDo') {
642 console.info('skipped (already included) ' + entry.id);
646 eventIds[entry.id] =
1;
648 // summary can be undefined!
649 var Summary = ((entry.Summary == null) ? '' : entry.Summary);
650 if (entry.Type == 'Meeting' && entry.Location != '' && config['showLocation'].Value)
651 Summary += ', ' + entry.Location;
653 // fix by yves: determine start and end dates/times
654 entryStartTime = ((entry.InstanceStartTime == null) ? entry.StartTime : entry.InstanceStartTime);
655 entryEndTime = ((entry.InstanceEndTime == null) ? entry.EndTime : entry.InstanceEndTime);
657 // there can be ToDos that have no date at all!
658 if (entry.Type == 'ToDo' && entry.EndTime == null)
659 entryDate =
""; // this will cause parseDate(entryDate) to return null;
661 entryDate = ((entry.Type == 'ToDo') ? entryEndTime : entryStartTime); // ToDo's use their EndTime, the rest use StartTime
663 // Convert date/time string to Date object
664 var date = parseDate(entryDate);
665 console.info('date: ' + date);
666 var endDate = ((entryEndTime == null) ? null : parseDate(entryEndTime));
667 console.info('endDate: ' + endDate);
669 // check if meeting event has already passed
670 if (entry.Type == 'Meeting') {
671 var compareTime = ((endDate == null) ? date.getTime() : endDate.getTime());
672 if (now.getTime()
> compareTime) {
673 console.info('skipping Meeting (already passed) ' + entry.id);
675 eventIds[entry.id] =
0;
680 // check if anniversary passed (not sure why they are in the list, the query was only for today - nokia?)
681 if (entry.Type == 'Anniversary') {
682 var tmp = new Date(now.getFullYear(), now.getMonth(), now.getDate(),
0,
0,
0);
683 if (date.getTime() < tmp.getTime()) {
684 console.info('skipping Anniversary (already passed) ' + entry.id);
686 eventIds[entry.id] =
0;
691 // fix DayEvents end time. End times are off by
1 Second. It's possible that the event has already passed
692 if (entry.Type == 'DayEvent' && endDate != null) {
693 endDate.setMinutes(endDate.getMinutes() -
1);
694 console.info('fixing DayEvent endDate: ' + endDate);
695 if (now.getTime()
> endDate.getTime()) {
696 console.info('event already passed ' + entry.id);
698 eventIds[entry.id] =
0;
703 // check if the event is currently taking place
704 if (entryStartTime != null && entryEndTime != null && date != null && endDate != null) {
705 // check if we are between start and endtime
706 if ((date.getTime() < now.getTime()) && (now.getTime() < endDate.getTime())) {
707 date = now; // change appointment date/time to now
708 console.info('event is currently taking place: ' + date);
712 // skip events for the first panel in case this is the second one and we're not in fullscreen mode
713 if (mode ==
0 && panelNum
> 0 && counter < panelNum * config['eventsPerWidget'].Value +
1) {
714 console.info('skipping (already in first widget) ' + entry.id);
718 // mark overdue todos
720 if (entry.Type == 'ToDo' && date != null) {
721 var tmp1 = new Date(date.getFullYear(), date.getMonth(), date.getDate(),
0,
0,
0);
722 var tmp2 = new Date(now.getFullYear(), now.getMonth(), now.getDate(),
0,
0,
0);
723 if (tmp1.getTime() < tmp2.getTime()) {
728 // generate html output
729 entriesHtml += '
<tr>';
730 if (config['showCalendarIndicator'].Value && calendarList.length
> 1) {
731 entriesHtml += '
<td><span class=
"calendar' + calendarColors[entry.CalendarName] + '"> </span></td>';
733 entriesHtml += '
<td><img class=
"icon" src=
"' + entry.Type + '.png" /></td>';
735 // some languages have very strange locale date formats, can't parse all those. Also some todos don't have dates at all.
736 entriesHtml += '
<td colspan=
"4"><span class=
"date">' + entryDate + '
</span> ';
738 var weekDay = date.toLocaleDateString().substr(
0,config['weekDayLength'].Value);
739 var time = formatTime(date);
740 var dateStr = formatDate(date, entryDate);
741 if (entry.Type == 'ToDo' && overdue && config['markOverdueTodos'].Value) {
742 dateStr = '
<span class=
"overdue">' + config['overdueText'].Value + '
</span>';
743 entriesHtml += '
<td colspan=
"4" width=
"1px"><span class=
"date">' + dateStr + '
</span> ';
744 } else if (entry.Type == 'ToDo' || entry.Type == 'Anniversary' || entry.Type == 'DayEvent' || entry.Type == 'Reminder') {
745 if ((isToday(date) || isTomorrow(date)) && config['showTodayAsText'].Value) // show weekday if the date string is not text. looks odd otherwise
746 entriesHtml += '
<td colspan=
"4" width=
"1px"><span class=
"date">' + dateStr + '
</span> ';
748 entriesHtml += '
<td class=
"weekDay" width=
"1px">' + weekDay + '
</td><td width=
"1px" class=
"date">' + dateStr + '
</td><td colspan=
"2">';
749 } else if (entry.Type == 'Meeting') {
750 if (config['showCombinedDateTime'].Value) {
752 entriesHtml += '
<td width=
"1px" colspan=
"4"><span class=
"today">' + time + '
</span> ';
753 else if (isTomorrow(date))
754 entriesHtml += '
<td width=
"1px" colspan=
"4"><span class=
"tomorrow">' + dateStr + '
</span> <span class=
"time">' + time + '
</span> ';
756 entriesHtml += '
<td width=
"1px" class=
"weekDay">' + weekDay + '
</td><td width=
"1px" class=
"date">' + dateStr + '
</td><td colspan=
"2">';
758 if ((isToday(date) || isTomorrow(date)) && config['showTodayAsText'].Value)
759 entriesHtml += '
<td colspan=
"4" width=
"1px"><span class=
"today">' + dateStr + '
</span> <span class=
"time">' + time + '
</span> ';
761 entriesHtml += '
<td width=
"1px" class=
"weekDay">' + weekDay + '
</td><td width=
"1px" class=
"date">' + dateStr + '
</td><td width=
"1px" class=
"time">' + time + '
</td><td>';
765 entriesHtml += '
<span class=
"description">' + Summary + '
</span></td></tr>';
768 entriesHtml += '
</table>';
769 if (config['showNothingText'].Value && entriesHtml == '
<table></table>') {
770 var text = config['nothingText'].Value.replace(/%d/, config['monthRange'].Value);
771 entriesHtml = '
<div style=
"width:295px; height:75px; text-align:center; line-height:75px; overflow:visible;">' + text + '
</div>';
773 if (cacheEntriesHtml != entriesHtml) {
775 document.getElementById('calendarList').innerHTML = entriesHtml;
777 document.getElementById('fullscreenCalendarList').innerHTML = entriesHtml;
778 cacheEntriesHtml = entriesHtml;
781 error('displaying list:' + e + ', line ' + e.line);
786 function updateScreen()
788 // check if opening fullscreen
789 if( window.innerHeight
> 91 && mode ==
0) {
791 cacheEntriesHtml = '';
792 document.getElementById('body').style.backgroundImage =
"";
795 else if (window.innerHeight <=
91 && mode !=
0) {
797 cacheEntriesHtml = '';
807 function launchCalendar()
810 widget.openApplication(config['calendarApp'].Value,
"");
811 if (config['hideWidgetOnCalendarOpen'].Value)
814 error('starting Calendar App');
821 console.info('New widget instance starting up...');
824 // call calendar service
825 if (device !=
"undefined")
826 calendarService = device.getServiceObject(
"Service.Calendar",
"IDataSource");
828 throw('device object does not exist');
830 error('loading Calendar service: ' + e + ', line ' + e.line);
834 calendarList = listCalendars();
836 updateCalendarColors();
839 requestNotification();
840 window.setInterval('updateData()',
1000 *
60 * config['updateDataInterval'].Value);
841 document.getElementById(
"settingsTitle").innerHTML = getLocalizedText('menu.settings');
843 if (window.innerHeight
> 91) {
844 mode =
0; // we're starting fullscreen, we set mode to homescreen in order to let updateScreen() do all the work for us
849 console.info(
"init(): updateScreen()");
851 if (config['useBackgroundImage'].Value)
852 // check for screen rotation every
1 secs
853 window.setInterval('updateScreen()',
1000 *
1);
854 console.info(
"init(): finished...");
857 function createMenu()
859 window.menu.setLeftSoftkeyLabel(
"",null);
860 window.menu.setRightSoftkeyLabel(
"",null);
862 var menuSettings = new MenuItem(getLocalizedText('menu.settings'), id++);
863 var menuCallApp = new MenuItem(getLocalizedText('menu.openCalendarApp'), id++);
864 var menuUpdate = new MenuItem(getLocalizedText('menu.update'), id++);
865 var menuAbout = new MenuItem(getLocalizedText('menu.about'), id++);
866 menuSettings.onSelect = showSettings;
867 menuAbout.onSelect = showAbout;
868 menuCallApp.onSelect = launchCalendar;
869 menuUpdate.onSelect = showUpdate;
871 window.menu.append(menuCallApp);
872 window.menu.append(menuSettings);
873 window.menu.append(menuUpdate);
874 window.menu.append(menuAbout);
877 function showSettings()
881 document.getElementById(
"settingsView").style.display =
"block";
882 document.onclick = null;
884 window.menu.setLeftSoftkeyLabel(getLocalizedText('settings.save'), function()
886 for (var key in config) {
887 if (config[key].Type == 'String')
888 config[key].Value = document.forms[
0].elements[
"settings." + key].value;
889 else if (config[key].Type == 'Int') {
890 config[key].Value = parseInt(document.forms[
0].elements[
"settings." + key].value);
891 if (config[key].Value <
0)
892 config[key].Value = config[key].Default;
894 else if (config[key].Type == 'Bool')
895 config[key].Value = document.forms[
0].elements[
"settings." + key].checked;
896 else if (config[key].Type == 'UID')
897 config[key].Value = parseInt(document.forms[
0].elements[
"settings." + key].value);
898 else if (config[key].Type == 'Enum') {
899 config[key].Value = document.forms[
0].elements[
"settings." + key].value;
900 if (config[key].ValidValues.indexOf(config[key].Value) == -
1)
901 config[key].Value = config[key].Default;
912 window.menu.setRightSoftkeyLabel(getLocalizedText('settings.cancel'), function()
918 var settingsHtml = '
<form>';
919 for (var key in config) {
920 if (config[key].Type == 'String') {
922 if (key.substring(
0,
9) ==
"cssStyle_")
923 prefix = getLocalizedText('settings.cssStyle_prefix');
924 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 />';
926 else if (config[key].Type == 'Int')
927 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 />';
928 else if (config[key].Type == 'Bool')
929 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 />';
930 else if (config[key].Type == 'UID')
931 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 />';
932 else if (config[key].Type == 'Enum') {
933 settingsHtml += '
<table><tr><td>' + getLocalizedText('settings.name.' + key) + '
<br /><select name=
"settings.' + key + '" size=
"1">';
934 for(var i =
0; i < config[key].ValidValues.length; i++)
935 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>';
936 settingsHtml += '
</select></div></td>' + printHintBox(getLocalizedText('settings.info.' + key)) + '
<hr />';
939 settingsHtml += '
<input name=
"reset" type=
"button" value=
"' + getLocalizedText('settings.restoreDefaults') + '" onclick=
"javascript:restoreDefaultSettings();showSettings();" />';
940 settingsHtml += '
</form>';
941 document.getElementById(
"settingsList").innerHTML = settingsHtml;
944 function changeCssClass(classname, properties)
946 for(var i =
0; i < document.styleSheets[
0]['cssRules'].length; i++)
948 if (document.styleSheets[
0]['cssRules'][i].selectorText == classname) {
949 document.styleSheets[
0].deleteRule(i);
950 document.styleSheets[
0].insertRule(classname + ' { ' + properties + ' }', document.styleSheets[
0]['cssRules'].length);
956 function updateCssClasses()
958 for(var key in config) {
959 changeCssClass(getLocalizedText('settings.name.' + key), config[key].Value);
963 function getSettingsCalEntryId()
965 if (settingsCalEntryId == null) {
966 // check if entry already exists
967 var listFiltering = {
968 Type:'CalendarEntry',
970 StartRange: new Date(
2000,
0,
1),
971 EndRange: new Date(
2000,
0,
1),
972 SearchText: 'ComingNext Settings|',
976 var result = calendarService.IDataSource.GetList(listFiltering);
977 if (result.ErrorCode) {
978 error(result.ErrorMessage);
981 var list = result.ReturnValue;
982 var entry = list.getNext();
983 if (entry != undefined) {
984 settingsCalEntryId = entry.LocalId;
985 console.info(
"settingsCalEntryId=" + settingsCalEntryId);
987 else { // create settings item
988 var item = new Object();
989 item.Type =
"DayEvent";
990 item.StartTime = new Date(
2000,
0,
1);
991 item.Summary =
"ComingNext Settings|";
993 var criteria = new Object();
994 criteria.Type =
"CalendarEntry";
995 criteria.Item = item;
998 var result = calendarService.IDataSource.Add(criteria);
999 if (result.ErrorCode)
1000 error(result.ErrorMessage);
1002 error(
"getSettingsCalEntryId: " + e + ', line ' + e.line);
1005 getSettingsCalEntryId();
1010 function restoreDefaultSettings()
1012 for (var key in config)
1013 config[key].Value = config[key].Default;
1016 function loadSettings()
1018 getSettingsCalEntryId();
1019 var listFiltering = {
1020 Type:'CalendarEntry',
1022 LocalId: settingsCalEntryId
1025 var result = calendarService.IDataSource.GetList(listFiltering);
1026 if (result.ErrorCode) {
1027 error(result.ErrorMessage);
1030 var entry = result.ReturnValue.getNext();
1031 if (entry != undefined) {
1032 console.info(
"Loading Settings...");
1033 // only reload settings if they chanced since the last reload
1034 if (settingsCache != entry.Summary)
1036 restoreDefaultSettings();
1037 var stringlist = entry.Summary.split(
"|");
1038 // skip the first two entries, those contain header and version info
1039 for(var i =
2; i < stringlist.length -
1; i++) {
1040 var pair = stringlist[i].split('=');
1042 var value = pair[
1];
1043 console.info('stringlist: ' + key + '=\'' + value + '\'');
1044 if (config[key].Type == 'Int')
1045 config[key].Value = Number(value);
1046 else if (config[key].Type == 'String')
1047 config[key].Value = value;
1048 else if (config[key].Type == 'Bool')
1049 config[key].Value = (value == 'true')
1050 else if (config[key].Type == 'Enum')
1051 config[key].Value = value;
1052 else if (config[key].Type == 'UID')
1053 config[key].Value = Number(value);
1055 settingsCache = entry.Summary;
1059 console.info(
"Settings already cached and did not change");
1063 error(
"Failed to load settings, calendar entry could not be found");
1067 function saveSettings()
1069 getSettingsCalEntryId();
1070 var item = new Object();
1071 item.Type =
"DayEvent";
1072 item.StartTime = new Date(
2000,
0,
1);
1073 item.LocalId = settingsCalEntryId;
1074 item.Summary =
"ComingNext Settings|" + version +
"|";
1076 for (var key in config) {
1077 if (config[key].Type == 'Int')
1078 item.Summary += key +
"=" + config[key].Value.toString() +
"|";
1079 else if (config[key].Type == 'String')
1080 item.Summary += key +
"=" + config[key].Value +
"|";
1081 else if (config[key].Type == 'Bool')
1082 item.Summary += key +
"=" + (config[key].Value ? 'true' : 'false') +
"|";
1083 else if (config[key].Type == 'Enum')
1084 item.Summary += key +
"=" + config[key].Value +
"|";
1085 else if (config[key].Type == 'UID')
1086 item.Summary += key +
"=" + config[key].Value.toString() +
"|";
1088 settingsCache = item.Summary;
1090 var criteria = new Object();
1091 criteria.Type =
"CalendarEntry";
1092 criteria.Item = item;
1094 console.info(
"Saving settings to calendar entry: " + item.Summary);
1096 var result = calendarService.IDataSource.Add(criteria);
1097 if (result.ErrorCode)
1098 error(result.ErrorMessage);
1100 error(
"saveSettings: " + e + ', line ' + e.line);
1104 function toggleVisibility(elementId)
1106 if (document.getElementById(elementId).style.display ==
"none")
1107 document.getElementById(elementId).style.display =
"block";
1109 document.getElementById(elementId).style.display =
"none";
1113 function printHintBox(text)
1116 return '
<td width=
"1%" align=
"right" onclick=
"javascript:toggleVisibility(\'info' + uniqueId + '\')">' + getLocalizedText('settings.help') + '
</td></tr></table>'+
1117 '
<div class=
"settingsInfo" id=
"info' + uniqueId + '">' + text + '
</div>';
1120 function showAbout()
1124 document.getElementById(
"aboutView").style.display =
"block";
1125 document.onclick = null;
1127 window.menu.setLeftSoftkeyLabel(
" ", function(){});
1128 window.menu.setRightSoftkeyLabel(getLocalizedText('softkey.back'), function()
1134 //document.getElementById(
"aboutView").innerHTML = 'aboutView';
1135 document.getElementById(
"name").innerHTML =
"Coming Next " + version;
1138 function updateFullscreen()
1142 function showFullscreen()
1144 console.info(
"showFullscreen()");
1146 document.getElementById(
"fullscreenView").style.display =
"block";
1147 document.getElementById('body').className =
"backgroundFullscreen";
1148 document.onclick = launchCalendar;
1153 function getBackgroundImage()
1156 if (config['backgroundImageLocation'].Value == config['backgroundImageLocation'].ValidValues[
0]) // internal
1157 bgImage = 'background_' + orientation + '.png';
1159 bgImage = 'C:/Data/background_' + panelNum + '_' + orientation + '.png';
1163 function updateHomescreen()
1165 if (config['useBackgroundImage'].Value) {
1166 // check for screen rotation
1167 if (orientation != 'portrait' && screen.width ==
360 && screen.height ==
640) {
1168 window.widget.prepareForTransition(
"fade");
1169 orientation = 'portrait';
1170 document.getElementById('body').style.backgroundImage = 'url(' + getBackgroundImage() + ')';
1171 document.getElementById('body').style.backgroundColor = 'none';
1172 window.widget.performTransition();
1173 } else if (orientation != 'landscape' && screen.width ==
640 && screen.height ==
360) {
1174 window.widget.prepareForTransition(
"fade");
1175 orientation = 'landscape';
1176 document.getElementById('body').style.backgroundImage = 'url(' + getBackgroundImage() + ')';
1177 document.getElementById('body').style.backgroundColor = 'none';
1178 window.widget.performTransition();
1180 else if (document.getElementById('body').style.backgroundImage ==
"")
1182 document.getElementById('body').style.backgroundImage = 'url(' + getBackgroundImage() + ')';
1187 function showHomescreen()
1189 console.info(
"showHomescreen()");
1191 document.getElementById(
"homescreenView").style.display =
"block";
1192 document.getElementById('body').className =
"background";
1193 document.onclick = null;
1197 function getLocalizedText(p_Txt)
1199 if (localizedText[p_Txt])
1200 return localizedText[p_Txt];
1202 return 'ERROR: missing translation for ' + p_Txt;
1205 function showUpdate()
1209 document.getElementById(
"updateView").style.display =
"block";
1210 document.onclick = null;
1212 window.menu.setLeftSoftkeyLabel(getLocalizedText('update.checknow'), function(){
1215 window.menu.setRightSoftkeyLabel(getLocalizedText('softkey.back'), function()
1221 document.getElementById(
"currentVersion").innerHTML = getLocalizedText(
"update.current") + version;
1225 function checkForUpdate()
1227 // asynch XHR to server url
1228 reqV = new XMLHttpRequest();
1229 reqV.onreadystatechange = checkForUpdateCallback;
1230 document.getElementById(
"updateDiv").innerHTML = getLocalizedText(
"update.checking");
1231 reqV.open(
"GET", versionURL, true);
1232 reqV.setRequestHeader(
"If-Modified-Since",
"Sat, 1 Jan 2000 00:00:00 GMT" ); // disable caching
1236 function checkForUpdateCallback()
1238 if (reqV.readyState ==
4) {
1239 if (reqV.status ==
200) {
1240 var resultXml = reqV.responseText;
1242 var div = document.getElementById(
"tmp");
1243 div.innerHTML = resultXml;
1244 var newVersion = div.getElementsByTagName('version')[
0].innerHTML;
1245 var newVersionURL = div.getElementsByTagName('url')[
0].innerHTML;
1247 if (version != newVersion) {
1248 document.getElementById(
"updateDiv").innerHTML = getLocalizedText(
"update.download").replace(/%
1/, newVersion).replace(/%
2/, newVersionURL);
1251 document.getElementById(
"updateDiv").innerHTML = getLocalizedText(
"update.nonewversion");
1256 document.getElementById(
"updateDiv").innerHTML = getLocalizedText(
"update.error") + reqV.status +
" " + reqV.responseText;
1261 function hideViews()
1263 document.getElementById(
"homescreenView").style.display =
"none";
1264 document.getElementById(
"fullscreenView").style.display =
"none";
1265 document.getElementById(
"aboutView").style.display =
"none";
1266 document.getElementById(
"settingsView").style.display =
"none";
1267 document.getElementById(
"updateView").style.display =
"none";
1270 function listCalendars()
1276 DefaultCalendar: false
1280 var calendarsResult = calendarService.IDataSource.GetList(criteria);
1281 if (calendarsResult.ErrorCode !=
0)
1282 throw(
"Error fetching list of calendars: " + calendarsResult.ErrorCode + ': ' + calendarsResult.ErrorMessage);
1283 var calendarListIterator = calendarsResult.ReturnValue;
1288 while (( item = calendarListIterator.getNext()) != undefined ) {
1289 calendars[count++] = item;
1291 console.info(
"Available Calendars: " + calendars.join(
", "));
1294 error('listing calendars:' + e + ', line ' + e.line);
1299 // Copies all objects and their properties to an array. Data is copied so nothing gets lost when the reference is removed
1300 // 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
1301 function listToArray(list, calendarName)
1303 var array = new Array();
1306 while (( item = list.getNext()) != undefined ) {
1307 var itemCopy = new Object();
1308 for(var i=
0; i < entryFields.length; i++) {
1309 itemCopy[entryFields[i]] = item[entryFields[i]];
1311 // for some reason, the CalendarName property is never correctly queried, so we assign it manually here
1312 if (!itemCopy['CalendarName']) {
1313 itemCopy['CalendarName'] = calendarName;
1315 array.push(itemCopy);
1316 txt += array[array.length -
1].Summary +
", ";
1318 console.info(
"listToArray(): " + txt);
1322 function sortCalendarEntries(a, b)
1325 console.info(
"sortCalendarEntries(" + a.Summary +
"," + b.Summary +
")");
1327 if (a.InstanceStartTime != null) {
1328 atime = a.InstanceStartTime;
1330 else if (a.StartTime != null) {
1331 atime = a.StartTime;
1333 else if (a.InstanceEndTime != null) {
1334 atime = a.InstanceEndTime;
1336 else if (a.EndTime != null) {
1340 if (b.InstanceStartTime != null) {
1341 btime = b.InstanceStartTime;
1343 else if (b.StartTime != null) {
1344 btime = b.StartTime;
1346 else if (b.InstanceEndTime != null) {
1347 btime = b.InstanceEndTime;
1349 else if (b.EndTime != null) {
1353 if (atime && btime) {
1355 atime = parseDate(atime);
1356 btime = parseDate(btime);
1358 // sort by date & time
1359 if (atime < btime) {
1362 else if (atime
> btime) {
1366 else if (a.Type != b.Type) {
1367 if (a.Type < b.Type) {
1370 else if (a.Type
> b.Type) {
1374 // sort by description
1375 else if (a.Summary && b.Summary && a.Summary != b.Summary) {
1376 if (a.Summary < b.Summary) {
1379 else if (a.Summary
> b.Summary) {
1388 function updateCalendarColors()
1391 calendarColors = [];
1392 if (calendarList.length
> maxColors) {
1393 console.info(
"updateCalendarColors(): Warning: more calendars than available indicator colors");
1395 for(var i=
0; i < calendarList.length; i++) {
1396 calendarColors[calendarList[i]] = (i % maxColors) +
1;
1402 <style type=
"text/css">
1403 table { margin:
0px; padding:
0px; border-spacing:
0px; }
1404 td { padding:
0px
5px
0px
0px; white-space:nowrap; overflow:hidden; }
1405 hr { color:#ffffff; background-color:#ffffff; height:
1px; text-align:left; border-style:none; }
1406 .settingsInfo { display:none; font-style:italic; }
1407 .title { font-weight:bold; font-size:
14pt; }
1408 .textInput { width:
90%; }
1409 .credits { margin-left:
40px; text-indent: -
20px; margin-bottom:
0px; }
1410 #homescreenView { width:
315px; height:
91px; overflow:hidden; }
1411 #calendarList { position:absolute; left:
5px; top:
4px; width:
295px; height:
75px; overflow:hidden; }
1412 #name { text-align:center; }
1413 #appicon { display: block; margin-left: auto; margin-right: auto; margin-top:
10px; }
1414 #smallappicon { width:
22px; height:
22px; margin-right:
10px; float:left; }
1419 <body id=
"body" class=
"background">
1420 <div id=
"homescreenView">
1421 <div id=
"calendarList"></div>
1423 <div id=
"fullscreenView" style=
"display:none;">
1424 <img src=
"Icon.png" id=
"smallappicon">
1425 <h1 class=
"title">Coming Next
</h1>
1427 <div id=
"fullscreenCalendarList">loading...
</div>
1429 <div id=
"settingsView" style=
"display:none">
1430 <img src=
"Icon.png" id=
"smallappicon">
1431 <h1 id=
"settingsTitle" class=
"title">Settings
</h1>
1433 <div id=
"settingsList"></div>
1435 <div id=
"aboutView" style=
"display:none">
1436 <img src=
"Icon.png" id=
"appicon">
1437 <h1 id=
"name">Coming Next
</h1>
1439 <p>Created by Dr. Cochambre and Michael Prager.
</p>
1440 <p>Contributions:
</p>
1441 <p class=
"credits">Paul Moore (bug fixes, new features and code cleanup)
</p>
1442 <p class=
"credits">Manfred Hanselmann (DST support)
</p>
1443 <p class=
"credits">Christophe Milsent (translation support & french translation)
</p>
1444 <p class=
"credits">Flavio Nathan (portuguese-brazilian translation)
</p>
1445 <p class=
"credits">Tokeda (russian translation)
</p>
1446 <p>This software is open source and licensed under the GPLv3.
</p>
1447 <p>Visit
<a href=
"http://sourceforge.net/projects/comingnext">sourceforge.net/projects/comingnext
</a> for free updates.
</p>
1450 <div id=
"updateView" style=
"display:none">
1451 <img src=
"Icon.png" id=
"smallappicon">
1452 <h1 class=
"title">Check for update
</h1>
1454 <div id=
"currentVersion">Coming Next ??
</div>
1455 <div id=
"updateDiv"></div>
1456 <div id=
"tmp" style=
"display:none;"></div>