]> code.delx.au - comingnext/blob - comingNextB/index.html
added "check for updates" feature
[comingnext] / comingNextB / index.html
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">
4 <head>
5
6 <title>Coming Next</title>
7
8 <style type="text/css">
9 /* The following classes can be modified by widget settings */
10 .background { }
11 .backgroundFullscreen { }
12 .weekDay { }
13 .date { }
14 .today { }
15 .tomorrow { }
16 .time { }
17 .now { }
18 .description { }
19 .icon { }
20 </style>
21
22 <script type="text/javascript" src="localizedTextStrings.js" charset="utf-8" />
23
24 <script>
25 // valid types for the config object are 'Int', 'Bool', 'String', 'Enum', 'UID'
26 var config = {
27 monthRange: { Type: 'Int', Default: 2, Value: 2,},
28 includeTodos: { Type: 'Bool', Default: true, Value: true,},
29 useBackgroundImage: { Type: 'Bool', Default: true, Value: true,},
30 showCombinedDateTime: { Type: 'Bool', Default: false, Value: false,},
31 showLocation: { Type: 'Bool', Default: true, Value: true,},
32 showTodayAsText: { Type: 'Bool', Default: true, Value: true,},
33 todayText: { Type: 'String', Default: getLocalizedText('settings.default.todayText'), Value: getLocalizedText('settings.default.todayText'),},
34 tomorrowText: { Type: 'String', Default: getLocalizedText('settings.default.tomorrowText'), Value: getLocalizedText('settings.default.tomorrowText'),},
35 showNowAsText: { Type: 'Bool', Default: true, Value: true,},
36 nowText: { Type: 'String', Default: getLocalizedText('settings.default.nowText'), Value: getLocalizedText('settings.default.nowText'),},
37 dateSeparator: { Type: 'String', Default: getLocalizedText('settings.default.dateSeparator'), Value: getLocalizedText('settings.default.dateSeparator'),},
38 dateFormat: { Type: 'Enum', Default: 'auto', Value: 'auto', ValidValues: ['auto', 'DDMM', 'MMDD'],},
39 weekDayLength: { Type: 'Int', Default: 2, Value: 2,},
40 updateDataInterval: { Type: 'Int', Default: 5, Value: 5,},
41 calendarApp: { Type: 'UID', Default: 0x10005901, Value: 0x10005901,},
42 eventsPerWidget: { Type: 'Int', Default: 4, Value: 4,},
43 showNothingText: { Type: 'Bool', Default: true, Value: true,},
44 nothingText: { Type: 'String', Default: getLocalizedText('settings.default.nothingText'), Value: getLocalizedText('settings.default.nothingText'),},
45 enableDaylightSaving: { Type: 'Bool', Default: true, Value: true,},
46 hideWidgetOnCalendarOpen: { Type: 'Bool', Default: false, Value: false,},
47 cssStyle_background: { Type: 'String', Default: 'color:#ffffff; background-color:#000000', Value: 'color:#ffffff; background-color:#000000',},
48 cssStyle_backgroundFullscreen: { Type: 'String', Default: 'color:#ffffff; background-color:#000000', Value: 'color:#ffffff; background-color:#000000',},
49 cssStyle_weekDay: { Type: 'String', Default: '', Value: '',},
50 cssStyle_date: { Type: 'String', Default: '', Value: '',},
51 cssStyle_today: { Type: 'String', Default: 'color:#ff0000', Value: 'color:#ff0000',},
52 cssStyle_tomorrow: { Type: 'String', Default: 'color:#0000ff', Value: 'color:#0000ff',},
53 cssStyle_time: { Type: 'String', Default: '', Value: '',},
54 cssStyle_now: { Type: 'String', Default: 'color:#ff00ff', Value: 'color:#ff00ff',},
55 cssStyle_description: { Type: 'String', Default: '', Value: '',},
56 cssStyle_icon: { Type: 'String', Default: 'width:15px; height:15px', Value: 'width:15px; height:15px',},
57 }
58
59
60
61 //-------------------------------------------------------
62 // Nothing of interest from here on...
63 //-------------------------------------------------------
64 var panelNum = 1; // use 1 for second panel
65 var version = "1.25";
66 var versionURL = "http://comingnext.sourceforge.net/version.xml";
67 var calendarService = null;
68 var cacheEntriesHtml = [];
69 var months_translated = [];
70 var orientation = '';
71 var now = new Date();
72 var mode = 0; // 0 = homescreen, 1 = fullscreen, 2 = settings, 3 = about, 4 = check for update
73 var reqV = null;
74
75 // vars for daylight saving time
76 var daylightsavingWinter = 0;
77 var daylightsavingSummer = 0;
78 var summertime = false;
79
80 window.onload = init;
81 window.onresize = updateScreen;
82 window.onshow = updateScreen;
83
84 function isLeapYear( year ) {
85 if (( year % 4 == 0 && year % 100 != 0 ) || year % 400 == 0 )
86 return true;
87 else
88 return false;
89 }
90
91 function calcLeapYear(year, days)
92 {
93 if (isLeapYear(year))
94 return ++days;
95 else
96 return days;
97 }
98
99 function subToSunday(myDate, year, days, prevMonthDays)
100 {
101 for (i = myDate.getDay(); i > 0 ;i--)
102 days--;
103 days -= prevMonthDays;
104 days = isLeapYear(year) ? --days : days;
105 return days;
106 }
107
108 function calcDaylightSaving()
109 {
110 var thisYearS = new Date(now.getFullYear(), 3, 0, 0, 0, 0 );
111 var thisYearW = new Date(now.getFullYear(), 10, 0, 0, 0, 0 );
112 var nextYearS = new Date(now.getFullYear() + 1, 3, 0, 0, 0, 0 );
113 var nextYearW = new Date(now.getFullYear() + 1, 10, 0, 0, 0, 0 );
114 var summer = false;
115 var winter = false;
116
117 thisYearSDays = nextYearSDays = 90;
118 thisYearWDays = nextYearWDays = 304;
119
120 thisYearSDays = calcLeapYear(now.getFullYear(), thisYearSDays);
121 thisYearWDays = calcLeapYear(now.getFullYear(), thisYearWDays);
122 nextYearSDays = calcLeapYear(now.getFullYear() + 1, nextYearSDays);
123 nextYearWDays = calcLeapYear(now.getFullYear() + 1, nextYearWDays);
124
125 thisYearSDays = subToSunday(thisYearS, now.getFullYear(), thisYearSDays, 59);
126 thisYearWDays = subToSunday(thisYearW, now.getFullYear(), thisYearWDays, 273);
127 nextYearSDays = subToSunday(nextYearS, now.getFullYear() + 1, nextYearSDays, 59);
128 nextYearWDays = subToSunday(nextYearW, now.getFullYear() + 1, nextYearWDays, 273);
129
130 daylightsavingSummer = new Date (now.getFullYear(), 03-1, thisYearSDays, 2, 0, 0);
131 daylightsavingWinter = new Date (now.getFullYear(), 10-1, thisYearWDays, 2, 0, 0);
132 if (daylightsavingSummer < now) {
133 daylightsavingSummer = new Date (now.getFullYear()+1, 03-1, nextYearSDays, 2, 0, 0);
134 var summer = true;
135 }
136 if (daylightsavingWinter < now) {
137 daylightsavingWinter = new Date (now.getFullYear()+1, 10-1, nextYearWDays, 2, 0, 0);
138 var winter = true;
139 }
140 if (summer && !winter)
141 summertime = true;
142 else
143 summertime = false;
144 }
145
146 function error(message)
147 {
148 console.info('Error: ' + message);
149 document.getElementById("calendarList").innerHTML = 'Error: ' + message;
150 }
151
152 function isToday(date)
153 {
154 if (date.getDate() == now.getDate() && date.getMonth() == now.getMonth())
155 return true;
156 return false;
157 }
158
159 function isTomorrow(date)
160 {
161 if ((date.getDate() == now.getDate() + 1 && date.getMonth() == now.getMonth()) ||
162 (date.getDate() == 0 && date.getMonth() == now.getMonth() + 1) ||
163 (date.getDate() == 0 && date.getMonth() == now.getMonth() + 1 && date.getYear() == now.getYear() + 1))
164 return true;
165 return false;
166 }
167
168 function collectLocales()
169 {
170 var tmpyear = ((panelNum == 0) ? 2000 : 2001);
171 var month = 0;
172
173 if (months_translated.length > 0)
174 return;
175 for (month = 0; month < 12; month++) {
176 var startDate = new Date(tmpyear, month, 15);
177
178 var item = new Object();
179 item.Type = "DayEvent";
180 item.StartTime = startDate;
181 item.Summary = "__temp" + month;
182
183 var criteria = new Object();
184 criteria.Type = "CalendarEntry";
185 criteria.Item = item;
186
187 try {
188 var result = calendarService.IDataSource.Add(criteria);
189 if (result.ErrorCode)
190 error(result.ErrorMessage);
191 } catch (e) {
192 error("collectLocales: " + e + ', line ' + e.line);
193 }
194 }
195 try {
196 var startTime = new Date(tmpyear,0,1);
197 var endTime = new Date(tmpyear,11,31);
198 var listFiltering = {
199 Type:'CalendarEntry',
200 Filter:{
201 StartRange: startTime,
202 EndRange: endTime,
203 SearchText: '__temp',
204 Type: 'DayEvent'
205 }
206 }
207 var result = calendarService.IDataSource.GetList(listFiltering);
208 if (result.ErrorCode) {
209 error(result.ErrorMessage);
210 return;
211 }
212 var list = result.ReturnValue;
213 } catch(e) {
214 error(e + ', line ' + e.line);
215 return;
216 }
217 var ids = new Array();
218 try {
219 var entry;
220 var counter = 0;
221 var dateArr = [];
222
223 while (list && (entry = list.getNext()) != undefined) {
224 dateArr = entry.StartTime.replace(/,/g,'').replace(/\./g,':').replace(/ /g,' ').split(' ');
225 var day = dateArr[1];
226 var month = dateArr[2];
227 var year = dateArr[3];
228
229 // make sure month is set properly
230 if (isNaN(parseInt(day))) {
231 var tmp = day;
232 day = month;
233 month = tmp;
234 } else if (isNaN(parseInt(year))) {
235 var tmp = year;
236 year = month;
237 month = tmp;
238 }
239
240 console.info(entry.StartTime + ' -> ' + month + ' ' + counter);
241 ids[counter] = entry.id;
242 months_translated[month] = counter + 1;
243 counter++;
244 }
245 } catch(e) {
246 error(e + ', line ' + e.line);
247 return;
248 }
249 console.info(ids);
250 try {
251 var criteria = new Object();
252 criteria.Type = "CalendarEntry";
253 criteria.Data = {
254 IdList: ids
255 }
256
257 var result = calendarService.IDataSource.Delete(criteria);
258 if (result.ErrorCode)
259 error(result.ErrorMessage);
260 } catch(e) {
261 error('deleting temp calendar entries:' + e + ', line ' + e.line);
262 return;
263 }
264 }
265
266 function requestNotification()
267 {
268 var criteria = new Object();
269 criteria.Type = "CalendarEntry";
270
271 try {
272 var result = calendarService.IDataSource.RequestNotification(criteria, callback);
273 if (result.ErrorCode)
274 error('loading Calendar items list');
275 } catch (e) {
276 error("requestNotification: " + e + ', line ' + e.line);
277 }
278 }
279
280 function callback(transId, eventCode, result)
281 {
282 updateData();
283 }
284
285 function parseDate(dateString)
286 {
287 /*
288 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:
289 Wednesday, 26 August, 2009 24:00:00
290 Wednesday, 26 August, 2009 12:00:00 am
291 Wednesday, August 26, 2009 12:00:00 am
292 Wednesday, 2009 August, 26 12:00:00 am
293 Wednesday, 2009 August, 28 8.00.00 pm
294 Wednesday, 2009 August, 28 08:00:00 PM
295 */
296
297 if (dateString == "" || dateString == null)
298 return null;
299 var dateArr = dateString.replace(/,/g,'').replace(/\./g,':').replace(/ /g,' ').split(' ');
300 if (dateArr.length != 5 && dateArr.length != 6)
301 return null;
302
303 // parse date
304 var weekDay = dateArr[0];
305 var day = dateArr[1];
306 var month = dateArr[2];
307 var year = dateArr[3];
308 // make sure month is set properly
309 if (isNaN(parseInt(day))) {
310 var tmp = day;
311 day = month;
312 month = tmp;
313 } else if (isNaN(parseInt(year))) {
314 var tmp = year;
315 year = month;
316 month = tmp;
317 }
318 // make sure day and year are set properly
319 if (Number(day) > Number(year)) {
320 var tmp = year;
321 year = day;
322 day = tmp;
323 }
324 month = months_translated[month];
325
326 // parse time
327 var timeArr = dateArr[4].split(':');
328 if (timeArr.length != 3)
329 return null;
330 var hours = Number(timeArr[0]);
331 var minutes = Number(timeArr[1]);
332 var seconds = Number(timeArr[2]);
333 if (dateArr.length == 6 && dateArr[5].toLowerCase() == 'pm' && hours < 12)
334 hours += 12;
335 if (dateArr.length == 6 && dateArr[5].toLowerCase() == 'am' && hours == 12)
336 hours = 0;
337
338 console.info('year=' + year + ' month=' + month + ' day=' + day + ' hours=' + hours + ' minutes=' + minutes+ ' seconds=' + seconds);
339
340 // take care of daylight saving time
341 if (config['enableDaylightSaving'].Value) {
342 var date = new Date(year, month - 1, day, hours, minutes, seconds);
343 if (summertime && date > daylightsavingWinter && date < daylightsavingSummer)
344 hours -= 1;
345 else if (!summertime && date > daylightsavingSummer && date < daylightsavingWinter)
346 hours += 1;
347 }
348
349 return new Date(year, month - 1, day, hours, minutes, seconds);
350 }
351
352 // 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"
353 function formatDate(date, format)
354 {
355 var day = date.getDate().toString();
356 var month = (date.getMonth() + 1).toString();
357 while (day.length < 2) { day = '0' + day; }
358 while (month.length < 2) { month = '0' + month; }
359
360 if (config['showTodayAsText'].Value && isToday(date))
361 return '<span class="today">' + config['todayText'].Value + '</span>';
362 if (config['showTodayAsText'].Value && isTomorrow(date))
363 return '<span class="tomorrow">' + config['tomorrowText'].Value + '</span>';
364
365 var dateArr = format.replace(/,/g,'').replace(/\./g,':').replace(/ /g,' ').split(' ');
366 if (dateArr.length != 5 && dateArr.length != 6) {
367 // we don't know how to format this
368 if (config['dateFormat'].Value == 'auto' || config['dateFormat'].Value == 'DDMM')
369 return day + config['dateSeparator'].Value + month;
370 else
371 return month + config['dateSeparator'].Value + day;
372 }
373
374 var dayFirst = true;
375 if (config['dateFormat'].Value == 'MMDD')
376 dayFirst = false;
377 else if (config['dateFormat'].Value == 'DDMM')
378 dayFirst = true;
379 else {
380 // config['dateFormat'].Value == 'auto', try to detect system setting
381 // parse date
382 var day_ = dateArr[1];
383 var month_ = dateArr[2];
384 var year_ = dateArr[3];
385 // make sure month is set properly
386 if (isNaN(parseInt(day_))) {
387 var tmp = day_;
388 day_ = month_;
389 month_ = tmp;
390 dayFirst = false;
391 } else if (isNaN(parseInt(year_))) {
392 var tmp = year_;
393 year_ = month_;
394 month_ = tmp;
395 dayFirst = true;
396 }
397 // make sure day and year are set properly
398 if (Number(day_) > Number(year_))
399 dayFirst = false;
400 }
401
402 if (dayFirst)
403 return day + config['dateSeparator'].Value + month;
404 else
405 return month + config['dateSeparator'].Value + day;
406 }
407
408 function formatTime(date)
409 {
410 // date is a Date() object
411 date.setSeconds(0); // we don't care about seconds
412 var time = date.toLocaleTimeString().replace(/[\.:]00/, ''); // remove seconds from string
413 if (time.replace(/\./, ':').split(':')[0].length < 2)
414 time = '0' + time;
415 if (config['showNowAsText'].Value && date.getTime() == now.getTime())
416 time = '<span class="now">' + config['nowText'].Value + '</span>';
417 return time;
418 }
419
420 function updateData()
421 {
422 console.info('updateData()');
423 calcDaylightSaving();
424 try {
425 // meetings have time
426 // 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
427 now = new Date();
428 var meetingListFiltering = {
429 Type:'CalendarEntry',
430 Filter:{
431 StartRange: (new Date(now.getFullYear(), now.getMonth(), now.getDate(), 0, 0, 0)),
432 EndRange: (new Date(now.getFullYear(), now.getMonth() + config['monthRange'].Value, now.getDate(), 0, 0, 0))
433 }
434 }
435 var meetingResult = calendarService.IDataSource.GetList(meetingListFiltering);
436 if (meetingResult.ErrorCode != 0)
437 throw("Error fetching calendar data: " + meetingResult.ErrorCode + ': ' + meetingResult.ErrorMessage);
438 var meetingList = meetingResult.ReturnValue;
439
440 // todos don't, they start on 00:00 hrs., but should be visible anyway
441 // this will generate a list of passed todos. We have to check if they have been marked as "done" yet
442 if (config['includeTodos'].Value) {
443 var todayTodoListFiltering = {
444 Type:'CalendarEntry',
445 Filter:{
446 Type: 'ToDo',
447 StartRange: (new Date(now.getFullYear() - 1, now.getMonth(), now.getDate(), 0, 0, 0)),
448 EndRange: (new Date(now.getFullYear(), now.getMonth(), now.getDate(), 0, 0, 1))
449 }
450 }
451 var todayTodoResult = calendarService.IDataSource.GetList(todayTodoListFiltering);
452 var todayTodoList = todayTodoResult.ReturnValue;
453 var entryLists = [todayTodoList, meetingList];
454 } else {
455 var entryLists = [meetingList];
456 }
457 } catch(e) {
458 error('loading Calendar items list:' + e + ', line ' + e.line);
459 return;
460 }
461
462 try {
463 var entry;
464 var counter = 0;
465 var entryDate = '';
466 var dateArr = [];
467 var entriesHtml = '<table>';
468 var eventIds = [];
469 var max;
470 if (mode == 0)
471 max = ((panelNum == 0) ? config['eventsPerWidget'].Value : 2 * config['eventsPerWidget'].Value);
472 else
473 max = 30; // we can display a lot more events in fullscreen mode
474
475 // the first outer loop iteration is for passed ToDos, the second loop is for all upcomming events (may also include ToDos)
476 for (var i=0; counter < max && i < entryLists.length; i++) {
477 while (counter < max && (entry = entryLists[i].getNext()) != undefined) {
478 counter++;
479
480 // output event info for debugging
481 console.info(
482 'event: Id=' + entry.id +
483 ',Type=' + entry.Type +
484 ',Summary=' + entry.Summary +
485 ',Location=' + entry.Location +
486 ',Status=' + entry.Status +
487 ',StartTime=' + entry.StartTime +
488 ',EndTime=' + entry.EndTime +
489 ',InstanceStartTime=' + entry.InstanceStartTime +
490 ',InstanceEndTime=' + entry.InstanceEndTime
491 );
492
493 // we don't want ToDos when includeTodos == false or when they are completed
494 if (entry.Type == 'ToDo' && (entry.Status == "TodoCompleted" || !config['includeTodos'].Value)) {
495 console.info('skipping ' + entry.id );
496 counter--;
497 continue;
498 }
499
500 // make sure that we don't include an event twice (useful for ToDos that might come up twice)
501 if (eventIds[entry.id] == 1 && entry.Type == 'ToDo') {
502 console.info('skipped (already included) ' + entry.id);
503 counter--;
504 continue;
505 } else
506 eventIds[entry.id] = 1;
507
508 // summary can be undefined!
509 var Summary = ((entry.Summary == null) ? '' : entry.Summary);
510 if (entry.Type == 'Meeting' && entry.Location != '' && config['showLocation'].Value)
511 Summary += ', ' + entry.Location;
512
513 // fix by yves: determine start and end dates/times
514 entryStartTime = ((entry.InstanceStartTime == null) ? entry.StartTime : entry.InstanceStartTime);
515 entryEndTime = ((entry.InstanceEndTime == null) ? entry.EndTime : entry.InstanceEndTime);
516
517 // there can be ToDos that have no date at all!
518 if (entry.Type == 'ToDo' && entry.EndTime == null)
519 entryDate = ""; // this will cause parseDate(entryDate) to return null;
520 else
521 entryDate = ((entry.Type == 'ToDo') ? entryEndTime : entryStartTime); // ToDo's use their EndTime, the rest use StartTime
522
523 // Convert date/time string to Date object
524 var date = parseDate(entryDate);
525 console.info('date: ' + date);
526 var endDate = ((entryEndTime == null) ? null : parseDate(entryEndTime));
527 console.info('endDate: ' + endDate);
528
529 // check if meeting event has already passed
530 if (entry.Type == 'Meeting') {
531 var compareTime = ((endDate == null) ? date.getTime() : endDate.getTime());
532 if (now.getTime() > compareTime) {
533 console.info('skipping Meeting (already passed) ' + entry.id);
534 counter--;
535 eventIds[entry.id] = 0;
536 continue;
537 }
538 }
539
540 // check if anniversary passed (not sure why they are in the list, the query was only for today - nokia?)
541 if (entry.Type == 'Anniversary') {
542 var tmp = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 0,0,0);
543 if (date.getTime() < tmp.getTime()) {
544 console.info('skipping Anniversary (already passed) ' + entry.id);
545 counter--;
546 eventIds[entry.id] = 0;
547 continue;
548 }
549 }
550
551 // fix DayEvents end time. End times are off by 1 Second. It's possible that the event has already passed
552 if (entry.Type == 'DayEvent' && endDate != null) {
553 endDate.setMinutes(endDate.getMinutes() - 1);
554 console.info('fixing DayEvent endDate: ' + endDate);
555 if (now.getTime() > endDate.getTime()) {
556 console.info('event already passed ' + entry.id);
557 counter--;
558 eventIds[entry.id] = 0;
559 continue;
560 }
561 }
562
563 // check if the event is currently taking place
564 if (entryStartTime != null && entryEndTime != null && date != null && endDate != null) {
565 // check if we are between start and endtime
566 if ((date.getTime() < now.getTime()) && (now.getTime() < endDate.getTime())) {
567 date = now; // change appointment date/time to now
568 console.info('event is currently taking place: ' + date);
569 }
570 }
571
572 // skip events for the first panel in case this is the second one and we're not in fullscreen mode
573 if (mode == 0 && panelNum == 1 && counter < config['eventsPerWidget'].Value + 1) {
574 console.info('skipping (already in first widget) ' + entry.id);
575 continue;
576 }
577
578 // generate html output
579 entriesHtml += '<tr><td><img class="icon" src="' + entry.Type + '.png" /></td>';
580 if(date == null) {
581 // some languages have very strange locale date formats, can't parse all those. Also some todos don't have dates at all.
582 entriesHtml += '<td colspan="4"><span class="date">' + entryDate + '</span> ';
583 } else {
584 var weekDay = date.toLocaleDateString().substr(0,config['weekDayLength'].Value);
585 var time = formatTime(date);
586 var dateStr = formatDate(date, entryDate);
587 if (entry.Type == 'ToDo' || entry.Type == 'Anniversary' || entry.Type == 'DayEvent' || entry.Type == 'Reminder') {
588 if ((isToday(date) || isTomorrow(date)) && config['showTodayAsText'].Value) // show weekday if the date string is not text. looks odd otherwise
589 entriesHtml += '<td colspan="4" width="1px"><span class="date">' + dateStr + '</span> ';
590 else
591 entriesHtml += '<td class="weekDay" width="1px">' + weekDay + '</td><td width="1px" class="date">' + dateStr + '</td><td colspan="2">';
592 } else if (entry.Type == 'Meeting') {
593 if (config['showCombinedDateTime'].Value) {
594 if (isToday(date))
595 entriesHtml += '<td width="1px" colspan="4"><span class="today">' + time + '</span> ';
596 else if (isTomorrow(date))
597 entriesHtml += '<td width="1px" colspan="4"><span class="tomorrow">' + dateStr + '</span> <span class="time">' + time + '</span> ';
598 else
599 entriesHtml += '<td width="1px" class="weekDay">' + weekDay + '</td><td width="1px" class="date">' + dateStr + '</td><td colspan="2">';
600 } else {
601 if ((isToday(date) || isTomorrow(date)) && config['showTodayAsText'].Value)
602 entriesHtml += '<td colspan="4" width="1px"><span class="today">' + dateStr + '</span> <span class="time">' + time + '</span> ';
603 else
604 entriesHtml += '<td width="1px" class="weekDay">' + weekDay + '</td><td width="1px" class="date">' + dateStr + '</td><td width="1px" class="time">' + time + '</td><td>';
605 }
606 }
607 }
608 entriesHtml += '<span class="description">' + Summary + '</span></td></tr>';
609 }
610 }
611 entriesHtml += '</table>';
612 if (config['showNothingText'].Value && entriesHtml == '<table></table>') {
613 var text = config['nothingText'].Value.replace(/%d/, config['monthRange'].Value);
614 entriesHtml = '<div style="width:295px; height:75px; text-align:center; line-height:75px; overflow:visible;">' + text + '</div>';
615 }
616 if (cacheEntriesHtml != entriesHtml) {
617 if (mode == 0)
618 document.getElementById('calendarList').innerHTML = entriesHtml;
619 else
620 document.getElementById('fullscreenCalendarList').innerHTML = entriesHtml;
621 cacheEntriesHtml = entriesHtml;
622 }
623 } catch(e) {
624 error('displaying list:' + e + ', line ' + e.line);
625 return;
626 }
627 }
628
629 function updateScreen()
630 {
631 // check if opening fullscreen
632 if( window.innerHeight > 91 && mode == 0) {
633 mode = 1;
634 cacheEntriesHtml = '';
635 document.getElementById('body').style.backgroundImage = "";
636 showFullscreen();
637 }
638 else if (window.innerHeight <= 91 && mode != 0) {
639 mode = 0;
640 cacheEntriesHtml = '';
641 showHomescreen();
642 }
643
644 if (mode == 0)
645 updateHomescreen();
646 else if (mode == 1)
647 updateFullscreen();
648 }
649
650 function launchCalendar()
651 {
652 try {
653 widget.openApplication(config['calendarApp'].Value, "");
654 if (config['hideWidgetOnCalendarOpen'].Value)
655 window.close();
656 } catch(e) {
657 error('starting Calendar App');
658 return;
659 }
660 }
661
662 function init()
663 {
664 console.info('New widget instance starting up...');
665
666 try {
667 // call calendar service
668 if (device != "undefined")
669 calendarService = device.getServiceObject("Service.Calendar", "IDataSource");
670 else
671 throw('device object does not exist');
672 } catch(e) {
673 error('loading Calendar service: ' + e + ', line ' + e.line);
674 return;
675 }
676
677 loadSettings();
678 updateCssClasses();
679 collectLocales();
680 //updateData();
681 requestNotification();
682 window.setInterval('updateData()', 1000 * 60 * config['updateDataInterval'].Value);
683
684 mode = 0;
685 showHomescreen();
686 updateScreen();
687 if (config['useBackgroundImage'].Value)
688 // check for screen rotation every 1 secs
689 window.setInterval('updateScreen()', 1000 * 1);
690 }
691
692 function createMenu()
693 {
694 window.menu.setLeftSoftkeyLabel("",null);
695 window.menu.setRightSoftkeyLabel("",null);
696 var id = 0;
697 var menuSettings = new MenuItem(getLocalizedText('menu.settings'), id++);
698 var menuCallApp = new MenuItem(getLocalizedText('menu.openCalendarApp'), id++);
699 var menuUpdate = new MenuItem(getLocalizedText('menu.update'), id++);
700 var menuAbout = new MenuItem(getLocalizedText('menu.about'), id++);
701 menuSettings.onSelect = showSettings;
702 menuAbout.onSelect = showAbout;
703 menuCallApp.onSelect = launchCalendar;
704 menuUpdate.onSelect = showUpdate;
705 window.menu.clear();
706 window.menu.append(menuCallApp);
707 window.menu.append(menuSettings);
708 window.menu.append(menuUpdate);
709 window.menu.append(menuAbout);
710 }
711
712 function showSettings()
713 {
714 mode = 2;
715 hideViews();
716 document.getElementById("settingsView").style.display = "block";
717 document.onclick = null;
718
719 window.menu.setLeftSoftkeyLabel(getLocalizedText('settings.save'), function()
720 {
721 for (var key in config) {
722 if (config[key].Type == 'String')
723 config[key].Value = document.forms[0].elements["settings." + key].value;
724 else if (config[key].Type == 'Int') {
725 config[key].Value = parseInt(document.forms[0].elements["settings." + key].value);
726 if (config[key].Value < 0)
727 config[key].Value = config[key].Default;
728 }
729 else if (config[key].Type == 'Bool')
730 config[key].Value = document.forms[0].elements["settings." + key].checked;
731 else if (config[key].Type == 'UID')
732 config[key].Value = parseInt(document.forms[0].elements["settings." + key].value);
733 else if (config[key].Type == 'Enum') {
734 config[key].Value = document.forms[0].elements["settings." + key].value;
735 if (config[key].ValidValues.indexOf(config[key].Value) == -1)
736 config[key].Value = config[key].Default;
737 }
738 }
739
740 updateCssClasses();
741
742 saveSettings();
743
744 mode = 1;
745 showFullscreen();
746 });
747 window.menu.setRightSoftkeyLabel(getLocalizedText('settings.cancel'), function()
748 {
749 mode = 1;
750 showFullscreen();
751 });
752
753 var settingsHtml = '<form>';
754 for (var key in config) {
755 if (config[key].Type == 'String')
756 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 />';
757 else if (config[key].Type == 'Int')
758 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 />';
759 else if (config[key].Type == 'Bool')
760 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 />';
761 else if (config[key].Type == 'UID')
762 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 />';
763 else if (config[key].Type == 'Enum') {
764 settingsHtml += '<table><tr><td>' + getLocalizedText('settings.name.' + key) + '<br /><select name="settings.' + key + '" size="1">';
765 for(var i = 0; i < config[key].ValidValues.length; i++)
766 settingsHtml += '<option label="' + config[key].ValidValues[i] + '"' + (config[key].Value == config[key].ValidValues[i] ? ' selected="selected"' : '') + '>' + config[key].ValidValues[i] + '</option>';
767 settingsHtml += '</select></div></td>' + printHintBox(getLocalizedText('settings.info.' + key)) + '<hr />';
768 }
769 }
770 settingsHtml += '<input name="reset" type="button" value="' + getLocalizedText('settings.restoreDefaults') + '" onclick="javascript:restoreDefaultSettings();showSettings();" />';
771 settingsHtml += '</form>';
772 document.getElementById("settingsList").innerHTML = settingsHtml;
773 }
774
775 function changeCssClass(classname, properties)
776 {
777 for(var i = 0; i < document.styleSheets[0]['cssRules'].length; i++)
778 {
779 if (document.styleSheets[0]['cssRules'][i].selectorText == classname) {
780 document.styleSheets[0].deleteRule(i);
781 document.styleSheets[0].insertRule(classname + ' { ' + properties + ' }', document.styleSheets[0]['cssRules'].length);
782 break;
783 }
784 }
785 }
786
787 function updateCssClasses()
788 {
789 for(var key in config) {
790 changeCssClass(getLocalizedText('settings.name.' + key), config[key].Value);
791 }
792 }
793
794 function restoreDefaultSettings()
795 {
796 for (var key in config)
797 config[key].Value = config[key].Default;
798 }
799
800 function loadSettings()
801 {
802 for (var key in config) {
803 if (widget.preferenceForKey(key)) {
804 if (config[key].Type == 'Int')
805 config[key].Value = Number(widget.preferenceForKey(key));
806 else if (config[key].Type == 'String')
807 config[key].Value = widget.preferenceForKey(key);
808 else if (config[key].Type == 'Bool')
809 config[key].Value = (widget.preferenceForKey(key) == 'true')
810 else if (config[key].Type == 'Enum')
811 config[key].Value = widget.preferenceForKey(key);
812 else if (config[key].Type == 'UID')
813 config[key].Value = Number(widget.preferenceForKey(key));
814 }
815 else
816 config[key].Value = config[key].Default;
817 console.info('Settings: ' + key + '=\'' + config[key].Value + '\'');
818 }
819 }
820
821 function saveSettings()
822 {
823 for (var key in config) {
824 if (config[key].Type == 'Int')
825 widget.setPreferenceForKey(config[key].Value.toString(), key);
826 else if (config[key].Type == 'String')
827 widget.setPreferenceForKey(config[key].Value, key);
828 else if (config[key].Type == 'Bool')
829 widget.setPreferenceForKey(config[key].Value ? 'true' : 'false', key);
830 else if (config[key].Type == 'Enum')
831 widget.setPreferenceForKey(config[key].Value, key);
832 else if (config[key].Type == 'UID')
833 widget.setPreferenceForKey(config[key].Value.toString(), key);
834 }
835 }
836
837 function toggleVisibility(elementId)
838 {
839 if (document.getElementById(elementId).style.display == "none")
840 document.getElementById(elementId).style.display = "block";
841 else
842 document.getElementById(elementId).style.display = "none";
843 }
844
845 var uniqueId = 0;
846 function printHintBox(text)
847 {
848 uniqueId++;
849 return '<td width="1%" align="right" onclick="javascript:toggleVisibility(\'info' + uniqueId + '\')">' + getLocalizedText('settings.help') + '</td></tr></table>'+
850 '<div class="settingsInfo" id="info' + uniqueId + '">' + text + '</div>';
851 }
852
853 function showAbout()
854 {
855 mode = 3;
856 hideViews();
857 document.getElementById("aboutView").style.display = "block";
858 document.onclick = null;
859
860 window.menu.setLeftSoftkeyLabel(" ", function(){});
861 window.menu.setRightSoftkeyLabel(getLocalizedText('softkey.back'), function()
862 {
863 mode = 1;
864 showFullscreen();
865 });
866
867 //document.getElementById("aboutView").innerHTML = 'aboutView';
868 document.getElementById("name").innerHTML = "Coming Next " + version;
869 }
870
871 function updateFullscreen()
872 {
873 }
874
875 function showFullscreen()
876 {
877 hideViews();
878 document.getElementById("fullscreenView").style.display = "block";
879 document.getElementById('body').className = "backgroundFullscreen";
880 document.onclick = launchCalendar;
881 createMenu();
882 updateData();
883 }
884
885 function updateHomescreen()
886 {
887 if (config['useBackgroundImage'].Value) {
888 // check for screen rotation
889 if (orientation != 'portrait' && screen.width == 360 && screen.height == 640) {
890 window.widget.prepareForTransition("fade");
891 orientation = 'portrait';
892 document.getElementById('body').style.backgroundImage = 'url(background_' + orientation + '.png)';
893 document.getElementById('body').style.backgroundColor = 'none';
894 window.widget.performTransition();
895 } else if (orientation != 'landscape' && screen.width == 640 && screen.height == 360) {
896 window.widget.prepareForTransition("fade");
897 orientation = 'landscape';
898 document.getElementById('body').style.backgroundImage = 'url(background_' + orientation + '.png)';
899 document.getElementById('body').style.backgroundColor = 'none';
900 window.widget.performTransition();
901 }
902 else if (document.getElementById('body').style.backgroundImage == "")
903 {
904 document.getElementById('body').style.backgroundImage = 'url(background_' + orientation + '.png)';
905 }
906 }
907 }
908
909 function showHomescreen()
910 {
911 hideViews();
912 document.getElementById("homescreenView").style.display = "block";
913 document.getElementById('body').className = "background";
914 document.onclick = null;
915 updateData();
916 }
917
918 function getLocalizedText(p_Txt)
919 {
920 if (localizedText[p_Txt])
921 return localizedText[p_Txt];
922 else
923 return 'ERROR: missing translation for ' + p_Txt;
924 }
925
926 function showUpdate()
927 {
928 mode = 4;
929 hideViews();
930 document.getElementById("updateView").style.display = "block";
931 document.onclick = null;
932
933 window.menu.setLeftSoftkeyLabel(getLocalizedText('update.checknow'), function(){
934 checkForUpdate();
935 });
936 window.menu.setRightSoftkeyLabel(getLocalizedText('softkey.back'), function()
937 {
938 mode = 1;
939 showFullscreen();
940 });
941
942 document.getElementById("currentVersion").innerHTML = getLocalizedText("update.current") + version;
943 checkForUpdate();
944 }
945
946 function checkForUpdate()
947 {
948 // asynch XHR to server url
949 reqV = new XMLHttpRequest();
950 reqV.onreadystatechange = checkForUpdateCallback;
951 document.getElementById("updateDiv").innerHTML = getLocalizedText("update.checking");
952 reqV.open("GET", versionURL, true);
953 reqV.setRequestHeader( "If-Modified-Since", "Sat, 1 Jan 2000 00:00:00 GMT" ); // disable caching
954 reqV.send(null);
955 }
956
957 function checkForUpdateCallback()
958 {
959 if (reqV.readyState == 4) {
960 if (reqV.status == 200) {
961 // little overhead here, one could use also string for version info
962 var resultXml = reqV.responseText;
963 if (resultXml) {
964 var div = document.getElementById("tmp");
965 div.innerHTML = resultXml;
966 var newVersion = div.getElementsByTagName('version')[0].innerHTML;
967 var newVersionURL = div.getElementsByTagName('url')[0].innerHTML;
968 div.innerHTML = "";
969 if (version != newVersion) {
970 document.getElementById("updateDiv").innerHTML = getLocalizedText("update.download").replace(/%1/, newVersion).replace(/%2/, newVersionURL);
971 }
972 else {
973 document.getElementById("updateDiv").innerHTML = getLocalizedText("update.nonewversion");
974 }
975 }
976 }
977 else {
978 document.getElementById("updateDiv").innerHTML = getLocalizedText("update.error") + reqV.status + " " + reqV.responseText;
979 }
980 }
981 }
982
983 function hideViews()
984 {
985 document.getElementById("homescreenView").style.display = "none";
986 document.getElementById("fullscreenView").style.display = "none";
987 document.getElementById("aboutView").style.display = "none";
988 document.getElementById("settingsView").style.display = "none";
989 document.getElementById("updateView").style.display = "none";
990 }
991 </script>
992
993 <style type="text/css">
994 table { margin:0px; padding:0px; border-spacing:0px; }
995 td { padding:0px 5px 0px 0px; white-space:nowrap; overflow:hidden; }
996 hr { color:#ffffff; background-color:#ffffff; height:1px; text-align:left; border-style:none; }
997 .settingsInfo { display:none; font-style:italic; }
998 .title { font-weight:bold; font-size:14pt; }
999 .textInput { width:90%; }
1000 .credits { margin-left:40px; text-indent: -20px; margin-bottom:0px; }
1001 #homescreenView { width: 315px; height:91px; overflow:hidden; }
1002 #calendarList { position:absolute; left:10px; top:4px; width:295px; height:75px; overflow:hidden; }
1003 #name { text-align:center; }
1004 #appicon { display: block; margin-left: auto; margin-right: auto; margin-top: 10px; }
1005 #smallappicon { width:22px; height:22px; margin-right:10px; float:left; }
1006 </style>
1007
1008 </head>
1009
1010 <body id="body" class="background">
1011 <div id="homescreenView">
1012 <div id="calendarList"></div>
1013 </div>
1014 <div id="fullscreenView" style="display:none;">
1015 <img src="Icon.png" id="smallappicon">
1016 <h1 class="title">Coming Next</h1>
1017 <hr />
1018 <div id="fullscreenCalendarList">loading...</div>
1019 </div>
1020 <div id="settingsView" style="display:none">
1021 <img src="Icon.png" id="smallappicon">
1022 <h1 class="title">Settings</h1>
1023 <hr />
1024 <div id="settingsList"></div>
1025 </div>
1026 <div id="aboutView" style="display:none">
1027 <img src="Icon.png" id="appicon">
1028 <h1 id="name">Coming Next</h1>
1029 <hr />
1030 <p>Created by Dr. Cochambre and Michael Prager.</p>
1031 <p>Contributions:</p>
1032 <p class="credits">Paul Moore (bug fixes, new features and code cleanup)</p>
1033 <p class="credits">Manfred Hanselmann (DST support)</p>
1034 <p class="credits">Christophe Milsent (translation support & french translation</p>
1035 <p>This software is open source and licensed under the GPLv3.</p>
1036 <p>Visit sourceforge.net/projects/comingnext for free updates.</p>
1037 <hr />
1038 </div>
1039 <div id="updateView" style="display:none">
1040 <img src="Icon.png" id="smallappicon">
1041 <h1 class="title">Check for update</h1>
1042 <hr />
1043 <div id="currentVersion">Coming Next ??</div>
1044 <div id="updateDiv"></div>
1045 <div id="tmp" style="display:none;"></div>
1046 </div>
1047 </body>
1048
1049 </html>