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