]> code.delx.au - comingnext/blob - comingNext/index.html
fixed CalendarName property not being passed to calendar events properly. Also fixed...
[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 .calendar1 {}
22 .calendar2 {}
23 .calendar3 {}
24 .calendar4 {}
25 .calendar5 {}
26 .calendar6 {}
27 </style>
28
29 <script type="text/javascript" src="localizedTextStrings.js" charset="utf-8" />
30
31 <script>
32 // valid types for the config object are 'Int', 'Bool', 'String', 'Enum', 'UID'
33 var config = {
34 monthRange: { Type: 'Int', Default: 2, Value: 2,},
35 includeTodos: { Type: 'Bool', Default: true, Value: true,},
36 useBackgroundImage: { Type: 'Bool', Default: true, Value: true,},
37 backgroundImageLocation: { Type: 'Enum', Default: 'internal', Value: 'internal', ValidValues: ['internal', 'external']},
38 showCombinedDateTime: { Type: 'Bool', Default: false, Value: false,},
39 showLocation: { Type: 'Bool', Default: true, Value: true,},
40 showTodayAsText: { Type: 'Bool', Default: true, Value: true,},
41 todayText: { Type: 'String', Default: getLocalizedText('settings.default.todayText'), Value: getLocalizedText('settings.default.todayText'),},
42 tomorrowText: { Type: 'String', Default: getLocalizedText('settings.default.tomorrowText'), Value: getLocalizedText('settings.default.tomorrowText'),},
43 showNowAsText: { Type: 'Bool', Default: true, Value: true,},
44 nowText: { Type: 'String', Default: getLocalizedText('settings.default.nowText'), Value: getLocalizedText('settings.default.nowText'),},
45 markOverdueTodos: { Type: 'Bool', Default: true, Value: true,},
46 overdueText: {Type: 'String', Default: getLocalizedText('settings.default.overdueText'), Value: getLocalizedText('settings.default.overdueText'),},
47 dateSeparator: { Type: 'String', Default: getLocalizedText('settings.default.dateSeparator'), Value: getLocalizedText('settings.default.dateSeparator'),},
48 dateFormat: { Type: 'Enum', Default: 'auto', Value: 'auto', ValidValues: ['auto', 'DDMM', 'MMDD'],},
49 weekDayLength: { Type: 'Int', Default: 2, Value: 2,},
50 updateDataInterval: { Type: 'Int', Default: 5, Value: 5,},
51 calendarApp: { Type: 'UID', Default: 0x10005901, Value: 0x10005901,},
52 eventsPerWidget: { Type: 'Int', Default: 4, Value: 4,},
53 showNothingText: { Type: 'Bool', Default: true, Value: true,},
54 nothingText: { Type: 'String', Default: getLocalizedText('settings.default.nothingText'), Value: getLocalizedText('settings.default.nothingText'),},
55 enableDaylightSaving: { Type: 'Bool', Default: true, Value: true,},
56 daylightSavingOffset: { Type: 'Int', Default: 1, Value: 1,},
57 hideWidgetOnCalendarOpen: { Type: 'Bool', Default: false, Value: false,},
58 showCalendarIndicator: { Type: 'Bool', Default: true, Value: true,},
59 cssStyle_background: { Type: 'String', Default: 'color:#ffffff; background-color:#000000', Value: 'color:#ffffff; background-color:#000000',},
60 cssStyle_backgroundFullscreen: { Type: 'String', Default: 'color:#ffffff; background-color:#000000', Value: 'color:#ffffff; background-color:#000000',},
61 cssStyle_weekDay: { Type: 'String', Default: '', Value: '',},
62 cssStyle_date: { Type: 'String', Default: '', Value: '',},
63 cssStyle_today: { Type: 'String', Default: 'color:#ff0000', Value: 'color:#ff0000',},
64 cssStyle_tomorrow: { Type: 'String', Default: 'color:#0000ff', Value: 'color:#0000ff',},
65 cssStyle_time: { Type: 'String', Default: '', Value: '',},
66 cssStyle_now: { Type: 'String', Default: 'color:#ff00ff', Value: 'color:#ff00ff',},
67 cssStyle_description: { Type: 'String', Default: '', Value: '',},
68 cssStyle_icon: { Type: 'String', Default: 'width:15px; height:15px', Value: 'width:15px; height:15px',},
69 cssStyle_overdue: { Type: 'String', Default: 'color:#ffff00', Value: 'color:#ffff00',},
70 cssStyle_calendar1: { Type: 'String', Default: 'background-color:#800000', Value: 'background-color:#800000',},
71 cssStyle_calendar2: { Type: 'String', Default: 'background-color:#ff0000', Value: 'background-color:#ff0000',},
72 cssStyle_calendar3: { Type: 'String', Default: 'background-color:#808000', Value: 'background-color:#808000',},
73 cssStyle_calendar4: { Type: 'String', Default: 'background-color:#ffff00', Value: 'background-color:#ffff00',},
74 cssStyle_calendar5: { Type: 'String', Default: 'background-color:#008000', Value: 'background-color:#008000',},
75 cssStyle_calendar6: { Type: 'String', Default: 'background-color:#008080', Value: 'background-color:#008080',},
76 }
77
78
79
80 //-------------------------------------------------------
81 // Nothing of interest from here on...
82 //-------------------------------------------------------
83 var panelNum = 0; // use 1 for second panel
84 var version = "1.30";
85 var versionURL = "http://comingnext.sourceforge.net/version.xml";
86 var calendarService = null;
87 var cacheEntriesHtml = [];
88 var months_translated = [];
89 var orientation = '';
90 var now = new Date();
91 var mode = 0; // 0 = homescreen, 1 = fullscreen, 2 = settings, 3 = about, 4 = check for update
92 var reqV = null;
93 var settingsCalEntryId = null;
94 var settingsCache = null;
95 var notificationRequests = new Array();
96 var calendarList = [];
97 var calendarColors = [];
98
99 // vars for daylight saving time
100 var summertime = false; // true, if current date is in summer, false if in winter
101 var daylightSavingDates = new Object(); // caches calculated DST winter and summer time shift dates
102
103 // this is a list of data fields a calendar event can have
104 var entryFields = [
105 "id",
106 "Type",
107 "CalendarName",
108 "Summary",
109 "Location",
110 "Status",
111 "StartTime",
112 "EndTime",
113 "InstanceStartTime",
114 "InstanceEndTime"
115 ];
116
117 window.onload = init;
118 window.onresize = updateScreen;
119 window.onshow = updateScreen;
120
121 function isLeapYear( year ) {
122 if (( year % 4 == 0 && year % 100 != 0 ) || year % 400 == 0 )
123 return true;
124 else
125 return false;
126 }
127
128 function calcLeapYear(year, days)
129 {
130 if (isLeapYear(year))
131 return ++days;
132 else
133 return days;
134 }
135
136 function subToSunday(myDate, year, days, prevMonthDays)
137 {
138 for (i = myDate.getDay(); i > 0 ;i--)
139 days--;
140 days -= prevMonthDays;
141 days = isLeapYear(year) ? --days : days;
142 return days;
143 }
144
145 function isSummertime(curDate)
146 {
147 var summer = false;
148 var winter = false;
149
150 // if we already calculated DST summer and winter time dates for this year, use cached values
151 var dst = daylightSavingDates[curDate.getFullYear()];
152 if (!dst) {
153 var thisYearS = new Date(curDate.getFullYear(), 3, 0, 0, 0, 0 );
154 var thisYearW = new Date(curDate.getFullYear(), 10, 0, 0, 0, 0 );
155 var nextYearS = new Date(curDate.getFullYear() + 1, 3, 0, 0, 0, 0 );
156 var nextYearW = new Date(curDate.getFullYear() + 1, 10, 0, 0, 0, 0 );
157
158 thisYearSDays = nextYearSDays = 90;
159 thisYearWDays = nextYearWDays = 304;
160
161 thisYearSDays = calcLeapYear(curDate.getFullYear(), thisYearSDays);
162 thisYearWDays = calcLeapYear(curDate.getFullYear(), thisYearWDays);
163 nextYearSDays = calcLeapYear(curDate.getFullYear() + 1, nextYearSDays);
164 nextYearWDays = calcLeapYear(curDate.getFullYear() + 1, nextYearWDays);
165
166 thisYearSDays = subToSunday(thisYearS, curDate.getFullYear(), thisYearSDays, 59);
167 thisYearWDays = subToSunday(thisYearW, curDate.getFullYear(), thisYearWDays, 273);
168 nextYearSDays = subToSunday(nextYearS, curDate.getFullYear() + 1, nextYearSDays, 59);
169 nextYearWDays = subToSunday(nextYearW, curDate.getFullYear() + 1, nextYearWDays, 273);
170
171 dst = {
172 Summer: new Date (curDate.getFullYear(), 03-1, thisYearSDays, 2, 0, 0),
173 Winter: new Date (curDate.getFullYear(), 10-1, thisYearWDays, 2, 0, 0),
174 }
175 daylightSavingDates[curDate.getFullYear()] = dst;
176 }
177
178 if (dst.Summer < curDate)
179 summer = true;
180 if (dst.Winter < curDate)
181 winter = true;
182 if (summer && !winter)
183 return true;
184 else
185 return false;
186 }
187
188 function error(message)
189 {
190 console.info('Error: ' + message);
191 document.getElementById("calendarList").innerHTML = 'Error: ' + message;
192 }
193
194 function areDatesEqual(date1, date2)
195 {
196 return (date1.getFullYear() == date2.getFullYear() &&
197 date1.getMonth() == date2.getMonth() &&
198 date1.getDate() == date2.getDate());
199 }
200
201 function isTomorrow(date)
202 {
203 // tommorow = now + 1 day
204 // ToDo: some days can be shorter as 24 hours(daylight saving change day)
205 return areDatesEqual(date, new Date (now.getTime() + 24*60*60*1000));
206 }
207
208 function isToday(date)
209 {
210 return areDatesEqual(date, now);
211 }
212
213 function collectLocales()
214 {
215 var tmpyear = 2000 + panelNum;
216 var month = 0;
217
218 if (months_translated.length > 0)
219 return;
220 for (month = 0; month < 12; month++) {
221 var startDate = new Date(tmpyear, month, 15);
222
223 var item = new Object();
224 item.Type = "DayEvent";
225 item.StartTime = startDate;
226 item.Summary = "__temp" + month;
227
228 var criteria = new Object();
229 criteria.Type = "CalendarEntry";
230 criteria.Item = item;
231
232 try {
233 var result = calendarService.IDataSource.Add(criteria);
234 if (result.ErrorCode)
235 error(result.ErrorMessage);
236 } catch (e) {
237 error("collectLocales: " + e + ', line ' + e.line);
238 }
239 }
240 try {
241 var startTime = new Date(tmpyear,0,1);
242 var endTime = new Date(tmpyear,11,31);
243 var listFiltering = {
244 Type:'CalendarEntry',
245 Filter:{
246 StartRange: startTime,
247 EndRange: endTime,
248 SearchText: '__temp',
249 Type: 'DayEvent'
250 }
251 }
252 var result = calendarService.IDataSource.GetList(listFiltering);
253 if (result.ErrorCode) {
254 error(result.ErrorMessage);
255 return;
256 }
257 var list = result.ReturnValue;
258 } catch(e) {
259 error(e + ', line ' + e.line);
260 return;
261 }
262 var ids = new Array();
263 try {
264 var entry;
265 var counter = 0;
266 var dateArr = [];
267
268 while (list && (entry = list.getNext()) != undefined) {
269 dateArr = entry.StartTime.replace(/,/g,'').replace(/\./g,':').replace(/ /g,' ').split(' ');
270 var day = dateArr[1];
271 var month = dateArr[2];
272 var year = dateArr[3];
273
274 // make sure month is set properly
275 if (isNaN(parseInt(day))) {
276 var tmp = day;
277 day = month;
278 month = tmp;
279 } else if (isNaN(parseInt(year))) {
280 var tmp = year;
281 year = month;
282 month = tmp;
283 }
284
285 console.info(entry.StartTime + ' -> ' + month + ' ' + counter);
286 ids[counter] = entry.id;
287 months_translated[month] = counter + 1;
288 counter++;
289 }
290 } catch(e) {
291 error(e + ', line ' + e.line);
292 return;
293 }
294 console.info(ids);
295 try {
296 var criteria = new Object();
297 criteria.Type = "CalendarEntry";
298 criteria.Data = {
299 IdList: ids
300 }
301
302 var result = calendarService.IDataSource.Delete(criteria);
303 if (result.ErrorCode)
304 error(result.ErrorMessage);
305 } catch(e) {
306 error('deleting temp calendar entries:' + e + ', line ' + e.line);
307 return;
308 }
309 }
310
311 function requestNotification()
312 {
313 var criteria = new Object();
314 criteria.Type = "CalendarEntry";
315 criteria.Filter = new Object();
316 for(var i=0; i < calendarList.length; i++) {
317 criteria.Filter.CalendarName = calendarList[i];
318 try {
319 var notificationRequest = calendarService.IDataSource.RequestNotification(criteria, callback);
320 if (notificationRequest.ErrorCode)
321 error('requestNotification failed with error code ' + notificationRequest.ErrorCode);
322 notificationRequests.push(notificationRequest);
323 } catch (e) {
324 error("requestNotification: " + e + ', line ' + e.line);
325 }
326 }
327
328 var criteria2 = new Object();
329 criteria2.Type = "CalendarEntry";
330 criteria2.Filter = new Object();
331 criteria2.Filter.LocalIdList = new Array();
332 criteria2.Filter.LocalIdList[0] = settingsCalEntryId;
333 try {
334 var notificationRequest = calendarService.IDataSource.RequestNotification(criteria2, settingsCallback);
335 if (notificationRequest.ErrorCode)
336 error('requestNotification failed with error code ' + notificationRequest.ErrorCode);
337 notificationRequests.push(notificationRequest);
338 } catch (e) {
339 error("requestNotification: " + e + ', line ' + e.line);
340 }
341 }
342
343 function cancelNotification()
344 {
345 for(var i=0; i < notificationRequests.length; i++) {
346 try {
347 var result = calendarService.IDataSource.Cancel(notificationRequests[i]);
348 if (result.ErrorCode)
349 error('cancelNotification failed with error code ' + result.ErrorCode);
350 } catch (e) {
351 error("cancelNotification: " + e + ', line ' + e.line);
352 }
353 }
354 }
355
356 function callback(transId, eventCode, result)
357 {
358 console.info("callback(): panelNum: %d transId: %d eventCode: %d result.ErrorCode: %d", panelNum, transId, eventCode, result.ErrorCode);
359 updateData();
360 }
361
362 function settingsCallback(transId, eventCode, result)
363 {
364 console.info("settingsCallback(): panelNum: %d transId: %d eventCode: %d result.ErrorCode: %d", panelNum, transId, eventCode, result.ErrorCode);
365 loadSettings();
366 }
367
368 function parseDate(dateString)
369 {
370 /*
371 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:
372 Wednesday, 26 August, 2009 24:00:00
373 Wednesday, 26 August, 2009 12:00:00 am
374 Wednesday, August 26, 2009 12:00:00 am
375 Wednesday, 2009 August, 26 12:00:00 am
376 Wednesday, 2009 August, 28 8.00.00 pm
377 Wednesday, 2009 August, 28 08:00:00 PM
378 */
379
380 if (dateString == "" || dateString == null)
381 return null;
382 var dateArr = dateString.replace(/,/g,'').replace(/\./g,':').replace(/ /g,' ').split(' ');
383 if (dateArr.length != 5 && dateArr.length != 6)
384 return null;
385
386 // parse date
387 var weekDay = dateArr[0];
388 var day = dateArr[1];
389 var month = dateArr[2];
390 var year = dateArr[3];
391 // make sure month is set properly
392 if (isNaN(parseInt(day))) {
393 var tmp = day;
394 day = month;
395 month = tmp;
396 } else if (isNaN(parseInt(year))) {
397 var tmp = year;
398 year = month;
399 month = tmp;
400 }
401 // make sure day and year are set properly
402 if (Number(day) > Number(year)) {
403 var tmp = year;
404 year = day;
405 day = tmp;
406 }
407 month = months_translated[month];
408
409 // parse time
410 var timeArr = dateArr[4].split(':');
411 if (timeArr.length != 3)
412 return null;
413 var hours = Number(timeArr[0]);
414 var minutes = Number(timeArr[1]);
415 var seconds = Number(timeArr[2]);
416 if (dateArr.length == 6 && dateArr[5].toLowerCase() == 'pm' && hours < 12)
417 hours += 12;
418 if (dateArr.length == 6 && dateArr[5].toLowerCase() == 'am' && hours == 12)
419 hours = 0;
420
421 var result = new Date(year, month - 1, day, hours, minutes, seconds);
422
423 // take care of daylight saving time
424 if (config['enableDaylightSaving'].Value) {
425
426 // determine if date is in summer or winter time
427 var dateSummerTime = isSummertime(result);
428
429 // work around bug in Nokias calendar api resulting in dates within a different DST to be off by 1 hour
430 if (summertime && !dateSummerTime) {
431 result = new Date(result.getTime() - 1000 * 60 * 60 * config['daylightSavingOffset'].Value); // -1 hour
432 console.info('parseDate(): fixing time -1h: ' + result);
433 }
434 else if (!summertime && dateSummerTime) {
435 result = new Date(result.getTime() + 1000 * 60 * 60 * config['daylightSavingOffset'].Value); // +1 hour
436 console.info('parseDate(): fixing time +1h: ' + result);
437 }
438 }
439
440 return result;
441 }
442
443 // 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"
444 function formatDate(date, format)
445 {
446 var day = date.getDate().toString();
447 var month = (date.getMonth() + 1).toString();
448 while (day.length < 2) { day = '0' + day; }
449 while (month.length < 2) { month = '0' + month; }
450
451 if (config['showTodayAsText'].Value && isToday(date))
452 return '<span class="today">' + config['todayText'].Value + '</span>';
453 if (config['showTodayAsText'].Value && isTomorrow(date))
454 return '<span class="tomorrow">' + config['tomorrowText'].Value + '</span>';
455
456 var dateArr = format.replace(/,/g,'').replace(/\./g,':').replace(/ /g,' ').split(' ');
457 if (dateArr.length != 5 && dateArr.length != 6) {
458 // we don't know how to format this
459 if (config['dateFormat'].Value == 'auto' || config['dateFormat'].Value == 'DDMM')
460 return day + config['dateSeparator'].Value + month;
461 else
462 return month + config['dateSeparator'].Value + day;
463 }
464
465 var dayFirst = true;
466 if (config['dateFormat'].Value == 'MMDD')
467 dayFirst = false;
468 else if (config['dateFormat'].Value == 'DDMM')
469 dayFirst = true;
470 else {
471 // config['dateFormat'].Value == 'auto', try to detect system setting
472 // parse date
473 var day_ = dateArr[1];
474 var month_ = dateArr[2];
475 var year_ = dateArr[3];
476 // make sure month is set properly
477 if (isNaN(parseInt(day_))) {
478 var tmp = day_;
479 day_ = month_;
480 month_ = tmp;
481 dayFirst = false;
482 } else if (isNaN(parseInt(year_))) {
483 var tmp = year_;
484 year_ = month_;
485 month_ = tmp;
486 dayFirst = true;
487 }
488 // make sure day and year are set properly
489 if (Number(day_) > Number(year_))
490 dayFirst = false;
491 }
492
493 if (dayFirst)
494 return day + config['dateSeparator'].Value + month;
495 else
496 return month + config['dateSeparator'].Value + day;
497 }
498
499 function formatTime(date)
500 {
501 // date is a Date() object
502 date.setSeconds(0); // we don't care about seconds
503 var time = date.toLocaleTimeString().replace(/[\.:]00/, ''); // remove seconds from string
504 if (time.replace(/\./, ':').split(':')[0].length < 2)
505 time = '0' + time;
506 if (config['showNowAsText'].Value && date.getTime() == now.getTime())
507 time = '<span class="now">' + config['nowText'].Value + '</span>';
508 return time;
509 }
510
511 function updateData()
512 {
513 console.info('updateData()');
514
515 // check if we got additional or less calendars since our last update
516 var newCalendarList = listCalendars();
517 if (newCalendarList.length != calendarList.length) {
518 calendarList = newCalendarList;
519 updateCalendarColors();
520 cancelNotification();
521 requestNotification();
522 }
523
524 try {
525 // meetings have time
526 // 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
527 now = new Date();
528 summertime = isSummertime(now); // cache summer time info for today
529 var meetingList = [];
530 for(var i=0; i < calendarList.length; i++) {
531 var meetingListFiltering = {
532 Type:'CalendarEntry',
533 Filter:{
534 CalendarName: calendarList[i],
535 StartRange: (new Date(now.getFullYear(), now.getMonth(), now.getDate(), 0, 0, 0)),
536 EndRange: (new Date(now.getFullYear(), now.getMonth() + config['monthRange'].Value, now.getDate(), 0, 0, 0))
537 }
538 }
539 var meetingResult = calendarService.IDataSource.GetList(meetingListFiltering);
540 if (meetingResult.ErrorCode != 0)
541 throw("Error fetching calendar data: " + meetingResult.ErrorCode + ': ' + meetingResult.ErrorMessage);
542 var list = meetingResult.ReturnValue;
543 meetingList = meetingList.concat(listToArray(list, calendarList[i]));
544 }
545 console.info("updateData(): meetingList.sort()");
546 meetingList.sort(sortCalendarEntries);
547
548 // todos don't, they start on 00:00 hrs., but should be visible anyway
549 // this will generate a list of passed todos. We have to check if they have been marked as "done" yet
550 if (config['includeTodos'].Value) {
551 var todayTodoList = [];
552 for(var i=0; i < calendarList.length; i++) {
553 var todayTodoListFiltering = {
554 Type:'CalendarEntry',
555 Filter:{
556 CalendarName: calendarList[i],
557 Type: 'ToDo',
558 StartRange: (new Date(now.getFullYear() - 1, now.getMonth(), now.getDate(), 0, 0, 0)),
559 EndRange: (new Date(now.getFullYear(), now.getMonth(), now.getDate(), 0, 0, 1))
560 }
561 }
562 var todayTodoResult = calendarService.IDataSource.GetList(todayTodoListFiltering);
563 var list = todayTodoResult.ReturnValue;
564 todayTodoList = todayTodoList.concat(listToArray(list, calendarList[i]));
565 }
566 console.info("updateData(): todayTodoList.sort()");
567 todayTodoList.sort(sortCalendarEntries);
568 var entryLists = [todayTodoList, meetingList];
569 } else {
570 var entryLists = [meetingList];
571 }
572 } catch(e) {
573 error('loading Calendar items list:' + e + ', line ' + e.line);
574 return;
575 }
576
577 try {
578 var entry;
579 var counter = 0;
580 var entryDate = '';
581 var dateArr = [];
582 var fontsize = 'normal';
583 if (mode == 0) {
584 if (config['eventsPerWidget'].Value == 3) {
585 fontsize = '17pt';
586 changeCssClass('.icon', 'width:20px; height:20px');
587 }
588 else if (config['eventsPerWidget'].Value == 5) {
589 fontsize = '10pt';
590 changeCssClass('.icon', 'width:10px; height:10px');
591 }
592 else if (config['eventsPerWidget'].Value == 6) {
593 fontsize = '8pt';
594 changeCssClass('.icon', 'width:8px; height:8px');
595 }
596 }
597 else
598 changeCssClass('.icon', config['cssStyle_icon'].Value);
599 var entriesHtml = '<table style="font-size:' + fontsize + ';">';
600 var eventIds = [];
601 var max;
602 if (mode == 0)
603 max = (panelNum + 1) * config['eventsPerWidget'].Value;
604 else
605 max = 30; // we can display a lot more events in fullscreen mode
606
607 var listinfo = "";
608 for (var i=0; i < entryLists.length; i++) {
609 listinfo = listinfo + " " + entryLists[i].length;
610 var entrieslist = "";
611 for (var j=0; j < entryLists[i].length; j++) {
612 entrieslist += entryLists[i][j].Summary + ", ";
613 }
614 console.info("updateData(): entrieslist: " + entrieslist);
615 }
616 console.info("updateData(): inner loop, " + entryLists.length + " lists, [" + listinfo + "] entries");
617
618 // the first outer loop iteration is for passed ToDos, the second loop is for all upcomming events (may also include ToDos)
619 for (var i=0; counter < max && i < entryLists.length; i++) {
620 for (var j=0; (counter < max) && (j < entryLists[i].length); j++) {
621 entry = entryLists[i][j];
622 counter++;
623
624 // output event info for debugging
625 var entryInfo = "event: ";
626 for(var k=0; k < entryFields.length; ++k) {
627 if (entry[entryFields[k]] != undefined) {
628 entryInfo += entryFields[k] + "=" + entry[entryFields[k]] + ",";
629 }
630 }
631 console.info(entryInfo);
632
633 // we don't want ToDos when includeTodos == false or when they are completed
634 if (entry.Type == 'ToDo' && (entry.Status == "TodoCompleted" || !config['includeTodos'].Value)) {
635 console.info('skipping ' + entry.id );
636 counter--;
637 continue;
638 }
639
640 // make sure that we don't include an event twice (useful for ToDos that might come up twice)
641 if (eventIds[entry.id] == 1 && entry.Type == 'ToDo') {
642 console.info('skipped (already included) ' + entry.id);
643 counter--;
644 continue;
645 } else
646 eventIds[entry.id] = 1;
647
648 // summary can be undefined!
649 var Summary = ((entry.Summary == null) ? '' : entry.Summary);
650 if (entry.Type == 'Meeting' && entry.Location != '' && config['showLocation'].Value)
651 Summary += ', ' + entry.Location;
652
653 // fix by yves: determine start and end dates/times
654 entryStartTime = ((entry.InstanceStartTime == null) ? entry.StartTime : entry.InstanceStartTime);
655 entryEndTime = ((entry.InstanceEndTime == null) ? entry.EndTime : entry.InstanceEndTime);
656
657 // there can be ToDos that have no date at all!
658 if (entry.Type == 'ToDo' && entry.EndTime == null)
659 entryDate = ""; // this will cause parseDate(entryDate) to return null;
660 else
661 entryDate = ((entry.Type == 'ToDo') ? entryEndTime : entryStartTime); // ToDo's use their EndTime, the rest use StartTime
662
663 // Convert date/time string to Date object
664 var date = parseDate(entryDate);
665 console.info('date: ' + date);
666 var endDate = ((entryEndTime == null) ? null : parseDate(entryEndTime));
667 console.info('endDate: ' + endDate);
668
669 // check if meeting event has already passed
670 if (entry.Type == 'Meeting') {
671 var compareTime = ((endDate == null) ? date.getTime() : endDate.getTime());
672 if (now.getTime() > compareTime) {
673 console.info('skipping Meeting (already passed) ' + entry.id);
674 counter--;
675 eventIds[entry.id] = 0;
676 continue;
677 }
678 }
679
680 // check if anniversary passed (not sure why they are in the list, the query was only for today - nokia?)
681 if (entry.Type == 'Anniversary') {
682 var tmp = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 0,0,0);
683 if (date.getTime() < tmp.getTime()) {
684 console.info('skipping Anniversary (already passed) ' + entry.id);
685 counter--;
686 eventIds[entry.id] = 0;
687 continue;
688 }
689 }
690
691 // fix DayEvents end time. End times are off by 1 Second. It's possible that the event has already passed
692 if (entry.Type == 'DayEvent' && endDate != null) {
693 endDate.setMinutes(endDate.getMinutes() - 1);
694 console.info('fixing DayEvent endDate: ' + endDate);
695 if (now.getTime() > endDate.getTime()) {
696 console.info('event already passed ' + entry.id);
697 counter--;
698 eventIds[entry.id] = 0;
699 continue;
700 }
701 }
702
703 // check if the event is currently taking place
704 if (entryStartTime != null && entryEndTime != null && date != null && endDate != null) {
705 // check if we are between start and endtime
706 if ((date.getTime() < now.getTime()) && (now.getTime() < endDate.getTime())) {
707 date = now; // change appointment date/time to now
708 console.info('event is currently taking place: ' + date);
709 }
710 }
711
712 // skip events for the first panel in case this is the second one and we're not in fullscreen mode
713 if (mode == 0 && panelNum > 0 && counter < panelNum * config['eventsPerWidget'].Value + 1) {
714 console.info('skipping (already in first widget) ' + entry.id);
715 continue;
716 }
717
718 // mark overdue todos
719 var overdue = false;
720 if (entry.Type == 'ToDo' && date != null) {
721 var tmp1 = new Date(date.getFullYear(), date.getMonth(), date.getDate(), 0,0,0);
722 var tmp2 = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 0,0,0);
723 if (tmp1.getTime() < tmp2.getTime()) {
724 overdue = true;
725 }
726 }
727
728 // generate html output
729 entriesHtml += '<tr>';
730 if (config['showCalendarIndicator'].Value && calendarList.length > 1) {
731 entriesHtml += '<td><span class="calendar' + calendarColors[entry.CalendarName] + '">&nbsp;</span></td>';
732 }
733 entriesHtml += '<td><img class="icon" src="' + entry.Type + '.png" /></td>';
734 if(date == null) {
735 // some languages have very strange locale date formats, can't parse all those. Also some todos don't have dates at all.
736 entriesHtml += '<td colspan="4"><span class="date">' + entryDate + '</span> ';
737 } else {
738 var weekDay = date.toLocaleDateString().substr(0,config['weekDayLength'].Value);
739 var time = formatTime(date);
740 var dateStr = formatDate(date, entryDate);
741 if (entry.Type == 'ToDo' && overdue && config['markOverdueTodos'].Value) {
742 dateStr = '<span class="overdue">' + config['overdueText'].Value + '</span>';
743 entriesHtml += '<td colspan="4" width="1px"><span class="date">' + dateStr + '</span> ';
744 } else if (entry.Type == 'ToDo' || entry.Type == 'Anniversary' || entry.Type == 'DayEvent' || entry.Type == 'Reminder') {
745 if ((isToday(date) || isTomorrow(date)) && config['showTodayAsText'].Value) // show weekday if the date string is not text. looks odd otherwise
746 entriesHtml += '<td colspan="4" width="1px"><span class="date">' + dateStr + '</span> ';
747 else
748 entriesHtml += '<td class="weekDay" width="1px">' + weekDay + '</td><td width="1px" class="date">' + dateStr + '</td><td colspan="2">';
749 } else if (entry.Type == 'Meeting') {
750 if (config['showCombinedDateTime'].Value) {
751 if (isToday(date))
752 entriesHtml += '<td width="1px" colspan="4"><span class="today">' + time + '</span> ';
753 else if (isTomorrow(date))
754 entriesHtml += '<td width="1px" colspan="4"><span class="tomorrow">' + dateStr + '</span> <span class="time">' + time + '</span> ';
755 else
756 entriesHtml += '<td width="1px" class="weekDay">' + weekDay + '</td><td width="1px" class="date">' + dateStr + '</td><td colspan="2">';
757 } else {
758 if ((isToday(date) || isTomorrow(date)) && config['showTodayAsText'].Value)
759 entriesHtml += '<td colspan="4" width="1px"><span class="today">' + dateStr + '</span> <span class="time">' + time + '</span> ';
760 else
761 entriesHtml += '<td width="1px" class="weekDay">' + weekDay + '</td><td width="1px" class="date">' + dateStr + '</td><td width="1px" class="time">' + time + '</td><td>';
762 }
763 }
764 }
765 entriesHtml += '<span class="description">' + Summary + '</span></td></tr>';
766 }
767 }
768 entriesHtml += '</table>';
769 if (config['showNothingText'].Value && entriesHtml == '<table></table>') {
770 var text = config['nothingText'].Value.replace(/%d/, config['monthRange'].Value);
771 entriesHtml = '<div style="width:295px; height:75px; text-align:center; line-height:75px; overflow:visible;">' + text + '</div>';
772 }
773 if (cacheEntriesHtml != entriesHtml) {
774 if (mode == 0)
775 document.getElementById('calendarList').innerHTML = entriesHtml;
776 else
777 document.getElementById('fullscreenCalendarList').innerHTML = entriesHtml;
778 cacheEntriesHtml = entriesHtml;
779 }
780 } catch(e) {
781 error('displaying list:' + e + ', line ' + e.line);
782 return;
783 }
784 }
785
786 function updateScreen()
787 {
788 // check if opening fullscreen
789 if( window.innerHeight > 91 && mode == 0) {
790 mode = 1;
791 cacheEntriesHtml = '';
792 document.getElementById('body').style.backgroundImage = "";
793 showFullscreen();
794 }
795 else if (window.innerHeight <= 91 && mode != 0) {
796 mode = 0;
797 cacheEntriesHtml = '';
798 showHomescreen();
799 }
800
801 if (mode == 0)
802 updateHomescreen();
803 else if (mode == 1)
804 updateFullscreen();
805 }
806
807 function launchCalendar()
808 {
809 try {
810 widget.openApplication(config['calendarApp'].Value, "");
811 if (config['hideWidgetOnCalendarOpen'].Value)
812 window.close();
813 } catch(e) {
814 error('starting Calendar App');
815 return;
816 }
817 }
818
819 function init()
820 {
821 console.info('New widget instance starting up...');
822
823 try {
824 // call calendar service
825 if (device != "undefined")
826 calendarService = device.getServiceObject("Service.Calendar", "IDataSource");
827 else
828 throw('device object does not exist');
829 } catch(e) {
830 error('loading Calendar service: ' + e + ', line ' + e.line);
831 return;
832 }
833
834 calendarList = listCalendars();
835 loadSettings();
836 updateCalendarColors();
837 collectLocales();
838 //updateData();
839 requestNotification();
840 window.setInterval('updateData()', 1000 * 60 * config['updateDataInterval'].Value);
841 document.getElementById("settingsTitle").innerHTML = getLocalizedText('menu.settings');
842
843 if (window.innerHeight > 91) {
844 mode = 0; // we're starting fullscreen, we set mode to homescreen in order to let updateScreen() do all the work for us
845 }
846 else {
847 mode = 1;
848 }
849 console.info("init(): updateScreen()");
850 updateScreen();
851 if (config['useBackgroundImage'].Value)
852 // check for screen rotation every 1 secs
853 window.setInterval('updateScreen()', 1000 * 1);
854 console.info("init(): finished...");
855 }
856
857 function createMenu()
858 {
859 window.menu.setLeftSoftkeyLabel("",null);
860 window.menu.setRightSoftkeyLabel("",null);
861 var id = 0;
862 var menuSettings = new MenuItem(getLocalizedText('menu.settings'), id++);
863 var menuCallApp = new MenuItem(getLocalizedText('menu.openCalendarApp'), id++);
864 var menuUpdate = new MenuItem(getLocalizedText('menu.update'), id++);
865 var menuAbout = new MenuItem(getLocalizedText('menu.about'), id++);
866 menuSettings.onSelect = showSettings;
867 menuAbout.onSelect = showAbout;
868 menuCallApp.onSelect = launchCalendar;
869 menuUpdate.onSelect = showUpdate;
870 window.menu.clear();
871 window.menu.append(menuCallApp);
872 window.menu.append(menuSettings);
873 window.menu.append(menuUpdate);
874 window.menu.append(menuAbout);
875 }
876
877 function showSettings()
878 {
879 mode = 2;
880 hideViews();
881 document.getElementById("settingsView").style.display = "block";
882 document.onclick = null;
883
884 window.menu.setLeftSoftkeyLabel(getLocalizedText('settings.save'), function()
885 {
886 for (var key in config) {
887 if (config[key].Type == 'String')
888 config[key].Value = document.forms[0].elements["settings." + key].value;
889 else if (config[key].Type == 'Int') {
890 config[key].Value = parseInt(document.forms[0].elements["settings." + key].value);
891 if (config[key].Value < 0)
892 config[key].Value = config[key].Default;
893 }
894 else if (config[key].Type == 'Bool')
895 config[key].Value = document.forms[0].elements["settings." + key].checked;
896 else if (config[key].Type == 'UID')
897 config[key].Value = parseInt(document.forms[0].elements["settings." + key].value);
898 else if (config[key].Type == 'Enum') {
899 config[key].Value = document.forms[0].elements["settings." + key].value;
900 if (config[key].ValidValues.indexOf(config[key].Value) == -1)
901 config[key].Value = config[key].Default;
902 }
903 }
904
905 updateCssClasses();
906
907 saveSettings();
908
909 mode = 1;
910 showFullscreen();
911 });
912 window.menu.setRightSoftkeyLabel(getLocalizedText('settings.cancel'), function()
913 {
914 mode = 1;
915 showFullscreen();
916 });
917
918 var settingsHtml = '<form>';
919 for (var key in config) {
920 if (config[key].Type == 'String') {
921 var prefix = "";
922 if (key.substring(0,9) == "cssStyle_")
923 prefix = getLocalizedText('settings.cssStyle_prefix');
924 settingsHtml += '<table><tr><td>' + prefix + getLocalizedText('settings.name.' + key) + '<br /><input class="textInput" name="settings.' + key + '" type="text" value="' + config[key].Value + '" /></td>' + printHintBox(getLocalizedText('settings.info.' + key)) + '<hr />';
925 }
926 else if (config[key].Type == 'Int')
927 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 />';
928 else if (config[key].Type == 'Bool')
929 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 />';
930 else if (config[key].Type == 'UID')
931 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 />';
932 else if (config[key].Type == 'Enum') {
933 settingsHtml += '<table><tr><td>' + getLocalizedText('settings.name.' + key) + '<br /><select name="settings.' + key + '" size="1">';
934 for(var i = 0; i < config[key].ValidValues.length; i++)
935 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>';
936 settingsHtml += '</select></div></td>' + printHintBox(getLocalizedText('settings.info.' + key)) + '<hr />';
937 }
938 }
939 settingsHtml += '<input name="reset" type="button" value="' + getLocalizedText('settings.restoreDefaults') + '" onclick="javascript:restoreDefaultSettings();showSettings();" />';
940 settingsHtml += '</form>';
941 document.getElementById("settingsList").innerHTML = settingsHtml;
942 }
943
944 function changeCssClass(classname, properties)
945 {
946 for(var i = 0; i < document.styleSheets[0]['cssRules'].length; i++)
947 {
948 if (document.styleSheets[0]['cssRules'][i].selectorText == classname) {
949 document.styleSheets[0].deleteRule(i);
950 document.styleSheets[0].insertRule(classname + ' { ' + properties + ' }', document.styleSheets[0]['cssRules'].length);
951 break;
952 }
953 }
954 }
955
956 function updateCssClasses()
957 {
958 for(var key in config) {
959 changeCssClass(getLocalizedText('settings.name.' + key), config[key].Value);
960 }
961 }
962
963 function getSettingsCalEntryId()
964 {
965 if (settingsCalEntryId == null) {
966 // check if entry already exists
967 var listFiltering = {
968 Type:'CalendarEntry',
969 Filter:{
970 StartRange: new Date(2000, 0, 1),
971 EndRange: new Date(2000, 0, 1),
972 SearchText: 'ComingNext Settings|',
973 Type: 'DayEvent'
974 }
975 }
976 var result = calendarService.IDataSource.GetList(listFiltering);
977 if (result.ErrorCode) {
978 error(result.ErrorMessage);
979 return;
980 }
981 var list = result.ReturnValue;
982 var entry = list.getNext();
983 if (entry != undefined) {
984 settingsCalEntryId = entry.LocalId;
985 console.info("settingsCalEntryId=" + settingsCalEntryId);
986 }
987 else { // create settings item
988 var item = new Object();
989 item.Type = "DayEvent";
990 item.StartTime = new Date(2000, 0, 1);
991 item.Summary = "ComingNext Settings|";
992
993 var criteria = new Object();
994 criteria.Type = "CalendarEntry";
995 criteria.Item = item;
996
997 try {
998 var result = calendarService.IDataSource.Add(criteria);
999 if (result.ErrorCode)
1000 error(result.ErrorMessage);
1001 } catch (e) {
1002 error("getSettingsCalEntryId: " + e + ', line ' + e.line);
1003 }
1004
1005 getSettingsCalEntryId();
1006 }
1007 }
1008 }
1009
1010 function restoreDefaultSettings()
1011 {
1012 for (var key in config)
1013 config[key].Value = config[key].Default;
1014 }
1015
1016 function loadSettings()
1017 {
1018 getSettingsCalEntryId();
1019 var listFiltering = {
1020 Type:'CalendarEntry',
1021 Filter:{
1022 LocalId: settingsCalEntryId
1023 }
1024 }
1025 var result = calendarService.IDataSource.GetList(listFiltering);
1026 if (result.ErrorCode) {
1027 error(result.ErrorMessage);
1028 return;
1029 }
1030 var entry = result.ReturnValue.getNext();
1031 if (entry != undefined) {
1032 console.info("Loading Settings...");
1033 // only reload settings if they chanced since the last reload
1034 if (settingsCache != entry.Summary)
1035 {
1036 restoreDefaultSettings();
1037 var stringlist = entry.Summary.split("|");
1038 // skip the first two entries, those contain header and version info
1039 for(var i = 2; i < stringlist.length - 1; i++) {
1040 var pair = stringlist[i].split('=');
1041 var key = pair[0];
1042 var value = pair[1];
1043 console.info('stringlist: ' + key + '=\'' + value + '\'');
1044 if (config[key].Type == 'Int')
1045 config[key].Value = Number(value);
1046 else if (config[key].Type == 'String')
1047 config[key].Value = value;
1048 else if (config[key].Type == 'Bool')
1049 config[key].Value = (value == 'true')
1050 else if (config[key].Type == 'Enum')
1051 config[key].Value = value;
1052 else if (config[key].Type == 'UID')
1053 config[key].Value = Number(value);
1054 }
1055 settingsCache = entry.Summary;
1056 updateCssClasses();
1057 }
1058 else {
1059 console.info("Settings already cached and did not change");
1060 }
1061 }
1062 else {
1063 error("Failed to load settings, calendar entry could not be found");
1064 }
1065 }
1066
1067 function saveSettings()
1068 {
1069 getSettingsCalEntryId();
1070 var item = new Object();
1071 item.Type = "DayEvent";
1072 item.StartTime = new Date(2000, 0, 1);
1073 item.LocalId = settingsCalEntryId;
1074 item.Summary = "ComingNext Settings|" + version + "|";
1075
1076 for (var key in config) {
1077 if (config[key].Type == 'Int')
1078 item.Summary += key + "=" + config[key].Value.toString() + "|";
1079 else if (config[key].Type == 'String')
1080 item.Summary += key + "=" + config[key].Value + "|";
1081 else if (config[key].Type == 'Bool')
1082 item.Summary += key + "=" + (config[key].Value ? 'true' : 'false') + "|";
1083 else if (config[key].Type == 'Enum')
1084 item.Summary += key + "=" + config[key].Value + "|";
1085 else if (config[key].Type == 'UID')
1086 item.Summary += key + "=" + config[key].Value.toString() + "|";
1087 }
1088 settingsCache = item.Summary;
1089
1090 var criteria = new Object();
1091 criteria.Type = "CalendarEntry";
1092 criteria.Item = item;
1093
1094 console.info("Saving settings to calendar entry: " + item.Summary);
1095 try {
1096 var result = calendarService.IDataSource.Add(criteria);
1097 if (result.ErrorCode)
1098 error(result.ErrorMessage);
1099 } catch (e) {
1100 error("saveSettings: " + e + ', line ' + e.line);
1101 }
1102 }
1103
1104 function toggleVisibility(elementId)
1105 {
1106 if (document.getElementById(elementId).style.display == "none")
1107 document.getElementById(elementId).style.display = "block";
1108 else
1109 document.getElementById(elementId).style.display = "none";
1110 }
1111
1112 var uniqueId = 0;
1113 function printHintBox(text)
1114 {
1115 uniqueId++;
1116 return '<td width="1%" align="right" onclick="javascript:toggleVisibility(\'info' + uniqueId + '\')">' + getLocalizedText('settings.help') + '</td></tr></table>'+
1117 '<div class="settingsInfo" id="info' + uniqueId + '">' + text + '</div>';
1118 }
1119
1120 function showAbout()
1121 {
1122 mode = 3;
1123 hideViews();
1124 document.getElementById("aboutView").style.display = "block";
1125 document.onclick = null;
1126
1127 window.menu.setLeftSoftkeyLabel(" ", function(){});
1128 window.menu.setRightSoftkeyLabel(getLocalizedText('softkey.back'), function()
1129 {
1130 mode = 1;
1131 showFullscreen();
1132 });
1133
1134 //document.getElementById("aboutView").innerHTML = 'aboutView';
1135 document.getElementById("name").innerHTML = "Coming Next " + version;
1136 }
1137
1138 function updateFullscreen()
1139 {
1140 }
1141
1142 function showFullscreen()
1143 {
1144 console.info("showFullscreen()");
1145 hideViews();
1146 document.getElementById("fullscreenView").style.display = "block";
1147 document.getElementById('body').className = "backgroundFullscreen";
1148 document.onclick = launchCalendar;
1149 createMenu();
1150 updateData();
1151 }
1152
1153 function getBackgroundImage()
1154 {
1155 var bgImage;
1156 if (config['backgroundImageLocation'].Value == config['backgroundImageLocation'].ValidValues[0]) // internal
1157 bgImage = 'background_' + orientation + '.png';
1158 else
1159 bgImage = 'C:/Data/background_' + panelNum + '_' + orientation + '.png';
1160 return bgImage;
1161 }
1162
1163 function updateHomescreen()
1164 {
1165 if (config['useBackgroundImage'].Value) {
1166 // check for screen rotation
1167 if (orientation != 'portrait' && screen.width == 360 && screen.height == 640) {
1168 window.widget.prepareForTransition("fade");
1169 orientation = 'portrait';
1170 document.getElementById('body').style.backgroundImage = 'url(' + getBackgroundImage() + ')';
1171 document.getElementById('body').style.backgroundColor = 'none';
1172 window.widget.performTransition();
1173 } else if (orientation != 'landscape' && screen.width == 640 && screen.height == 360) {
1174 window.widget.prepareForTransition("fade");
1175 orientation = 'landscape';
1176 document.getElementById('body').style.backgroundImage = 'url(' + getBackgroundImage() + ')';
1177 document.getElementById('body').style.backgroundColor = 'none';
1178 window.widget.performTransition();
1179 }
1180 else if (document.getElementById('body').style.backgroundImage == "")
1181 {
1182 document.getElementById('body').style.backgroundImage = 'url(' + getBackgroundImage() + ')';
1183 }
1184 }
1185 }
1186
1187 function showHomescreen()
1188 {
1189 console.info("showHomescreen()");
1190 hideViews();
1191 document.getElementById("homescreenView").style.display = "block";
1192 document.getElementById('body').className = "background";
1193 document.onclick = null;
1194 updateData();
1195 }
1196
1197 function getLocalizedText(p_Txt)
1198 {
1199 if (localizedText[p_Txt])
1200 return localizedText[p_Txt];
1201 else
1202 return 'ERROR: missing translation for ' + p_Txt;
1203 }
1204
1205 function showUpdate()
1206 {
1207 mode = 4;
1208 hideViews();
1209 document.getElementById("updateView").style.display = "block";
1210 document.onclick = null;
1211
1212 window.menu.setLeftSoftkeyLabel(getLocalizedText('update.checknow'), function(){
1213 checkForUpdate();
1214 });
1215 window.menu.setRightSoftkeyLabel(getLocalizedText('softkey.back'), function()
1216 {
1217 mode = 1;
1218 showFullscreen();
1219 });
1220
1221 document.getElementById("currentVersion").innerHTML = getLocalizedText("update.current") + version;
1222 checkForUpdate();
1223 }
1224
1225 function checkForUpdate()
1226 {
1227 // asynch XHR to server url
1228 reqV = new XMLHttpRequest();
1229 reqV.onreadystatechange = checkForUpdateCallback;
1230 document.getElementById("updateDiv").innerHTML = getLocalizedText("update.checking");
1231 reqV.open("GET", versionURL, true);
1232 reqV.setRequestHeader( "If-Modified-Since", "Sat, 1 Jan 2000 00:00:00 GMT" ); // disable caching
1233 reqV.send(null);
1234 }
1235
1236 function checkForUpdateCallback()
1237 {
1238 if (reqV.readyState == 4) {
1239 if (reqV.status == 200) {
1240 var resultXml = reqV.responseText;
1241 if (resultXml) {
1242 var div = document.getElementById("tmp");
1243 div.innerHTML = resultXml;
1244 var newVersion = div.getElementsByTagName('version')[0].innerHTML;
1245 var newVersionURL = div.getElementsByTagName('url')[0].innerHTML;
1246 div.innerHTML = "";
1247 if (version != newVersion) {
1248 document.getElementById("updateDiv").innerHTML = getLocalizedText("update.download").replace(/%1/, newVersion).replace(/%2/, newVersionURL);
1249 }
1250 else {
1251 document.getElementById("updateDiv").innerHTML = getLocalizedText("update.nonewversion");
1252 }
1253 }
1254 }
1255 else {
1256 document.getElementById("updateDiv").innerHTML = getLocalizedText("update.error") + reqV.status + " " + reqV.responseText;
1257 }
1258 }
1259 }
1260
1261 function hideViews()
1262 {
1263 document.getElementById("homescreenView").style.display = "none";
1264 document.getElementById("fullscreenView").style.display = "none";
1265 document.getElementById("aboutView").style.display = "none";
1266 document.getElementById("settingsView").style.display = "none";
1267 document.getElementById("updateView").style.display = "none";
1268 }
1269
1270 function listCalendars()
1271 {
1272 try {
1273 var criteria = {
1274 Type:'Calendar',
1275 Filter:{
1276 DefaultCalendar: false
1277 }
1278 }
1279
1280 var calendarsResult = calendarService.IDataSource.GetList(criteria);
1281 if (calendarsResult.ErrorCode != 0)
1282 throw("Error fetching list of calendars: " + calendarsResult.ErrorCode + ': ' + calendarsResult.ErrorMessage);
1283 var calendarListIterator = calendarsResult.ReturnValue;
1284
1285 var calendars = [];
1286 var count = 0;
1287 var item;
1288 while (( item = calendarListIterator.getNext()) != undefined ) {
1289 calendars[count++] = item;
1290 }
1291 console.info("Available Calendars: " + calendars.join(", "));
1292 return calendars;
1293 } catch(e) {
1294 error('listing calendars:' + e + ', line ' + e.line);
1295 return null;
1296 }
1297 }
1298
1299 // Copies all objects and their properties to an array. Data is copied so nothing gets lost when the reference is removed
1300 // Note: this will also set the "CalendarName" property of every entry to the name specified by the calendarName parameter if it has not been defined already
1301 function listToArray(list, calendarName)
1302 {
1303 var array = new Array();
1304 var item;
1305 var txt = "";
1306 while (( item = list.getNext()) != undefined ) {
1307 var itemCopy = new Object();
1308 for(var i=0; i < entryFields.length; i++) {
1309 itemCopy[entryFields[i]] = item[entryFields[i]];
1310 }
1311 // for some reason, the CalendarName property is never correctly queried, so we assign it manually here
1312 if (!itemCopy['CalendarName']) {
1313 itemCopy['CalendarName'] = calendarName;
1314 }
1315 array.push(itemCopy);
1316 txt += array[array.length - 1].Summary + ", ";
1317 }
1318 console.info("listToArray(): " + txt);
1319 return array;
1320 }
1321
1322 function sortCalendarEntries(a, b)
1323 {
1324 var atime, btime;
1325 console.info("sortCalendarEntries(" + a.Summary + "," + b.Summary + ")");
1326
1327 if (a.InstanceStartTime != null) {
1328 atime = a.InstanceStartTime;
1329 }
1330 else if (a.StartTime != null) {
1331 atime = a.StartTime;
1332 }
1333 else if (a.InstanceEndTime != null) {
1334 atime = a.InstanceEndTime;
1335 }
1336 else if (a.EndTime != null) {
1337 atime = a.EndTime;
1338 }
1339
1340 if (b.InstanceStartTime != null) {
1341 btime = b.InstanceStartTime;
1342 }
1343 else if (b.StartTime != null) {
1344 btime = b.StartTime;
1345 }
1346 else if (b.InstanceEndTime != null) {
1347 btime = b.InstanceEndTime;
1348 }
1349 else if (b.EndTime != null) {
1350 btime = b.EndTime;
1351 }
1352
1353 if (atime && btime) {
1354
1355 atime = parseDate(atime);
1356 btime = parseDate(btime);
1357
1358 // sort by date & time
1359 if (atime < btime) {
1360 return -1;
1361 }
1362 else if (atime > btime) {
1363 return 1;
1364 }
1365 // sort by type
1366 else if (a.Type != b.Type) {
1367 if (a.Type < b.Type) {
1368 return -1;
1369 }
1370 else if (a.Type > b.Type) {
1371 return 1;
1372 }
1373 }
1374 // sort by description
1375 else if (a.Summary && b.Summary && a.Summary != b.Summary) {
1376 if (a.Summary < b.Summary) {
1377 return -1;
1378 }
1379 else if (a.Summary > b.Summary) {
1380 return 1;
1381 }
1382 }
1383 }
1384
1385 return 0;
1386 }
1387
1388 function updateCalendarColors()
1389 {
1390 var maxColors = 6;
1391 calendarColors = [];
1392 if (calendarList.length > maxColors) {
1393 console.info("updateCalendarColors(): Warning: more calendars than available indicator colors");
1394 }
1395 for(var i=0; i < calendarList.length; i++) {
1396 calendarColors[calendarList[i]] = (i % maxColors) + 1;
1397 }
1398 }
1399
1400 </script>
1401
1402 <style type="text/css">
1403 table { margin:0px; padding:0px; border-spacing:0px; }
1404 td { padding:0px 5px 0px 0px; white-space:nowrap; overflow:hidden; }
1405 hr { color:#ffffff; background-color:#ffffff; height:1px; text-align:left; border-style:none; }
1406 .settingsInfo { display:none; font-style:italic; }
1407 .title { font-weight:bold; font-size:14pt; }
1408 .textInput { width:90%; }
1409 .credits { margin-left:40px; text-indent: -20px; margin-bottom:0px; }
1410 #homescreenView { width: 315px; height:91px; overflow:hidden; }
1411 #calendarList { position:absolute; left:5px; top:4px; width:295px; height:75px; overflow:hidden; }
1412 #name { text-align:center; }
1413 #appicon { display: block; margin-left: auto; margin-right: auto; margin-top: 10px; }
1414 #smallappicon { width:22px; height:22px; margin-right:10px; float:left; }
1415 </style>
1416
1417 </head>
1418
1419 <body id="body" class="background">
1420 <div id="homescreenView">
1421 <div id="calendarList"></div>
1422 </div>
1423 <div id="fullscreenView" style="display:none;">
1424 <img src="Icon.png" id="smallappicon">
1425 <h1 class="title">Coming Next</h1>
1426 <hr />
1427 <div id="fullscreenCalendarList">loading...</div>
1428 </div>
1429 <div id="settingsView" style="display:none">
1430 <img src="Icon.png" id="smallappicon">
1431 <h1 id="settingsTitle" class="title">Settings</h1>
1432 <hr />
1433 <div id="settingsList"></div>
1434 </div>
1435 <div id="aboutView" style="display:none">
1436 <img src="Icon.png" id="appicon">
1437 <h1 id="name">Coming Next</h1>
1438 <hr />
1439 <p>Created by Dr. Cochambre and Michael Prager.</p>
1440 <p>Contributions:</p>
1441 <p class="credits">Paul Moore (bug fixes, new features and code cleanup)</p>
1442 <p class="credits">Manfred Hanselmann (DST support)</p>
1443 <p class="credits">Christophe Milsent (translation support & french translation)</p>
1444 <p class="credits">Flavio Nathan (portuguese-brazilian translation)</p>
1445 <p class="credits">Tokeda (russian translation)</p>
1446 <p>This software is open source and licensed under the GPLv3.</p>
1447 <p>Visit <a href="http://sourceforge.net/projects/comingnext">sourceforge.net/projects/comingnext</a> for free updates.</p>
1448 <hr />
1449 </div>
1450 <div id="updateView" style="display:none">
1451 <img src="Icon.png" id="smallappicon">
1452 <h1 class="title">Check for update</h1>
1453 <hr />
1454 <div id="currentVersion">Coming Next ??</div>
1455 <div id="updateDiv"></div>
1456 <div id="tmp" style="display:none;"></div>
1457 </div>
1458 </body>
1459
1460 </html>