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