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