]> code.delx.au - comingnext/blob - comingNext/index.html
fixed 'overdue' text setting not beeing used
[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 = 2000 + panelNum;
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 fontsize = 'normal';
476 if (mode == 0) {
477 if (config['eventsPerWidget'].Value == 3) {
478 fontsize = '17pt';
479 changeCssClass('.icon', 'width:20px; height:20px');
480 }
481 else if (config['eventsPerWidget'].Value == 5) {
482 fontsize = '10pt';
483 changeCssClass('.icon', 'width:10px; height:10px');
484 }
485 else if (config['eventsPerWidget'].Value == 6) {
486 fontsize = '8pt';
487 changeCssClass('.icon', 'width:8px; height:8px');
488 }
489 }
490 else
491 changeCssClass('.icon', config['cssStyle_icon'].Value);
492 var entriesHtml = '<table style="font-size:' + fontsize + ';">';
493 var eventIds = [];
494 var max;
495 if (mode == 0)
496 max = (panelNum + 1) * config['eventsPerWidget'].Value;
497 else
498 max = 30; // we can display a lot more events in fullscreen mode
499
500 // the first outer loop iteration is for passed ToDos, the second loop is for all upcomming events (may also include ToDos)
501 for (var i=0; counter < max && i < entryLists.length; i++) {
502 while (counter < max && (entry = entryLists[i].getNext()) != undefined) {
503 counter++;
504
505 // output event info for debugging
506 console.info(
507 'event: Id=' + entry.id +
508 ',Type=' + entry.Type +
509 ',Summary=' + entry.Summary +
510 ',Location=' + entry.Location +
511 ',Status=' + entry.Status +
512 ',StartTime=' + entry.StartTime +
513 ',EndTime=' + entry.EndTime +
514 ',InstanceStartTime=' + entry.InstanceStartTime +
515 ',InstanceEndTime=' + entry.InstanceEndTime
516 );
517
518 // we don't want ToDos when includeTodos == false or when they are completed
519 if (entry.Type == 'ToDo' && (entry.Status == "TodoCompleted" || !config['includeTodos'].Value)) {
520 console.info('skipping ' + entry.id );
521 counter--;
522 continue;
523 }
524
525 // make sure that we don't include an event twice (useful for ToDos that might come up twice)
526 if (eventIds[entry.id] == 1 && entry.Type == 'ToDo') {
527 console.info('skipped (already included) ' + entry.id);
528 counter--;
529 continue;
530 } else
531 eventIds[entry.id] = 1;
532
533 // summary can be undefined!
534 var Summary = ((entry.Summary == null) ? '' : entry.Summary);
535 if (entry.Type == 'Meeting' && entry.Location != '' && config['showLocation'].Value)
536 Summary += ', ' + entry.Location;
537
538 // fix by yves: determine start and end dates/times
539 entryStartTime = ((entry.InstanceStartTime == null) ? entry.StartTime : entry.InstanceStartTime);
540 entryEndTime = ((entry.InstanceEndTime == null) ? entry.EndTime : entry.InstanceEndTime);
541
542 // there can be ToDos that have no date at all!
543 if (entry.Type == 'ToDo' && entry.EndTime == null)
544 entryDate = ""; // this will cause parseDate(entryDate) to return null;
545 else
546 entryDate = ((entry.Type == 'ToDo') ? entryEndTime : entryStartTime); // ToDo's use their EndTime, the rest use StartTime
547
548 // Convert date/time string to Date object
549 var date = parseDate(entryDate);
550 console.info('date: ' + date);
551 var endDate = ((entryEndTime == null) ? null : parseDate(entryEndTime));
552 console.info('endDate: ' + endDate);
553
554 // check if meeting event has already passed
555 if (entry.Type == 'Meeting') {
556 var compareTime = ((endDate == null) ? date.getTime() : endDate.getTime());
557 if (now.getTime() > compareTime) {
558 console.info('skipping Meeting (already passed) ' + entry.id);
559 counter--;
560 eventIds[entry.id] = 0;
561 continue;
562 }
563 }
564
565 // check if anniversary passed (not sure why they are in the list, the query was only for today - nokia?)
566 if (entry.Type == 'Anniversary') {
567 var tmp = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 0,0,0);
568 if (date.getTime() < tmp.getTime()) {
569 console.info('skipping Anniversary (already passed) ' + entry.id);
570 counter--;
571 eventIds[entry.id] = 0;
572 continue;
573 }
574 }
575
576 // fix DayEvents end time. End times are off by 1 Second. It's possible that the event has already passed
577 if (entry.Type == 'DayEvent' && endDate != null) {
578 endDate.setMinutes(endDate.getMinutes() - 1);
579 console.info('fixing DayEvent endDate: ' + endDate);
580 if (now.getTime() > endDate.getTime()) {
581 console.info('event already passed ' + entry.id);
582 counter--;
583 eventIds[entry.id] = 0;
584 continue;
585 }
586 }
587
588 // check if the event is currently taking place
589 if (entryStartTime != null && entryEndTime != null && date != null && endDate != null) {
590 // check if we are between start and endtime
591 if ((date.getTime() < now.getTime()) && (now.getTime() < endDate.getTime())) {
592 date = now; // change appointment date/time to now
593 console.info('event is currently taking place: ' + date);
594 }
595 }
596
597 // skip events for the first panel in case this is the second one and we're not in fullscreen mode
598 if (mode == 0 && panelNum > 0 && counter < panelNum * config['eventsPerWidget'].Value + 1) {
599 console.info('skipping (already in first widget) ' + entry.id);
600 continue;
601 }
602
603 // mark overdue todos
604 var overdue = false;
605 if (entry.Type == 'ToDo') {
606 var tmp1 = new Date(date.getFullYear(), date.getMonth(), date.getDate(), 0,0,0);
607 var tmp2 = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 0,0,0);
608 if (tmp1.getTime() < tmp2.getTime()) {
609 overdue = true;
610 }
611 }
612
613 // generate html output
614 entriesHtml += '<tr><td><img class="icon" src="' + entry.Type + '.png" /></td>';
615 if(date == null) {
616 // some languages have very strange locale date formats, can't parse all those. Also some todos don't have dates at all.
617 entriesHtml += '<td colspan="4"><span class="date">' + entryDate + '</span> ';
618 } else {
619 var weekDay = date.toLocaleDateString().substr(0,config['weekDayLength'].Value);
620 var time = formatTime(date);
621 var dateStr = formatDate(date, entryDate);
622 if (entry.Type == 'ToDo' && overdue && config['markOverdueTodos'].Value) {
623 dateStr = '<span class="overdue">' + config['overdueText'].Value + '</span>';
624 entriesHtml += '<td colspan="4" width="1px"><span class="date">' + dateStr + '</span> ';
625 } else if (entry.Type == 'ToDo' || entry.Type == 'Anniversary' || entry.Type == 'DayEvent' || entry.Type == 'Reminder') {
626 if ((isToday(date) || isTomorrow(date)) && config['showTodayAsText'].Value) // show weekday if the date string is not text. looks odd otherwise
627 entriesHtml += '<td colspan="4" width="1px"><span class="date">' + dateStr + '</span> ';
628 else
629 entriesHtml += '<td class="weekDay" width="1px">' + weekDay + '</td><td width="1px" class="date">' + dateStr + '</td><td colspan="2">';
630 } else if (entry.Type == 'Meeting') {
631 if (config['showCombinedDateTime'].Value) {
632 if (isToday(date))
633 entriesHtml += '<td width="1px" colspan="4"><span class="today">' + time + '</span> ';
634 else if (isTomorrow(date))
635 entriesHtml += '<td width="1px" colspan="4"><span class="tomorrow">' + dateStr + '</span> <span class="time">' + time + '</span> ';
636 else
637 entriesHtml += '<td width="1px" class="weekDay">' + weekDay + '</td><td width="1px" class="date">' + dateStr + '</td><td colspan="2">';
638 } else {
639 if ((isToday(date) || isTomorrow(date)) && config['showTodayAsText'].Value)
640 entriesHtml += '<td colspan="4" width="1px"><span class="today">' + dateStr + '</span> <span class="time">' + time + '</span> ';
641 else
642 entriesHtml += '<td width="1px" class="weekDay">' + weekDay + '</td><td width="1px" class="date">' + dateStr + '</td><td width="1px" class="time">' + time + '</td><td>';
643 }
644 }
645 }
646 entriesHtml += '<span class="description">' + Summary + '</span></td></tr>';
647 }
648 }
649 entriesHtml += '</table>';
650 if (config['showNothingText'].Value && entriesHtml == '<table></table>') {
651 var text = config['nothingText'].Value.replace(/%d/, config['monthRange'].Value);
652 entriesHtml = '<div style="width:295px; height:75px; text-align:center; line-height:75px; overflow:visible;">' + text + '</div>';
653 }
654 if (cacheEntriesHtml != entriesHtml) {
655 if (mode == 0)
656 document.getElementById('calendarList').innerHTML = entriesHtml;
657 else
658 document.getElementById('fullscreenCalendarList').innerHTML = entriesHtml;
659 cacheEntriesHtml = entriesHtml;
660 }
661 } catch(e) {
662 error('displaying list:' + e + ', line ' + e.line);
663 return;
664 }
665 }
666
667 function updateScreen()
668 {
669 // check if opening fullscreen
670 if( window.innerHeight > 91 && mode == 0) {
671 mode = 1;
672 cacheEntriesHtml = '';
673 document.getElementById('body').style.backgroundImage = "";
674 showFullscreen();
675 }
676 else if (window.innerHeight <= 91 && mode != 0) {
677 mode = 0;
678 cacheEntriesHtml = '';
679 showHomescreen();
680 }
681
682 if (mode == 0)
683 updateHomescreen();
684 else if (mode == 1)
685 updateFullscreen();
686 }
687
688 function launchCalendar()
689 {
690 try {
691 widget.openApplication(config['calendarApp'].Value, "");
692 if (config['hideWidgetOnCalendarOpen'].Value)
693 window.close();
694 } catch(e) {
695 error('starting Calendar App');
696 return;
697 }
698 }
699
700 function init()
701 {
702 console.info('New widget instance starting up...');
703
704 try {
705 // call calendar service
706 if (device != "undefined")
707 calendarService = device.getServiceObject("Service.Calendar", "IDataSource");
708 else
709 throw('device object does not exist');
710 } catch(e) {
711 error('loading Calendar service: ' + e + ', line ' + e.line);
712 return;
713 }
714
715 loadSettings();
716 updateCssClasses();
717 collectLocales();
718 //updateData();
719 requestNotification();
720 window.setInterval('updateData()', 1000 * 60 * config['updateDataInterval'].Value);
721
722 mode = 0;
723 showHomescreen();
724 updateScreen();
725 if (config['useBackgroundImage'].Value)
726 // check for screen rotation every 1 secs
727 window.setInterval('updateScreen()', 1000 * 1);
728 }
729
730 function createMenu()
731 {
732 window.menu.setLeftSoftkeyLabel("",null);
733 window.menu.setRightSoftkeyLabel("",null);
734 var id = 0;
735 var menuSettings = new MenuItem(getLocalizedText('menu.settings'), id++);
736 var menuCallApp = new MenuItem(getLocalizedText('menu.openCalendarApp'), id++);
737 var menuUpdate = new MenuItem(getLocalizedText('menu.update'), id++);
738 var menuAbout = new MenuItem(getLocalizedText('menu.about'), id++);
739 menuSettings.onSelect = showSettings;
740 menuAbout.onSelect = showAbout;
741 menuCallApp.onSelect = launchCalendar;
742 menuUpdate.onSelect = showUpdate;
743 window.menu.clear();
744 window.menu.append(menuCallApp);
745 window.menu.append(menuSettings);
746 window.menu.append(menuUpdate);
747 window.menu.append(menuAbout);
748 }
749
750 function showSettings()
751 {
752 mode = 2;
753 hideViews();
754 document.getElementById("settingsView").style.display = "block";
755 document.onclick = null;
756
757 window.menu.setLeftSoftkeyLabel(getLocalizedText('settings.save'), function()
758 {
759 for (var key in config) {
760 if (config[key].Type == 'String')
761 config[key].Value = document.forms[0].elements["settings." + key].value;
762 else if (config[key].Type == 'Int') {
763 config[key].Value = parseInt(document.forms[0].elements["settings." + key].value);
764 if (config[key].Value < 0)
765 config[key].Value = config[key].Default;
766 }
767 else if (config[key].Type == 'Bool')
768 config[key].Value = document.forms[0].elements["settings." + key].checked;
769 else if (config[key].Type == 'UID')
770 config[key].Value = parseInt(document.forms[0].elements["settings." + key].value);
771 else if (config[key].Type == 'Enum') {
772 config[key].Value = document.forms[0].elements["settings." + key].value;
773 if (config[key].ValidValues.indexOf(config[key].Value) == -1)
774 config[key].Value = config[key].Default;
775 }
776 }
777
778 updateCssClasses();
779
780 saveSettings();
781
782 mode = 1;
783 showFullscreen();
784 });
785 window.menu.setRightSoftkeyLabel(getLocalizedText('settings.cancel'), function()
786 {
787 mode = 1;
788 showFullscreen();
789 });
790
791 var settingsHtml = '<form>';
792 for (var key in config) {
793 if (config[key].Type == 'String')
794 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 />';
795 else if (config[key].Type == 'Int')
796 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 />';
797 else if (config[key].Type == 'Bool')
798 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 />';
799 else if (config[key].Type == 'UID')
800 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 />';
801 else if (config[key].Type == 'Enum') {
802 settingsHtml += '<table><tr><td>' + getLocalizedText('settings.name.' + key) + '<br /><select name="settings.' + key + '" size="1">';
803 for(var i = 0; i < config[key].ValidValues.length; i++)
804 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>';
805 settingsHtml += '</select></div></td>' + printHintBox(getLocalizedText('settings.info.' + key)) + '<hr />';
806 }
807 }
808 settingsHtml += '<input name="reset" type="button" value="' + getLocalizedText('settings.restoreDefaults') + '" onclick="javascript:restoreDefaultSettings();showSettings();" />';
809 settingsHtml += '</form>';
810 document.getElementById("settingsList").innerHTML = settingsHtml;
811 }
812
813 function changeCssClass(classname, properties)
814 {
815 for(var i = 0; i < document.styleSheets[0]['cssRules'].length; i++)
816 {
817 if (document.styleSheets[0]['cssRules'][i].selectorText == classname) {
818 document.styleSheets[0].deleteRule(i);
819 document.styleSheets[0].insertRule(classname + ' { ' + properties + ' }', document.styleSheets[0]['cssRules'].length);
820 break;
821 }
822 }
823 }
824
825 function updateCssClasses()
826 {
827 for(var key in config) {
828 changeCssClass(getLocalizedText('settings.name.' + key), config[key].Value);
829 }
830 }
831
832 function restoreDefaultSettings()
833 {
834 for (var key in config)
835 config[key].Value = config[key].Default;
836 }
837
838 function loadSettings()
839 {
840 for (var key in config) {
841 if (widget.preferenceForKey(key)) {
842 if (config[key].Type == 'Int')
843 config[key].Value = Number(widget.preferenceForKey(key));
844 else if (config[key].Type == 'String')
845 config[key].Value = widget.preferenceForKey(key);
846 else if (config[key].Type == 'Bool')
847 config[key].Value = (widget.preferenceForKey(key) == 'true')
848 else if (config[key].Type == 'Enum')
849 config[key].Value = widget.preferenceForKey(key);
850 else if (config[key].Type == 'UID')
851 config[key].Value = Number(widget.preferenceForKey(key));
852 }
853 else
854 config[key].Value = config[key].Default;
855 console.info('Settings: ' + key + '=\'' + config[key].Value + '\'');
856 }
857 }
858
859 function saveSettings()
860 {
861 for (var key in config) {
862 if (config[key].Type == 'Int')
863 widget.setPreferenceForKey(config[key].Value.toString(), key);
864 else if (config[key].Type == 'String')
865 widget.setPreferenceForKey(config[key].Value, key);
866 else if (config[key].Type == 'Bool')
867 widget.setPreferenceForKey(config[key].Value ? 'true' : 'false', key);
868 else if (config[key].Type == 'Enum')
869 widget.setPreferenceForKey(config[key].Value, key);
870 else if (config[key].Type == 'UID')
871 widget.setPreferenceForKey(config[key].Value.toString(), key);
872 }
873 }
874
875 function toggleVisibility(elementId)
876 {
877 if (document.getElementById(elementId).style.display == "none")
878 document.getElementById(elementId).style.display = "block";
879 else
880 document.getElementById(elementId).style.display = "none";
881 }
882
883 var uniqueId = 0;
884 function printHintBox(text)
885 {
886 uniqueId++;
887 return '<td width="1%" align="right" onclick="javascript:toggleVisibility(\'info' + uniqueId + '\')">' + getLocalizedText('settings.help') + '</td></tr></table>'+
888 '<div class="settingsInfo" id="info' + uniqueId + '">' + text + '</div>';
889 }
890
891 function showAbout()
892 {
893 mode = 3;
894 hideViews();
895 document.getElementById("aboutView").style.display = "block";
896 document.onclick = null;
897
898 window.menu.setLeftSoftkeyLabel(" ", function(){});
899 window.menu.setRightSoftkeyLabel(getLocalizedText('softkey.back'), function()
900 {
901 mode = 1;
902 showFullscreen();
903 });
904
905 //document.getElementById("aboutView").innerHTML = 'aboutView';
906 document.getElementById("name").innerHTML = "Coming Next " + version;
907 }
908
909 function updateFullscreen()
910 {
911 }
912
913 function showFullscreen()
914 {
915 hideViews();
916 document.getElementById("fullscreenView").style.display = "block";
917 document.getElementById('body').className = "backgroundFullscreen";
918 document.onclick = launchCalendar;
919 createMenu();
920 updateData();
921 }
922
923 function getBackgroundImage()
924 {
925 var bgImage;
926 if (config['backgroundImageLocation'].Value == config['backgroundImageLocation'].ValidValues[0]) // internal
927 bgImage = 'background_' + orientation + '.png';
928 else
929 bgImage = 'C:/Data/background_' + panelNum + '_' + orientation + '.png';
930 return bgImage;
931 }
932
933 function updateHomescreen()
934 {
935 if (config['useBackgroundImage'].Value) {
936 // check for screen rotation
937 if (orientation != 'portrait' && screen.width == 360 && screen.height == 640) {
938 window.widget.prepareForTransition("fade");
939 orientation = 'portrait';
940 document.getElementById('body').style.backgroundImage = 'url(' + getBackgroundImage() + ')';
941 document.getElementById('body').style.backgroundColor = 'none';
942 window.widget.performTransition();
943 } else if (orientation != 'landscape' && screen.width == 640 && screen.height == 360) {
944 window.widget.prepareForTransition("fade");
945 orientation = 'landscape';
946 document.getElementById('body').style.backgroundImage = 'url(' + getBackgroundImage() + ')';
947 document.getElementById('body').style.backgroundColor = 'none';
948 window.widget.performTransition();
949 }
950 else if (document.getElementById('body').style.backgroundImage == "")
951 {
952 document.getElementById('body').style.backgroundImage = 'url(' + getBackgroundImage() + ')';
953 }
954 }
955 }
956
957 function showHomescreen()
958 {
959 hideViews();
960 document.getElementById("homescreenView").style.display = "block";
961 document.getElementById('body').className = "background";
962 document.onclick = null;
963 updateData();
964 }
965
966 function getLocalizedText(p_Txt)
967 {
968 if (localizedText[p_Txt])
969 return localizedText[p_Txt];
970 else
971 return 'ERROR: missing translation for ' + p_Txt;
972 }
973
974 function showUpdate()
975 {
976 mode = 4;
977 hideViews();
978 document.getElementById("updateView").style.display = "block";
979 document.onclick = null;
980
981 window.menu.setLeftSoftkeyLabel(getLocalizedText('update.checknow'), function(){
982 checkForUpdate();
983 });
984 window.menu.setRightSoftkeyLabel(getLocalizedText('softkey.back'), function()
985 {
986 mode = 1;
987 showFullscreen();
988 });
989
990 document.getElementById("currentVersion").innerHTML = getLocalizedText("update.current") + version;
991 checkForUpdate();
992 }
993
994 function checkForUpdate()
995 {
996 // asynch XHR to server url
997 reqV = new XMLHttpRequest();
998 reqV.onreadystatechange = checkForUpdateCallback;
999 document.getElementById("updateDiv").innerHTML = getLocalizedText("update.checking");
1000 reqV.open("GET", versionURL, true);
1001 reqV.setRequestHeader( "If-Modified-Since", "Sat, 1 Jan 2000 00:00:00 GMT" ); // disable caching
1002 reqV.send(null);
1003 }
1004
1005 function checkForUpdateCallback()
1006 {
1007 if (reqV.readyState == 4) {
1008 if (reqV.status == 200) {
1009 var resultXml = reqV.responseText;
1010 if (resultXml) {
1011 var div = document.getElementById("tmp");
1012 div.innerHTML = resultXml;
1013 var newVersion = div.getElementsByTagName('version')[0].innerHTML;
1014 var newVersionURL = div.getElementsByTagName('url')[0].innerHTML;
1015 div.innerHTML = "";
1016 if (version != newVersion) {
1017 document.getElementById("updateDiv").innerHTML = getLocalizedText("update.download").replace(/%1/, newVersion).replace(/%2/, newVersionURL);
1018 }
1019 else {
1020 document.getElementById("updateDiv").innerHTML = getLocalizedText("update.nonewversion");
1021 }
1022 }
1023 }
1024 else {
1025 document.getElementById("updateDiv").innerHTML = getLocalizedText("update.error") + reqV.status + " " + reqV.responseText;
1026 }
1027 }
1028 }
1029
1030 function hideViews()
1031 {
1032 document.getElementById("homescreenView").style.display = "none";
1033 document.getElementById("fullscreenView").style.display = "none";
1034 document.getElementById("aboutView").style.display = "none";
1035 document.getElementById("settingsView").style.display = "none";
1036 document.getElementById("updateView").style.display = "none";
1037 }
1038 </script>
1039
1040 <style type="text/css">
1041 table { margin:0px; padding:0px; border-spacing:0px; }
1042 td { padding:0px 5px 0px 0px; white-space:nowrap; overflow:hidden; }
1043 hr { color:#ffffff; background-color:#ffffff; height:1px; text-align:left; border-style:none; }
1044 .settingsInfo { display:none; font-style:italic; }
1045 .title { font-weight:bold; font-size:14pt; }
1046 .textInput { width:90%; }
1047 .credits { margin-left:40px; text-indent: -20px; margin-bottom:0px; }
1048 #homescreenView { width: 315px; height:91px; overflow:hidden; }
1049 #calendarList { position:absolute; left:10px; top:4px; width:295px; height:75px; overflow:hidden; }
1050 #name { text-align:center; }
1051 #appicon { display: block; margin-left: auto; margin-right: auto; margin-top: 10px; }
1052 #smallappicon { width:22px; height:22px; margin-right:10px; float:left; }
1053 </style>
1054
1055 </head>
1056
1057 <body id="body" class="background">
1058 <div id="homescreenView">
1059 <div id="calendarList"></div>
1060 </div>
1061 <div id="fullscreenView" style="display:none;">
1062 <img src="Icon.png" id="smallappicon">
1063 <h1 class="title">Coming Next</h1>
1064 <hr />
1065 <div id="fullscreenCalendarList">loading...</div>
1066 </div>
1067 <div id="settingsView" style="display:none">
1068 <img src="Icon.png" id="smallappicon">
1069 <h1 class="title">Settings</h1>
1070 <hr />
1071 <div id="settingsList"></div>
1072 </div>
1073 <div id="aboutView" style="display:none">
1074 <img src="Icon.png" id="appicon">
1075 <h1 id="name">Coming Next</h1>
1076 <hr />
1077 <p>Created by Dr. Cochambre and Michael Prager.</p>
1078 <p>Contributions:</p>
1079 <p class="credits">Paul Moore (bug fixes, new features and code cleanup)</p>
1080 <p class="credits">Manfred Hanselmann (DST support)</p>
1081 <p class="credits">Christophe Milsent (translation support & french translation</p>
1082 <p class="credits">Flavio Nathan (portuguese-brazilian translation</p>
1083 <p>This software is open source and licensed under the GPLv3.</p>
1084 <p>Visit sourceforge.net/projects/comingnext for free updates.</p>
1085 <hr />
1086 </div>
1087 <div id="updateView" style="display:none">
1088 <img src="Icon.png" id="smallappicon">
1089 <h1 class="title">Check for update</h1>
1090 <hr />
1091 <div id="currentVersion">Coming Next ??</div>
1092 <div id="updateDiv"></div>
1093 <div id="tmp" style="display:none;"></div>
1094 </div>
1095 </body>
1096
1097 </html>