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