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