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