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 { }
23 <script type=
"text/javascript" src=
"localizedTextStrings.js" charset=
"utf-8" />
26 // valid types for the config object are 'Int', 'Bool', 'String', 'Enum', 'UID'
28 monthRange: { Type: 'Int', Default:
2, Value:
2,},
29 includeTodos: { Type: 'Bool', Default: true, Value: true,},
30 useBackgroundImage: { Type: 'Bool', Default: true, Value: true,},
31 backgroundImageLocation: { Type: 'Enum', Default: 'internal', Value: 'internal', ValidValues: ['internal', 'external']},
32 showCombinedDateTime: { Type: 'Bool', Default: false, Value: false,},
33 showLocation: { Type: 'Bool', Default: true, Value: true,},
34 showTodayAsText: { Type: 'Bool', Default: true, Value: true,},
35 todayText: { Type: 'String', Default: getLocalizedText('settings.default.todayText'), Value: getLocalizedText('settings.default.todayText'),},
36 tomorrowText: { Type: 'String', Default: getLocalizedText('settings.default.tomorrowText'), Value: getLocalizedText('settings.default.tomorrowText'),},
37 showNowAsText: { Type: 'Bool', Default: true, Value: true,},
38 nowText: { Type: 'String', Default: getLocalizedText('settings.default.nowText'), Value: getLocalizedText('settings.default.nowText'),},
39 markOverdueTodos: { Type: 'Bool', Default: true, Value: true,},
40 overdueText: {Type: 'String', Default: getLocalizedText('settings.default.overdueText'), Value: getLocalizedText('settings.default.overdueText'),},
41 dateSeparator: { Type: 'String', Default: getLocalizedText('settings.default.dateSeparator'), Value: getLocalizedText('settings.default.dateSeparator'),},
42 dateFormat: { Type: 'Enum', Default: 'auto', Value: 'auto', ValidValues: ['auto', 'DDMM', 'MMDD'],},
43 weekDayLength: { Type: 'Int', Default:
2, Value:
2,},
44 updateDataInterval: { Type: 'Int', Default:
5, Value:
5,},
45 calendarApp: { Type: 'UID', Default:
0x10005901, Value:
0x10005901,},
46 eventsPerWidget: { Type: 'Int', Default:
4, Value:
4,},
47 showNothingText: { Type: 'Bool', Default: true, Value: true,},
48 nothingText: { Type: 'String', Default: getLocalizedText('settings.default.nothingText'), Value: getLocalizedText('settings.default.nothingText'),},
49 enableDaylightSaving: { Type: 'Bool', Default: true, Value: true,},
50 hideWidgetOnCalendarOpen: { Type: 'Bool', Default: false, Value: false,},
51 cssStyle_background: { Type: 'String', Default: 'color:#ffffff; background-color:#
000000', Value: 'color:#ffffff; background-color:#
000000',},
52 cssStyle_backgroundFullscreen: { Type: 'String', Default: 'color:#ffffff; background-color:#
000000', Value: 'color:#ffffff; background-color:#
000000',},
53 cssStyle_weekDay: { Type: 'String', Default: '', Value: '',},
54 cssStyle_date: { Type: 'String', Default: '', Value: '',},
55 cssStyle_today: { Type: 'String', Default: 'color:#ff0000', Value: 'color:#ff0000',},
56 cssStyle_tomorrow: { Type: 'String', Default: 'color:#
0000ff', Value: 'color:#
0000ff',},
57 cssStyle_time: { Type: 'String', Default: '', Value: '',},
58 cssStyle_now: { Type: 'String', Default: 'color:#ff00ff', Value: 'color:#ff00ff',},
59 cssStyle_description: { Type: 'String', Default: '', Value: '',},
60 cssStyle_icon: { Type: 'String', Default: 'width:
15px; height:
15px', Value: 'width:
15px; height:
15px',},
61 cssStyle_overdue: { Type: 'String', Default: 'color:#ffff00', Value: 'color:#ffff00',},
66 //-------------------------------------------------------
67 // Nothing of interest from here on...
68 //-------------------------------------------------------
69 var panelNum =
0; // use
1 for second panel
71 var versionURL =
"http://comingnext.sourceforge.net/version.xml";
72 var calendarService = null;
73 var cacheEntriesHtml = [];
74 var months_translated = [];
77 var mode =
0; //
0 = homescreen,
1 = fullscreen,
2 = settings,
3 = about,
4 = check for update
79 var settingsCalEntryId = null;
80 var settingsCache = null;
81 var notificationRequest1;
82 var notificationRequest2;
84 // vars for daylight saving time
85 var daylightsavingWinter =
0;
86 var daylightsavingSummer =
0;
87 var summertime = false;
90 window.onresize = updateScreen;
91 window.onshow = updateScreen;
93 function isLeapYear( year ) {
94 if (( year %
4 ==
0 && year %
100 !=
0 ) || year %
400 ==
0 )
100 function calcLeapYear(year, days)
102 if (isLeapYear(year))
108 function subToSunday(myDate, year, days, prevMonthDays)
110 for (i = myDate.getDay(); i
> 0 ;i--)
112 days -= prevMonthDays;
113 days = isLeapYear(year) ? --days : days;
117 function calcDaylightSaving()
119 var thisYearS = new Date(now.getFullYear(),
3,
0,
0,
0,
0 );
120 var thisYearW = new Date(now.getFullYear(),
10,
0,
0,
0,
0 );
121 var nextYearS = new Date(now.getFullYear() +
1,
3,
0,
0,
0,
0 );
122 var nextYearW = new Date(now.getFullYear() +
1,
10,
0,
0,
0,
0 );
126 thisYearSDays = nextYearSDays =
90;
127 thisYearWDays = nextYearWDays =
304;
129 thisYearSDays = calcLeapYear(now.getFullYear(), thisYearSDays);
130 thisYearWDays = calcLeapYear(now.getFullYear(), thisYearWDays);
131 nextYearSDays = calcLeapYear(now.getFullYear() +
1, nextYearSDays);
132 nextYearWDays = calcLeapYear(now.getFullYear() +
1, nextYearWDays);
134 thisYearSDays = subToSunday(thisYearS, now.getFullYear(), thisYearSDays,
59);
135 thisYearWDays = subToSunday(thisYearW, now.getFullYear(), thisYearWDays,
273);
136 nextYearSDays = subToSunday(nextYearS, now.getFullYear() +
1, nextYearSDays,
59);
137 nextYearWDays = subToSunday(nextYearW, now.getFullYear() +
1, nextYearWDays,
273);
139 daylightsavingSummer = new Date (now.getFullYear(),
03-
1, thisYearSDays,
2,
0,
0);
140 daylightsavingWinter = new Date (now.getFullYear(),
10-
1, thisYearWDays,
2,
0,
0);
141 if (daylightsavingSummer < now) {
142 daylightsavingSummer = new Date (now.getFullYear()+
1,
03-
1, nextYearSDays,
2,
0,
0);
145 if (daylightsavingWinter < now) {
146 daylightsavingWinter = new Date (now.getFullYear()+
1,
10-
1, nextYearWDays,
2,
0,
0);
149 if (summer && !winter)
155 function error(message)
157 console.info('Error: ' + message);
158 document.getElementById(
"calendarList").innerHTML = 'Error: ' + message;
161 function areDatesEqual(date1, date2)
163 return (date1.getFullYear() == date2.getFullYear() &&
164 date1.getMonth() == date2.getMonth() &&
165 date1.getDate() == date2.getDate());
168 function isTomorrow(date)
170 // tommorow = now +
1 day
171 // ToDo: some days can be shorter as
24 hours(daylight saving change day)
172 return areDatesEqual(date, new Date (now.getTime() +
24*
60*
60*
1000));
175 function isToday(date)
177 return areDatesEqual(date, now);
180 function collectLocales()
182 var tmpyear =
2000 + panelNum;
185 if (months_translated.length
> 0)
187 for (month =
0; month <
12; month++) {
188 var startDate = new Date(tmpyear, month,
15);
190 var item = new Object();
191 item.Type =
"DayEvent";
192 item.StartTime = startDate;
193 item.Summary =
"__temp" + month;
195 var criteria = new Object();
196 criteria.Type =
"CalendarEntry";
197 criteria.Item = item;
200 var result = calendarService.IDataSource.Add(criteria);
201 if (result.ErrorCode)
202 error(result.ErrorMessage);
204 error(
"collectLocales: " + e + ', line ' + e.line);
208 var startTime = new Date(tmpyear,
0,
1);
209 var endTime = new Date(tmpyear,
11,
31);
210 var listFiltering = {
211 Type:'CalendarEntry',
213 StartRange: startTime,
215 SearchText: '__temp',
219 var result = calendarService.IDataSource.GetList(listFiltering);
220 if (result.ErrorCode) {
221 error(result.ErrorMessage);
224 var list = result.ReturnValue;
226 error(e + ', line ' + e.line);
229 var ids = new Array();
235 while (list && (entry = list.getNext()) != undefined) {
236 dateArr = entry.StartTime.replace(/,/g,'').replace(/\./g,':').replace(/ /g,' ').split(' ');
237 var day = dateArr[
1];
238 var month = dateArr[
2];
239 var year = dateArr[
3];
241 // make sure month is set properly
242 if (isNaN(parseInt(day))) {
246 } else if (isNaN(parseInt(year))) {
252 console.info(entry.StartTime + ' -
> ' + month + ' ' + counter);
253 ids[counter] = entry.id;
254 months_translated[month] = counter +
1;
258 error(e + ', line ' + e.line);
263 var criteria = new Object();
264 criteria.Type =
"CalendarEntry";
269 var result = calendarService.IDataSource.Delete(criteria);
270 if (result.ErrorCode)
271 error(result.ErrorMessage);
273 error('deleting temp calendar entries:' + e + ', line ' + e.line);
278 function requestNotification()
280 var criteria = new Object();
281 criteria.Type =
"CalendarEntry";
284 notificationRequest1 = calendarService.IDataSource.RequestNotification(criteria, callback);
285 if (notificationRequest1.ErrorCode)
286 error('requestNotification failed with error code ' + notificationRequest1.ErrorCode);
288 error(
"requestNotification: " + e + ', line ' + e.line);
291 var criteria2 = new Object();
292 criteria2.Type =
"CalendarEntry";
293 criteria2.Filter = new Object();
294 criteria2.Filter.LocalIdList = new Array();
295 criteria2.Filter.LocalIdList[
0] = settingsCalEntryId;
298 notificationRequest2 = calendarService.IDataSource.RequestNotification(criteria2, settingsCallback);
299 if (notificationRequest2.ErrorCode)
300 error('requestNotification failed with error code ' + notificationRequest2.ErrorCode);
302 error(
"requestNotification: " + e + ', line ' + e.line);
306 function callback(transId, eventCode, result)
308 console.info(
"callback(): panelNum: %d transId: %d eventCode: %d result.ErrorCode: %d", panelNum, transId, eventCode, result.ErrorCode);
312 function settingsCallback(transId, eventCode, result)
314 console.info(
"settingsCallback(): panelNum: %d transId: %d eventCode: %d result.ErrorCode: %d", panelNum, transId, eventCode, result.ErrorCode);
318 function parseDate(dateString)
321 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:
322 Wednesday,
26 August,
2009 24:
00:
00
323 Wednesday,
26 August,
2009 12:
00:
00 am
324 Wednesday, August
26,
2009 12:
00:
00 am
325 Wednesday,
2009 August,
26 12:
00:
00 am
326 Wednesday,
2009 August,
28 8.00.00 pm
327 Wednesday,
2009 August,
28 08:
00:
00 PM
330 if (dateString ==
"" || dateString == null)
332 var dateArr = dateString.replace(/,/g,'').replace(/\./g,':').replace(/ /g,' ').split(' ');
333 if (dateArr.length !=
5 && dateArr.length !=
6)
337 var weekDay = dateArr[
0];
338 var day = dateArr[
1];
339 var month = dateArr[
2];
340 var year = dateArr[
3];
341 // make sure month is set properly
342 if (isNaN(parseInt(day))) {
346 } else if (isNaN(parseInt(year))) {
351 // make sure day and year are set properly
352 if (Number(day)
> Number(year)) {
357 month = months_translated[month];
360 var timeArr = dateArr[
4].split(':');
361 if (timeArr.length !=
3)
363 var hours = Number(timeArr[
0]);
364 var minutes = Number(timeArr[
1]);
365 var seconds = Number(timeArr[
2]);
366 if (dateArr.length ==
6 && dateArr[
5].toLowerCase() == 'pm' && hours <
12)
368 if (dateArr.length ==
6 && dateArr[
5].toLowerCase() == 'am' && hours ==
12)
371 console.info('year=' + year + ' month=' + month + ' day=' + day + ' hours=' + hours + ' minutes=' + minutes+ ' seconds=' + seconds);
373 // take care of daylight saving time
374 if (config['enableDaylightSaving'].Value) {
375 var date = new Date(year, month -
1, day, hours, minutes, seconds);
376 if (summertime && date
> daylightsavingWinter && date < daylightsavingSummer)
378 else if (!summertime && date
> daylightsavingSummer && date < daylightsavingWinter)
382 return new Date(year, month -
1, day, hours, minutes, seconds);
385 // 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"
386 function formatDate(date, format)
388 var day = date.getDate().toString();
389 var month = (date.getMonth() +
1).toString();
390 while (day.length <
2) { day = '
0' + day; }
391 while (month.length <
2) { month = '
0' + month; }
393 if (config['showTodayAsText'].Value && isToday(date))
394 return '
<span class=
"today">' + config['todayText'].Value + '
</span>';
395 if (config['showTodayAsText'].Value && isTomorrow(date))
396 return '
<span class=
"tomorrow">' + config['tomorrowText'].Value + '
</span>';
398 var dateArr = format.replace(/,/g,'').replace(/\./g,':').replace(/ /g,' ').split(' ');
399 if (dateArr.length !=
5 && dateArr.length !=
6) {
400 // we don't know how to format this
401 if (config['dateFormat'].Value == 'auto' || config['dateFormat'].Value == 'DDMM')
402 return day + config['dateSeparator'].Value + month;
404 return month + config['dateSeparator'].Value + day;
408 if (config['dateFormat'].Value == 'MMDD')
410 else if (config['dateFormat'].Value == 'DDMM')
413 // config['dateFormat'].Value == 'auto', try to detect system setting
415 var day_ = dateArr[
1];
416 var month_ = dateArr[
2];
417 var year_ = dateArr[
3];
418 // make sure month is set properly
419 if (isNaN(parseInt(day_))) {
424 } else if (isNaN(parseInt(year_))) {
430 // make sure day and year are set properly
431 if (Number(day_)
> Number(year_))
436 return day + config['dateSeparator'].Value + month;
438 return month + config['dateSeparator'].Value + day;
441 function formatTime(date)
443 // date is a Date() object
444 date.setSeconds(
0); // we don't care about seconds
445 var time = date.toLocaleTimeString().replace(/[\.:]
00/, ''); // remove seconds from string
446 if (time.replace(/\./, ':').split(':')[
0].length <
2)
448 if (config['showNowAsText'].Value && date.getTime() == now.getTime())
449 time = '
<span class=
"now">' + config['nowText'].Value + '
</span>';
453 function updateData()
455 console.info('updateData()');
456 calcDaylightSaving();
458 // meetings have time
459 // 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
461 var meetingListFiltering = {
462 Type:'CalendarEntry',
464 StartRange: (new Date(now.getFullYear(), now.getMonth(), now.getDate(),
0,
0,
0)),
465 EndRange: (new Date(now.getFullYear(), now.getMonth() + config['monthRange'].Value, now.getDate(),
0,
0,
0))
468 var meetingResult = calendarService.IDataSource.GetList(meetingListFiltering);
469 if (meetingResult.ErrorCode !=
0)
470 throw(
"Error fetching calendar data: " + meetingResult.ErrorCode + ': ' + meetingResult.ErrorMessage);
471 var meetingList = meetingResult.ReturnValue;
473 // todos don't, they start on
00:
00 hrs., but should be visible anyway
474 // this will generate a list of passed todos. We have to check if they have been marked as
"done" yet
475 if (config['includeTodos'].Value) {
476 var todayTodoListFiltering = {
477 Type:'CalendarEntry',
480 StartRange: (new Date(now.getFullYear() -
1, now.getMonth(), now.getDate(),
0,
0,
0)),
481 EndRange: (new Date(now.getFullYear(), now.getMonth(), now.getDate(),
0,
0,
1))
484 var todayTodoResult = calendarService.IDataSource.GetList(todayTodoListFiltering);
485 var todayTodoList = todayTodoResult.ReturnValue;
486 var entryLists = [todayTodoList, meetingList];
488 var entryLists = [meetingList];
491 error('loading Calendar items list:' + e + ', line ' + e.line);
500 var fontsize = 'normal';
502 if (config['eventsPerWidget'].Value ==
3) {
504 changeCssClass('.icon', 'width:
20px; height:
20px');
506 else if (config['eventsPerWidget'].Value ==
5) {
508 changeCssClass('.icon', 'width:
10px; height:
10px');
510 else if (config['eventsPerWidget'].Value ==
6) {
512 changeCssClass('.icon', 'width:
8px; height:
8px');
516 changeCssClass('.icon', config['cssStyle_icon'].Value);
517 var entriesHtml = '
<table style=
"font-size:' + fontsize + ';">';
521 max = (panelNum +
1) * config['eventsPerWidget'].Value;
523 max =
30; // we can display a lot more events in fullscreen mode
525 // the first outer loop iteration is for passed ToDos, the second loop is for all upcomming events (may also include ToDos)
526 for (var i=
0; counter < max && i < entryLists.length; i++) {
527 while (counter < max && (entry = entryLists[i].getNext()) != undefined) {
530 // output event info for debugging
532 'event: Id=' + entry.id +
533 ',Type=' + entry.Type +
534 ',Summary=' + entry.Summary +
535 ',Location=' + entry.Location +
536 ',Status=' + entry.Status +
537 ',StartTime=' + entry.StartTime +
538 ',EndTime=' + entry.EndTime +
539 ',InstanceStartTime=' + entry.InstanceStartTime +
540 ',InstanceEndTime=' + entry.InstanceEndTime
543 // we don't want ToDos when includeTodos == false or when they are completed
544 if (entry.Type == 'ToDo' && (entry.Status ==
"TodoCompleted" || !config['includeTodos'].Value)) {
545 console.info('skipping ' + entry.id );
550 // make sure that we don't include an event twice (useful for ToDos that might come up twice)
551 if (eventIds[entry.id] ==
1 && entry.Type == 'ToDo') {
552 console.info('skipped (already included) ' + entry.id);
556 eventIds[entry.id] =
1;
558 // summary can be undefined!
559 var Summary = ((entry.Summary == null) ? '' : entry.Summary);
560 if (entry.Type == 'Meeting' && entry.Location != '' && config['showLocation'].Value)
561 Summary += ', ' + entry.Location;
563 // fix by yves: determine start and end dates/times
564 entryStartTime = ((entry.InstanceStartTime == null) ? entry.StartTime : entry.InstanceStartTime);
565 entryEndTime = ((entry.InstanceEndTime == null) ? entry.EndTime : entry.InstanceEndTime);
567 // there can be ToDos that have no date at all!
568 if (entry.Type == 'ToDo' && entry.EndTime == null)
569 entryDate =
""; // this will cause parseDate(entryDate) to return null;
571 entryDate = ((entry.Type == 'ToDo') ? entryEndTime : entryStartTime); // ToDo's use their EndTime, the rest use StartTime
573 // Convert date/time string to Date object
574 var date = parseDate(entryDate);
575 console.info('date: ' + date);
576 var endDate = ((entryEndTime == null) ? null : parseDate(entryEndTime));
577 console.info('endDate: ' + endDate);
579 // check if meeting event has already passed
580 if (entry.Type == 'Meeting') {
581 var compareTime = ((endDate == null) ? date.getTime() : endDate.getTime());
582 if (now.getTime()
> compareTime) {
583 console.info('skipping Meeting (already passed) ' + entry.id);
585 eventIds[entry.id] =
0;
590 // check if anniversary passed (not sure why they are in the list, the query was only for today - nokia?)
591 if (entry.Type == 'Anniversary') {
592 var tmp = new Date(now.getFullYear(), now.getMonth(), now.getDate(),
0,
0,
0);
593 if (date.getTime() < tmp.getTime()) {
594 console.info('skipping Anniversary (already passed) ' + entry.id);
596 eventIds[entry.id] =
0;
601 // fix DayEvents end time. End times are off by
1 Second. It's possible that the event has already passed
602 if (entry.Type == 'DayEvent' && endDate != null) {
603 endDate.setMinutes(endDate.getMinutes() -
1);
604 console.info('fixing DayEvent endDate: ' + endDate);
605 if (now.getTime()
> endDate.getTime()) {
606 console.info('event already passed ' + entry.id);
608 eventIds[entry.id] =
0;
613 // check if the event is currently taking place
614 if (entryStartTime != null && entryEndTime != null && date != null && endDate != null) {
615 // check if we are between start and endtime
616 if ((date.getTime() < now.getTime()) && (now.getTime() < endDate.getTime())) {
617 date = now; // change appointment date/time to now
618 console.info('event is currently taking place: ' + date);
622 // skip events for the first panel in case this is the second one and we're not in fullscreen mode
623 if (mode ==
0 && panelNum
> 0 && counter < panelNum * config['eventsPerWidget'].Value +
1) {
624 console.info('skipping (already in first widget) ' + entry.id);
628 // mark overdue todos
630 if (entry.Type == 'ToDo' && date != null) {
631 var tmp1 = new Date(date.getFullYear(), date.getMonth(), date.getDate(),
0,
0,
0);
632 var tmp2 = new Date(now.getFullYear(), now.getMonth(), now.getDate(),
0,
0,
0);
633 if (tmp1.getTime() < tmp2.getTime()) {
638 // generate html output
639 entriesHtml += '
<tr><td><img class=
"icon" src=
"' + entry.Type + '.png" /></td>';
641 // some languages have very strange locale date formats, can't parse all those. Also some todos don't have dates at all.
642 entriesHtml += '
<td colspan=
"4"><span class=
"date">' + entryDate + '
</span> ';
644 var weekDay = date.toLocaleDateString().substr(
0,config['weekDayLength'].Value);
645 var time = formatTime(date);
646 var dateStr = formatDate(date, entryDate);
647 if (entry.Type == 'ToDo' && overdue && config['markOverdueTodos'].Value) {
648 dateStr = '
<span class=
"overdue">' + config['overdueText'].Value + '
</span>';
649 entriesHtml += '
<td colspan=
"4" width=
"1px"><span class=
"date">' + dateStr + '
</span> ';
650 } else if (entry.Type == 'ToDo' || entry.Type == 'Anniversary' || entry.Type == 'DayEvent' || entry.Type == 'Reminder') {
651 if ((isToday(date) || isTomorrow(date)) && config['showTodayAsText'].Value) // show weekday if the date string is not text. looks odd otherwise
652 entriesHtml += '
<td colspan=
"4" width=
"1px"><span class=
"date">' + dateStr + '
</span> ';
654 entriesHtml += '
<td class=
"weekDay" width=
"1px">' + weekDay + '
</td><td width=
"1px" class=
"date">' + dateStr + '
</td><td colspan=
"2">';
655 } else if (entry.Type == 'Meeting') {
656 if (config['showCombinedDateTime'].Value) {
658 entriesHtml += '
<td width=
"1px" colspan=
"4"><span class=
"today">' + time + '
</span> ';
659 else if (isTomorrow(date))
660 entriesHtml += '
<td width=
"1px" colspan=
"4"><span class=
"tomorrow">' + dateStr + '
</span> <span class=
"time">' + time + '
</span> ';
662 entriesHtml += '
<td width=
"1px" class=
"weekDay">' + weekDay + '
</td><td width=
"1px" class=
"date">' + dateStr + '
</td><td colspan=
"2">';
664 if ((isToday(date) || isTomorrow(date)) && config['showTodayAsText'].Value)
665 entriesHtml += '
<td colspan=
"4" width=
"1px"><span class=
"today">' + dateStr + '
</span> <span class=
"time">' + time + '
</span> ';
667 entriesHtml += '
<td width=
"1px" class=
"weekDay">' + weekDay + '
</td><td width=
"1px" class=
"date">' + dateStr + '
</td><td width=
"1px" class=
"time">' + time + '
</td><td>';
671 entriesHtml += '
<span class=
"description">' + Summary + '
</span></td></tr>';
674 entriesHtml += '
</table>';
675 if (config['showNothingText'].Value && entriesHtml == '
<table></table>') {
676 var text = config['nothingText'].Value.replace(/%d/, config['monthRange'].Value);
677 entriesHtml = '
<div style=
"width:295px; height:75px; text-align:center; line-height:75px; overflow:visible;">' + text + '
</div>';
679 if (cacheEntriesHtml != entriesHtml) {
681 document.getElementById('calendarList').innerHTML = entriesHtml;
683 document.getElementById('fullscreenCalendarList').innerHTML = entriesHtml;
684 cacheEntriesHtml = entriesHtml;
687 error('displaying list:' + e + ', line ' + e.line);
692 function updateScreen()
694 // check if opening fullscreen
695 if( window.innerHeight
> 91 && mode ==
0) {
697 cacheEntriesHtml = '';
698 document.getElementById('body').style.backgroundImage =
"";
701 else if (window.innerHeight <=
91 && mode !=
0) {
703 cacheEntriesHtml = '';
713 function launchCalendar()
716 widget.openApplication(config['calendarApp'].Value,
"");
717 if (config['hideWidgetOnCalendarOpen'].Value)
720 error('starting Calendar App');
727 console.info('New widget instance starting up...');
730 // call calendar service
731 if (device !=
"undefined")
732 calendarService = device.getServiceObject(
"Service.Calendar",
"IDataSource");
734 throw('device object does not exist');
736 error('loading Calendar service: ' + e + ', line ' + e.line);
743 requestNotification();
744 window.setInterval('updateData()',
1000 *
60 * config['updateDataInterval'].Value);
749 if (config['useBackgroundImage'].Value)
750 // check for screen rotation every
1 secs
751 window.setInterval('updateScreen()',
1000 *
1);
754 function createMenu()
756 window.menu.setLeftSoftkeyLabel(
"",null);
757 window.menu.setRightSoftkeyLabel(
"",null);
759 var menuSettings = new MenuItem(getLocalizedText('menu.settings'), id++);
760 var menuCallApp = new MenuItem(getLocalizedText('menu.openCalendarApp'), id++);
761 var menuUpdate = new MenuItem(getLocalizedText('menu.update'), id++);
762 var menuAbout = new MenuItem(getLocalizedText('menu.about'), id++);
763 menuSettings.onSelect = showSettings;
764 menuAbout.onSelect = showAbout;
765 menuCallApp.onSelect = launchCalendar;
766 menuUpdate.onSelect = showUpdate;
768 window.menu.append(menuCallApp);
769 window.menu.append(menuSettings);
770 window.menu.append(menuUpdate);
771 window.menu.append(menuAbout);
774 function showSettings()
778 document.getElementById(
"settingsView").style.display =
"block";
779 document.onclick = null;
781 window.menu.setLeftSoftkeyLabel(getLocalizedText('settings.save'), function()
783 for (var key in config) {
784 if (config[key].Type == 'String')
785 config[key].Value = document.forms[
0].elements[
"settings." + key].value;
786 else if (config[key].Type == 'Int') {
787 config[key].Value = parseInt(document.forms[
0].elements[
"settings." + key].value);
788 if (config[key].Value <
0)
789 config[key].Value = config[key].Default;
791 else if (config[key].Type == 'Bool')
792 config[key].Value = document.forms[
0].elements[
"settings." + key].checked;
793 else if (config[key].Type == 'UID')
794 config[key].Value = parseInt(document.forms[
0].elements[
"settings." + key].value);
795 else if (config[key].Type == 'Enum') {
796 config[key].Value = document.forms[
0].elements[
"settings." + key].value;
797 if (config[key].ValidValues.indexOf(config[key].Value) == -
1)
798 config[key].Value = config[key].Default;
809 window.menu.setRightSoftkeyLabel(getLocalizedText('settings.cancel'), function()
815 var settingsHtml = '
<form>';
816 for (var key in config) {
817 if (config[key].Type == 'String') {
819 if (key.substring(
0,
9) ==
"cssStyle_")
820 prefix = getLocalizedText('settings.cssStyle_prefix');
821 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 />';
823 else if (config[key].Type == 'Int')
824 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 />';
825 else if (config[key].Type == 'Bool')
826 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 />';
827 else if (config[key].Type == 'UID')
828 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 />';
829 else if (config[key].Type == 'Enum') {
830 settingsHtml += '
<table><tr><td>' + getLocalizedText('settings.name.' + key) + '
<br /><select name=
"settings.' + key + '" size=
"1">';
831 for(var i =
0; i < config[key].ValidValues.length; i++)
832 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>';
833 settingsHtml += '
</select></div></td>' + printHintBox(getLocalizedText('settings.info.' + key)) + '
<hr />';
836 settingsHtml += '
<input name=
"reset" type=
"button" value=
"' + getLocalizedText('settings.restoreDefaults') + '" onclick=
"javascript:restoreDefaultSettings();showSettings();" />';
837 settingsHtml += '
</form>';
838 document.getElementById(
"settingsList").innerHTML = settingsHtml;
841 function changeCssClass(classname, properties)
843 for(var i =
0; i < document.styleSheets[
0]['cssRules'].length; i++)
845 if (document.styleSheets[
0]['cssRules'][i].selectorText == classname) {
846 document.styleSheets[
0].deleteRule(i);
847 document.styleSheets[
0].insertRule(classname + ' { ' + properties + ' }', document.styleSheets[
0]['cssRules'].length);
853 function updateCssClasses()
855 for(var key in config) {
856 changeCssClass(getLocalizedText('settings.name.' + key), config[key].Value);
860 function getSettingsCalEntryId()
862 if (settingsCalEntryId == null) {
863 // check if entry already exists
864 var listFiltering = {
865 Type:'CalendarEntry',
867 StartRange: new Date(
2000,
0,
1),
868 EndRange: new Date(
2000,
0,
1),
869 SearchText: 'ComingNext Settings|',
873 var result = calendarService.IDataSource.GetList(listFiltering);
874 if (result.ErrorCode) {
875 error(result.ErrorMessage);
878 var list = result.ReturnValue;
879 var entry = list.getNext();
880 if (entry != undefined) {
881 settingsCalEntryId = entry.LocalId;
882 console.info(
"settingsCalEntryId=" + settingsCalEntryId);
884 else { // create settings item
885 var item = new Object();
886 item.Type =
"DayEvent";
887 item.StartTime = new Date(
2000,
0,
1);
888 item.Summary =
"ComingNext Settings|";
890 var criteria = new Object();
891 criteria.Type =
"CalendarEntry";
892 criteria.Item = item;
895 var result = calendarService.IDataSource.Add(criteria);
896 if (result.ErrorCode)
897 error(result.ErrorMessage);
899 error(
"getSettingsCalEntryId: " + e + ', line ' + e.line);
902 getSettingsCalEntryId();
907 function restoreDefaultSettings()
909 for (var key in config)
910 config[key].Value = config[key].Default;
913 function loadSettings()
915 getSettingsCalEntryId();
916 var listFiltering = {
917 Type:'CalendarEntry',
919 LocalId: settingsCalEntryId
922 var result = calendarService.IDataSource.GetList(listFiltering);
923 if (result.ErrorCode) {
924 error(result.ErrorMessage);
927 var entry = result.ReturnValue.getNext();
928 if (entry != undefined) {
929 // only reload settings if they chanced since the last reload
930 if (settingsCache != entry.Summary)
932 restoreDefaultSettings();
933 var stringlist = entry.Summary.split(
"|");
934 // skip the first two entries, those contain header and version info
935 for(var i =
2; i < stringlist.length -
1; i++) {
936 var pair = stringlist[i].split('=');
939 console.info('stringlist: ' + key + '=\'' + value + '\'');
940 if (config[key].Type == 'Int')
941 config[key].Value = Number(value);
942 else if (config[key].Type == 'String')
943 config[key].Value = value;
944 else if (config[key].Type == 'Bool')
945 config[key].Value = (value == 'true')
946 else if (config[key].Type == 'Enum')
947 config[key].Value = value;
948 else if (config[key].Type == 'UID')
949 config[key].Value = Number(value);
951 settingsCache = entry.Summary;
956 error(
"Failed to load settings, calendar entry could not be found");
960 function saveSettings()
962 getSettingsCalEntryId();
963 var item = new Object();
964 item.Type =
"DayEvent";
965 item.StartTime = new Date(
2000,
0,
1);
966 item.LocalId = settingsCalEntryId;
967 item.Summary =
"ComingNext Settings|" + version +
"|";
969 for (var key in config) {
970 if (config[key].Type == 'Int')
971 item.Summary += key +
"=" + config[key].Value.toString() +
"|";
972 else if (config[key].Type == 'String')
973 item.Summary += key +
"=" + config[key].Value +
"|";
974 else if (config[key].Type == 'Bool')
975 item.Summary += key +
"=" + (config[key].Value ? 'true' : 'false') +
"|";
976 else if (config[key].Type == 'Enum')
977 item.Summary += key +
"=" + config[key].Value +
"|";
978 else if (config[key].Type == 'UID')
979 item.Summary += key +
"=" + config[key].Value.toString() +
"|";
981 settingsCache = item.Summary;
983 var criteria = new Object();
984 criteria.Type =
"CalendarEntry";
985 criteria.Item = item;
987 console.info(
"Saving settings to calendar entry: " + item.Summary);
989 var result = calendarService.IDataSource.Add(criteria);
990 if (result.ErrorCode)
991 error(result.ErrorMessage);
993 error(
"saveSettings: " + e + ', line ' + e.line);
997 function toggleVisibility(elementId)
999 if (document.getElementById(elementId).style.display ==
"none")
1000 document.getElementById(elementId).style.display =
"block";
1002 document.getElementById(elementId).style.display =
"none";
1006 function printHintBox(text)
1009 return '
<td width=
"1%" align=
"right" onclick=
"javascript:toggleVisibility(\'info' + uniqueId + '\')">' + getLocalizedText('settings.help') + '
</td></tr></table>'+
1010 '
<div class=
"settingsInfo" id=
"info' + uniqueId + '">' + text + '
</div>';
1013 function showAbout()
1017 document.getElementById(
"aboutView").style.display =
"block";
1018 document.onclick = null;
1020 window.menu.setLeftSoftkeyLabel(
" ", function(){});
1021 window.menu.setRightSoftkeyLabel(getLocalizedText('softkey.back'), function()
1027 //document.getElementById(
"aboutView").innerHTML = 'aboutView';
1028 document.getElementById(
"name").innerHTML =
"Coming Next " + version;
1031 function updateFullscreen()
1035 function showFullscreen()
1038 document.getElementById(
"fullscreenView").style.display =
"block";
1039 document.getElementById('body').className =
"backgroundFullscreen";
1040 document.onclick = launchCalendar;
1045 function getBackgroundImage()
1048 if (config['backgroundImageLocation'].Value == config['backgroundImageLocation'].ValidValues[
0]) // internal
1049 bgImage = 'background_' + orientation + '.png';
1051 bgImage = 'C:/Data/background_' + panelNum + '_' + orientation + '.png';
1055 function updateHomescreen()
1057 if (config['useBackgroundImage'].Value) {
1058 // check for screen rotation
1059 if (orientation != 'portrait' && screen.width ==
360 && screen.height ==
640) {
1060 window.widget.prepareForTransition(
"fade");
1061 orientation = 'portrait';
1062 document.getElementById('body').style.backgroundImage = 'url(' + getBackgroundImage() + ')';
1063 document.getElementById('body').style.backgroundColor = 'none';
1064 window.widget.performTransition();
1065 } else if (orientation != 'landscape' && screen.width ==
640 && screen.height ==
360) {
1066 window.widget.prepareForTransition(
"fade");
1067 orientation = 'landscape';
1068 document.getElementById('body').style.backgroundImage = 'url(' + getBackgroundImage() + ')';
1069 document.getElementById('body').style.backgroundColor = 'none';
1070 window.widget.performTransition();
1072 else if (document.getElementById('body').style.backgroundImage ==
"")
1074 document.getElementById('body').style.backgroundImage = 'url(' + getBackgroundImage() + ')';
1079 function showHomescreen()
1082 document.getElementById(
"homescreenView").style.display =
"block";
1083 document.getElementById('body').className =
"background";
1084 document.onclick = null;
1088 function getLocalizedText(p_Txt)
1090 if (localizedText[p_Txt])
1091 return localizedText[p_Txt];
1093 return 'ERROR: missing translation for ' + p_Txt;
1096 function showUpdate()
1100 document.getElementById(
"updateView").style.display =
"block";
1101 document.onclick = null;
1103 window.menu.setLeftSoftkeyLabel(getLocalizedText('update.checknow'), function(){
1106 window.menu.setRightSoftkeyLabel(getLocalizedText('softkey.back'), function()
1112 document.getElementById(
"currentVersion").innerHTML = getLocalizedText(
"update.current") + version;
1116 function checkForUpdate()
1118 // asynch XHR to server url
1119 reqV = new XMLHttpRequest();
1120 reqV.onreadystatechange = checkForUpdateCallback;
1121 document.getElementById(
"updateDiv").innerHTML = getLocalizedText(
"update.checking");
1122 reqV.open(
"GET", versionURL, true);
1123 reqV.setRequestHeader(
"If-Modified-Since",
"Sat, 1 Jan 2000 00:00:00 GMT" ); // disable caching
1127 function checkForUpdateCallback()
1129 if (reqV.readyState ==
4) {
1130 if (reqV.status ==
200) {
1131 var resultXml = reqV.responseText;
1133 var div = document.getElementById(
"tmp");
1134 div.innerHTML = resultXml;
1135 var newVersion = div.getElementsByTagName('version')[
0].innerHTML;
1136 var newVersionURL = div.getElementsByTagName('url')[
0].innerHTML;
1138 if (version != newVersion) {
1139 document.getElementById(
"updateDiv").innerHTML = getLocalizedText(
"update.download").replace(/%
1/, newVersion).replace(/%
2/, newVersionURL);
1142 document.getElementById(
"updateDiv").innerHTML = getLocalizedText(
"update.nonewversion");
1147 document.getElementById(
"updateDiv").innerHTML = getLocalizedText(
"update.error") + reqV.status +
" " + reqV.responseText;
1152 function hideViews()
1154 document.getElementById(
"homescreenView").style.display =
"none";
1155 document.getElementById(
"fullscreenView").style.display =
"none";
1156 document.getElementById(
"aboutView").style.display =
"none";
1157 document.getElementById(
"settingsView").style.display =
"none";
1158 document.getElementById(
"updateView").style.display =
"none";
1162 <style type=
"text/css">
1163 table { margin:
0px; padding:
0px; border-spacing:
0px; }
1164 td { padding:
0px
5px
0px
0px; white-space:nowrap; overflow:hidden; }
1165 hr { color:#ffffff; background-color:#ffffff; height:
1px; text-align:left; border-style:none; }
1166 .settingsInfo { display:none; font-style:italic; }
1167 .title { font-weight:bold; font-size:
14pt; }
1168 .textInput { width:
90%; }
1169 .credits { margin-left:
40px; text-indent: -
20px; margin-bottom:
0px; }
1170 #homescreenView { width:
315px; height:
91px; overflow:hidden; }
1171 #calendarList { position:absolute; left:
10px; top:
4px; width:
295px; height:
75px; overflow:hidden; }
1172 #name { text-align:center; }
1173 #appicon { display: block; margin-left: auto; margin-right: auto; margin-top:
10px; }
1174 #smallappicon { width:
22px; height:
22px; margin-right:
10px; float:left; }
1179 <body id=
"body" class=
"background">
1180 <div id=
"homescreenView">
1181 <div id=
"calendarList"></div>
1183 <div id=
"fullscreenView" style=
"display:none;">
1184 <img src=
"Icon.png" id=
"smallappicon">
1185 <h1 class=
"title">Coming Next
</h1>
1187 <div id=
"fullscreenCalendarList">loading...
</div>
1189 <div id=
"settingsView" style=
"display:none">
1190 <img src=
"Icon.png" id=
"smallappicon">
1191 <h1 id=
"settingsTitle" class=
"title">Settings
</h1>
1193 <div id=
"settingsList"></div>
1195 <div id=
"aboutView" style=
"display:none">
1196 <img src=
"Icon.png" id=
"appicon">
1197 <h1 id=
"name">Coming Next
</h1>
1199 <p>Created by Dr. Cochambre and Michael Prager.
</p>
1200 <p>Contributions:
</p>
1201 <p class=
"credits">Paul Moore (bug fixes, new features and code cleanup)
</p>
1202 <p class=
"credits">Manfred Hanselmann (DST support)
</p>
1203 <p class=
"credits">Christophe Milsent (translation support & french translation
</p>
1204 <p class=
"credits">Flavio Nathan (portuguese-brazilian translation
</p>
1205 <p>This software is open source and licensed under the GPLv3.
</p>
1206 <p>Visit sourceforge.net/projects/comingnext for free updates.
</p>
1209 <div id=
"updateView" style=
"display:none">
1210 <img src=
"Icon.png" id=
"smallappicon">
1211 <h1 class=
"title">Check for update
</h1>
1213 <div id=
"currentVersion">Coming Next ??
</div>
1214 <div id=
"updateDiv"></div>
1215 <div id=
"tmp" style=
"display:none;"></div>