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