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