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