]> code.delx.au - comingnext/blob - comingNext/index.html
added new option "hideWidgetOnCalendarOpen" to automatically hide the widget when...
[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 </style>
21
22 <script>
23 // valid types for the config object are 'Int', 'Bool', 'String', 'Enum', 'UID'
24 var config = {
25 monthRange: {
26 Type: 'Int',
27 Default: 2,
28 Value: 2,
29 Name: 'Month Range',
30 Info: 'number of months to include in the event list',},
31 includeTodos: {
32 Type: 'Bool',
33 Default: true,
34 Value: true,
35 Name: 'Include ToDos',
36 Info: 'disable to remove ToDos from event list',},
37 useBackgroundImage: {
38 Type: 'Bool',
39 Default: true,
40 Value: true,
41 Name: 'Use Background Image',
42 Info: 'use background_portrait.png and background_landscape.png to fake transparency. Disable to use a solid background color',},
43 showCombinedDateTime: {
44 Type: 'Bool',
45 Default: false,
46 Value: false,
47 Name: 'Show Combined Date/Time',
48 Info: 'only show the time for events happening today, otherwise just show the date',},
49 showLocation: {
50 Type: 'Bool',
51 Default: true,
52 Value: true,
53 Name: 'Show Location',
54 Info: 'show the location for meeting events',},
55 showTodayAsText: {
56 Type: 'Bool',
57 Default: true,
58 Value: true,
59 Name: 'Show Today as Text',
60 Info: 'if enabled, the current date will be shown as "Today" instead of "31.12"',},
61 todayText: {
62 Type: 'String',
63 Default: 'Today',
64 Value: 'Today',
65 Name: '"Today" Text',
66 Info: 'text to display for "Today"',},
67 tomorrowText: {
68 Type: 'String',
69 Default: 'Tomorrow',
70 Value: 'Tomorrow',
71 Name: '"Tomorrow" Text',
72 Info: 'text to display for "Tomorrow"',},
73 showNowAsText: {
74 Type: 'Bool',
75 Default: true,
76 Value: true,
77 Name: 'Show Now as Text',
78 Info: 'if enabled, the appointment time will be shown as "Now" instead of "12:00"',},
79 nowText: {
80 Type: 'String',
81 Default: 'Now',
82 Value: 'Now',
83 Name: '"Now" Text',
84 Info: 'text to display for "Now"',},
85 dateSeparator: {
86 Type: 'String',
87 Default: '.',
88 Value: '.',
89 Name: 'Date Separator',
90 Info: 'separator for dates. e.g. "31.12" or "31/12"',},
91 dateFormat: {
92 Type: 'Enum',
93 Default: 'auto',
94 Value: 'auto',
95 ValidValues: ['auto', 'DDMM', 'MMDD'],
96 Name: 'Date Format',
97 Info: 'how dates will be displayed. \'auto\' will autodetect your phone\'s date format setting. \'MMDD\' will write month first, \'DDMM\' will write day first',},
98 weekDayLength: {
99 Type: 'Int',
100 Default: 2,
101 Value: 2,
102 Name: 'Weekday Length',
103 Info: 'defines how many characters of the weekday will be shown. E.g. 2 will cut "Friday" to "Fr"',},
104 updateDataInterval: {
105 Type: 'Int',
106 Default: 5,
107 Value: 5,
108 Name: 'Update Data Interval',
109 Info: 'how many minutes to wait before updating the displayed data. The higher the number, the less battery is used',},
110 calendarApp: {
111 Type: 'UID',
112 Default: 0x10005901,
113 Value: 0x10005901,
114 Name: 'Calendar Application To Run',
115 Info: 'UID of the calendar app to run when clicking the widget. 0x10005901 = buildin calendar, 0x20004ec1 = Epocware Handy Calendar',},
116 eventsPerWidget: {
117 Type: 'Int',
118 Default: 4,
119 Value: 4,
120 Name: 'Events Per Widget',
121 Info: 'number of events to show per widget. Default is 4',},
122 showNothingText: {
123 Type: 'Bool',
124 Default: true,
125 Value: true,
126 Name: 'Show "Nothing" Text',
127 Info: 'if enabled, show a text if no events are in the list',},
128 nothingText: {
129 Type: 'String',
130 Default: 'No further events within 2 months',
131 Value: 'No further events within 2 months',
132 Name: '"No further events..." Text',
133 Info: 'text to show when no events are in the list',},
134 enableDaylightSaving: {
135 Type: 'Bool',
136 Default: true,
137 Value: true,
138 Name: 'Enable Daylight Saving (+1h)',
139 Info: 'enable this if you are in a timezone that has daylight saving time (+1h)',},
140 hideWidgetOnCalendarOpen: {
141 Type: 'Bool',
142 Default: false,
143 Value: false,
144 Name: 'Hide Widget When Opening Calendar',
145 Info: 'enable this if the fullscreen widget should be hidden when clicking on the widget to open the calendar app',},
146 cssStyle_background: {
147 Type: 'String',
148 Default: 'color:#ffffff; background-color:#000000',
149 Value: 'color:#ffffff; background-color:#000000',
150 Name: '.background',
151 Info: 'Defines the background of the widget. If you want to use a background image, set useBackgroundImage = true below. For the default themes, black, gray, and light blue, codes are #292029, #e7dfe7, #009aef',},
152 cssStyle_backgroundFullscreen: {
153 Type: 'String',
154 Default: 'color:#ffffff; background-color:#000000',
155 Value: 'color:#ffffff; background-color:#000000',
156 Name: '.backgroundFullscreen',
157 Info: 'Same as background but for the fullscreen version of the widget',},
158 cssStyle_weekDay: {
159 Type: 'String',
160 Default: '',
161 Value: '',
162 Name: '.weekDay',
163 Info: 'Defines the appearance of all week day texts',},
164 cssStyle_date: {
165 Type: 'String',
166 Default: '',
167 Value: '',
168 Name: '.date',
169 Info: 'Defines the appearance of all date texts',},
170 cssStyle_today: {
171 Type: 'String',
172 Default: 'color:#ff0000',
173 Value: 'color:#ff0000',
174 Name: '.today',
175 Info: 'Defines the appearance of "Today" text',},
176 cssStyle_tomorrow: {
177 Type: 'String',
178 Default: 'color:#0000ff',
179 Value: 'color:#0000ff',
180 Name: '.tomorrow',
181 Info: 'Defines the appearance of "Tomorrow" text',},
182 cssStyle_time: {
183 Type: 'String',
184 Default: '',
185 Value: '',
186 Name: '.time',
187 Info: 'Defines the appearance of all time texts',},
188 cssStyle_now: {
189 Type: 'String',
190 Default: 'color:#ff00ff',
191 Value: 'color:#ff00ff',
192 Name: '.now',
193 Info: 'Defines the appearance of "Now" text',},
194 cssStyle_description: {
195 Type: 'String',
196 Default: '',
197 Value: '',
198 Name: '.description',
199 Info: 'Defines the appearance of all event descriptions',},
200 cssStyle_icon: {
201 Type: 'String',
202 Default: 'width:15px; height:15px',
203 Value: 'width:15px; height:15px',
204 Name: '.icon',
205 Info: 'Defines size and appearance of icons',},
206 }
207
208 //-------------------------------------------------------
209 // Nothing of interest from here on...
210 //-------------------------------------------------------
211 var panelNum = 0; // use 1 for second panel
212 var version = "1.23";
213 var calendarService = null;
214 var cacheEntriesHtml = [];
215 var months_translated = [];
216 var orientation = '';
217 var now = new Date();
218 var mode = 0; // 0 = homescreen, 1 = fullscreen, 2 = settings, 3 = about
219
220 // vars for daylight saving time
221 var daylightsavingWinter = 0;
222 var daylightsavingSummer = 0;
223 var summertime = false;
224
225 window.onload = init;
226 window.onresize = updateScreen;
227 window.onshow = updateScreen;
228
229 function isLeapYear( year ) {
230 if (( year % 4 == 0 && year % 100 != 0 ) || year % 400 == 0 )
231 return true;
232 else
233 return false;
234 }
235
236 function calcLeapYear(year, days)
237 {
238 if (isLeapYear(year))
239 return ++days;
240 else
241 return days;
242 }
243
244 function subToSunday(myDate, year, days, prevMonthDays)
245 {
246 for (i = myDate.getDay(); i > 0 ;i--)
247 days--;
248 days -= prevMonthDays;
249 days = isLeapYear(year) ? --days : days;
250 return days;
251 }
252
253 function calcDaylightSaving()
254 {
255 var thisYearS = new Date(now.getFullYear(), 3, 0, 0, 0, 0 );
256 var thisYearW = new Date(now.getFullYear(), 10, 0, 0, 0, 0 );
257 var nextYearS = new Date(now.getFullYear() + 1, 3, 0, 0, 0, 0 );
258 var nextYearW = new Date(now.getFullYear() + 1, 10, 0, 0, 0, 0 );
259 var summer = false;
260 var winter = false;
261
262 thisYearSDays = nextYearSDays = 90;
263 thisYearWDays = nextYearWDays = 304;
264
265 thisYearSDays = calcLeapYear(now.getFullYear(), thisYearSDays);
266 thisYearWDays = calcLeapYear(now.getFullYear(), thisYearWDays);
267 nextYearSDays = calcLeapYear(now.getFullYear() + 1, nextYearSDays);
268 nextYearWDays = calcLeapYear(now.getFullYear() + 1, nextYearWDays);
269
270 thisYearSDays = subToSunday(thisYearS, now.getFullYear(), thisYearSDays, 59);
271 thisYearWDays = subToSunday(thisYearW, now.getFullYear(), thisYearWDays, 273);
272 nextYearSDays = subToSunday(nextYearS, now.getFullYear() + 1, nextYearSDays, 59);
273 nextYearWDays = subToSunday(nextYearW, now.getFullYear() + 1, nextYearWDays, 273);
274
275 daylightsavingSummer = new Date (now.getFullYear(), 03-1, thisYearSDays, 2, 0, 0);
276 daylightsavingWinter = new Date (now.getFullYear(), 10-1, thisYearWDays, 2, 0, 0);
277 if (daylightsavingSummer < now) {
278 daylightsavingSummer = new Date (now.getFullYear()+1, 03-1, nextYearSDays, 2, 0, 0);
279 var summer = true;
280 }
281 if (daylightsavingWinter < now) {
282 daylightsavingWinter = new Date (now.getFullYear()+1, 10-1, nextYearWDays, 2, 0, 0);
283 var winter = true;
284 }
285 if (summer && !winter)
286 summertime = true;
287 else
288 summertime = false;
289 }
290
291 function error(message)
292 {
293 console.info('Error: ' + message);
294 document.getElementById("calendarList").innerHTML = 'Error: ' + message;
295 }
296
297 function isToday(date)
298 {
299 if (date.getDate() == now.getDate() && date.getMonth() == now.getMonth())
300 return true;
301 return false;
302 }
303
304 function isTomorrow(date)
305 {
306 if ((date.getDate() == now.getDate() + 1 && date.getMonth() == now.getMonth()) ||
307 (date.getDate() == 0 && date.getMonth() == now.getMonth() + 1) ||
308 (date.getDate() == 0 && date.getMonth() == now.getMonth() + 1 && date.getYear() == now.getYear() + 1))
309 return true;
310 return false;
311 }
312
313 function collectLocales()
314 {
315 var tmpyear = ((panelNum == 0) ? 2000 : 2001);
316 var month = 0;
317
318 if (months_translated.length > 0)
319 return;
320 for (month = 0; month < 12; month++) {
321 var startDate = new Date(tmpyear, month, 15);
322
323 var item = new Object();
324 item.Type = "DayEvent";
325 item.StartTime = startDate;
326 item.Summary = "__temp" + month;
327
328 var criteria = new Object();
329 criteria.Type = "CalendarEntry";
330 criteria.Item = item;
331
332 try {
333 var result = calendarService.IDataSource.Add(criteria);
334 if (result.ErrorCode)
335 error(result.ErrorMessage);
336 } catch (e) {
337 error("collectLocales: " + e + ', line ' + e.line);
338 }
339 }
340 try {
341 var startTime = new Date(tmpyear,0,1);
342 var endTime = new Date(tmpyear,11,31);
343 var listFiltering = {
344 Type:'CalendarEntry',
345 Filter:{
346 StartRange: startTime,
347 EndRange: endTime,
348 SearchText: '__temp',
349 Type: 'DayEvent'
350 }
351 }
352 var result = calendarService.IDataSource.GetList(listFiltering);
353 if (result.ErrorCode) {
354 error(result.ErrorMessage);
355 return;
356 }
357 var list = result.ReturnValue;
358 } catch(e) {
359 error(e + ', line ' + e.line);
360 return;
361 }
362 var ids = new Array();
363 try {
364 var entry;
365 var counter = 0;
366 var dateArr = [];
367
368 while (list && (entry = list.getNext()) != undefined) {
369 dateArr = entry.StartTime.replace(/,/g,'').replace(/\./g,':').replace(/ /g,' ').split(' ');
370 var day = dateArr[1];
371 var month = dateArr[2];
372 var year = dateArr[3];
373
374 // make sure month is set properly
375 if (isNaN(parseInt(day))) {
376 var tmp = day;
377 day = month;
378 month = tmp;
379 } else if (isNaN(parseInt(year))) {
380 var tmp = year;
381 year = month;
382 month = tmp;
383 }
384
385 console.info(entry.StartTime + ' -> ' + month + ' ' + counter);
386 ids[counter] = entry.id;
387 months_translated[month] = counter + 1;
388 counter++;
389 }
390 } catch(e) {
391 error(e + ', line ' + e.line);
392 return;
393 }
394 console.info(ids);
395 try {
396 var criteria = new Object();
397 criteria.Type = "CalendarEntry";
398 criteria.Data = {
399 IdList: ids
400 }
401
402 var result = calendarService.IDataSource.Delete(criteria);
403 if (result.ErrorCode)
404 error(result.ErrorMessage);
405 } catch(e) {
406 error('deleting temp calendar entries:' + e + ', line ' + e.line);
407 return;
408 }
409 }
410
411 function requestNotification()
412 {
413 var criteria = new Object();
414 criteria.Type = "CalendarEntry";
415
416 try {
417 var result = calendarService.IDataSource.RequestNotification(criteria, callback);
418 if (result.ErrorCode)
419 error('loading Calendar items list');
420 } catch (e) {
421 error("requestNotification: " + e + ', line ' + e.line);
422 }
423 }
424
425 function callback(transId, eventCode, result)
426 {
427 updateData();
428 }
429
430 function parseDate(dateString)
431 {
432 /*
433 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:
434 Wednesday, 26 August, 2009 24:00:00
435 Wednesday, 26 August, 2009 12:00:00 am
436 Wednesday, August 26, 2009 12:00:00 am
437 Wednesday, 2009 August, 26 12:00:00 am
438 Wednesday, 2009 August, 28 8.00.00 pm
439 Wednesday, 2009 August, 28 08:00:00 PM
440 */
441
442 if (dateString == "" || dateString == null)
443 return null;
444 var dateArr = dateString.replace(/,/g,'').replace(/\./g,':').replace(/ /g,' ').split(' ');
445 if (dateArr.length != 5 && dateArr.length != 6)
446 return null;
447
448 // parse date
449 var weekDay = dateArr[0];
450 var day = dateArr[1];
451 var month = dateArr[2];
452 var year = dateArr[3];
453 // make sure month is set properly
454 if (isNaN(parseInt(day))) {
455 var tmp = day;
456 day = month;
457 month = tmp;
458 } else if (isNaN(parseInt(year))) {
459 var tmp = year;
460 year = month;
461 month = tmp;
462 }
463 // make sure day and year are set properly
464 if (Number(day) > Number(year)) {
465 var tmp = year;
466 year = day;
467 day = tmp;
468 }
469 month = months_translated[month];
470
471 // parse time
472 var timeArr = dateArr[4].split(':');
473 if (timeArr.length != 3)
474 return null;
475 var hours = Number(timeArr[0]);
476 var minutes = Number(timeArr[1]);
477 var seconds = Number(timeArr[2]);
478 if (dateArr.length == 6 && dateArr[5].toLowerCase() == 'pm' && hours < 12)
479 hours += 12;
480 if (dateArr.length == 6 && dateArr[5].toLowerCase() == 'am' && hours == 12)
481 hours = 0;
482
483 console.info('year=' + year + ' month=' + month + ' day=' + day + ' hours=' + hours + ' minutes=' + minutes+ ' seconds=' + seconds);
484
485 // take care of daylight saving time
486 if (config['enableDaylightSaving'].Value) {
487 var date = new Date(year, month - 1, day, hours, minutes, seconds);
488 if (summertime && date > daylightsavingWinter && date < daylightsavingSummer)
489 hours -= 1;
490 else if (!summertime && date > daylightsavingSummer && date < daylightsavingWinter)
491 hours += 1;
492 }
493
494 return new Date(year, month - 1, day, hours, minutes, seconds);
495 }
496
497 // 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"
498 function formatDate(date, format)
499 {
500 var day = date.getDate().toString();
501 var month = (date.getMonth() + 1).toString();
502 while (day.length < 2) { day = '0' + day; }
503 while (month.length < 2) { month = '0' + month; }
504
505 if (config['showTodayAsText'].Value && isToday(date))
506 return '<span class="today">' + config['todayText'].Value + '</span>';
507 if (config['showTodayAsText'].Value && isTomorrow(date))
508 return '<span class="tomorrow">' + config['tomorrowText'].Value + '</span>';
509
510 var dateArr = format.replace(/,/g,'').replace(/\./g,':').replace(/ /g,' ').split(' ');
511 if (dateArr.length != 5 && dateArr.length != 6) {
512 // we don't know how to format this
513 if (config['dateFormat'].Value == 'auto' || config['dateFormat'].Value == 'DDMM')
514 return day + config['dateSeparator'].Value + month;
515 else
516 return month + config['dateSeparator'].Value + day;
517 }
518
519 var dayFirst = true;
520 if (config['dateFormat'].Value == 'MMDD')
521 dayFirst = false;
522 else if (config['dateFormat'].Value == 'DDMM')
523 dayFirst = true;
524 else {
525 // config['dateFormat'].Value == 'auto', try to detect system setting
526 // parse date
527 var day_ = dateArr[1];
528 var month_ = dateArr[2];
529 var year_ = dateArr[3];
530 // make sure month is set properly
531 if (isNaN(parseInt(day_))) {
532 var tmp = day_;
533 day_ = month_;
534 month_ = tmp;
535 dayFirst = false;
536 } else if (isNaN(parseInt(year_))) {
537 var tmp = year_;
538 year_ = month_;
539 month_ = tmp;
540 dayFirst = true;
541 }
542 // make sure day and year are set properly
543 if (Number(day_) > Number(year_))
544 dayFirst = false;
545 }
546
547 if (dayFirst)
548 return day + config['dateSeparator'].Value + month;
549 else
550 return month + config['dateSeparator'].Value + day;
551 }
552
553 function formatTime(date)
554 {
555 // date is a Date() object
556 date.setSeconds(0); // we don't care about seconds
557 var time = date.toLocaleTimeString().replace(/[\.:]00/, ''); // remove seconds from string
558 if (time.replace(/\./, ':').split(':')[0].length < 2)
559 time = '0' + time;
560 if (config['showNowAsText'].Value && date.getTime() == now.getTime())
561 time = '<span class="now">' + config['nowText'].Value + '</span>';
562 return time;
563 }
564
565 function updateData()
566 {
567 calcDaylightSaving();
568 try {
569 // meetings have time
570 // 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
571 now = new Date();
572 var meetingListFiltering = {
573 Type:'CalendarEntry',
574 Filter:{
575 StartRange: (new Date(now.getFullYear(), now.getMonth(), now.getDate(), 0, 0, 0)),
576 EndRange: (new Date(now.getFullYear(), now.getMonth() + config['monthRange'].Value, now.getDate(), 0, 0, 0))
577 }
578 }
579 var meetingResult = calendarService.IDataSource.GetList(meetingListFiltering);
580 var meetingList = meetingResult.ReturnValue;
581
582 // todos don't, they start on 00:00 hrs., but should be visible anyway
583 // this will generate a list of passed todos. We have to check if they have been marked as "done" yet
584 if (config['includeTodos'].Value) {
585 var todayTodoListFiltering = {
586 Type:'CalendarEntry',
587 Filter:{
588 Type: 'ToDo',
589 StartRange: (new Date(now.getFullYear() - 1, now.getMonth(), now.getDate(), 0, 0, 0)),
590 EndRange: (new Date(now.getFullYear(), now.getMonth(), now.getDate(), 0, 0, 1))
591 }
592 }
593 var todayTodoResult = calendarService.IDataSource.GetList(todayTodoListFiltering);
594 var todayTodoList = todayTodoResult.ReturnValue;
595 var entryLists = [todayTodoList, meetingList];
596 } else {
597 var entryLists = [meetingList];
598 }
599 } catch(e) {
600 error('loading Calendar items list:' + e + ', line ' + e.line);
601 return;
602 }
603
604 try {
605 var entry;
606 var counter = 0;
607 var entryDate = '';
608 var dateArr = [];
609 var entriesHtml = '<table>';
610 var eventIds = [];
611 var max;
612 if (mode == 0)
613 max = ((panelNum == 0) ? config['eventsPerWidget'].Value : 2 * config['eventsPerWidget'].Value);
614 else
615 max = 30; // we can display a lot more events in fullscreen mode
616
617 // the first outer loop iteration is for passed ToDos, the second loop is for all upcomming events (may also include ToDos)
618 for (var i=0; counter < max && i < entryLists.length; i++) {
619 while (counter < max && (entry = entryLists[i].getNext()) != undefined) {
620 counter++;
621
622 // output event info for debugging
623 console.info(
624 'event: Id=' + entry.id +
625 ',Type=' + entry.Type +
626 ',Summary=' + entry.Summary +
627 ',Location=' + entry.Location +
628 ',Status=' + entry.Status +
629 ',StartTime=' + entry.StartTime +
630 ',EndTime=' + entry.EndTime +
631 ',InstanceStartTime=' + entry.InstanceStartTime +
632 ',InstanceEndTime=' + entry.InstanceEndTime
633 );
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) {
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 == 1 && counter < config['eventsPerWidget'].Value + 1) {
716 console.info('skipping (already in first widget) ' + entry.id);
717 continue;
718 }
719
720 // generate html output
721 entriesHtml += '<tr><td><img class="icon" src="' + entry.Type + '.png" /></td>';
722 if(date == null) {
723 // some languages have very strange locale date formats, can't parse all those. Also some todos don't have dates at all.
724 entriesHtml += '<td colspan="4"><span class="date">' + entryDate + '</span> ';
725 } else {
726 var weekDay = date.toLocaleDateString().substr(0,config['weekDayLength'].Value);
727 var time = formatTime(date);
728 var dateStr = formatDate(date, entryDate);
729 if (entry.Type == 'ToDo' || entry.Type == 'Anniversary' || entry.Type == 'DayEvent' || entry.Type == 'Reminder') {
730 if ((isToday(date) || isTomorrow(date)) && config['showTodayAsText'].Value) // show weekday if the date string is not text. looks odd otherwise
731 entriesHtml += '<td colspan="4" width="1px"><span class="date">' + dateStr + '</span> ';
732 else
733 entriesHtml += '<td class="weekDay" width="1px">' + weekDay + '</td><td width="1px" class="date">' + dateStr + '</td><td colspan="2">';
734 } else if (entry.Type == 'Meeting') {
735 if (config['showCombinedDateTime'].Value) {
736 if (isToday(date))
737 entriesHtml += '<td width="1px" colspan="4"><span class="today">' + time + '</span> ';
738 else if (isTomorrow(date))
739 entriesHtml += '<td width="1px" colspan="4"><span class="tomorrow">' + dateStr + '</span> <span class="time">' + time + '</span> ';
740 else
741 entriesHtml += '<td width="1px" class="weekDay">' + weekDay + '</td><td width="1px" class="date">' + dateStr + '</td><td colspan="2">';
742 } else {
743 if ((isToday(date) || isTomorrow(date)) && config['showTodayAsText'].Value)
744 entriesHtml += '<td colspan="4" width="1px"><span class="today">' + dateStr + '</span> <span class="time">' + time + '</span> ';
745 else
746 entriesHtml += '<td width="1px" class="weekDay">' + weekDay + '</td><td width="1px" class="date">' + dateStr + '</td><td width="1px" class="time">' + time + '</td><td>';
747 }
748 }
749 }
750 entriesHtml += '<span class="description">' + Summary + '</span></td></tr>';
751 }
752 }
753 entriesHtml += '</table>';
754 if (config['showNothingText'].Value && entriesHtml == '<table></table>')
755 entriesHtml = '<div style="width:295px; height:75px; text-align:center; line-height:75px; overflow:visible;">' + config['nothingText'].Value + '</div>';
756 if (cacheEntriesHtml != entriesHtml) {
757 if (mode == 0)
758 document.getElementById('calendarList').innerHTML = entriesHtml;
759 else
760 document.getElementById('fullscreenCalendarList').innerHTML = entriesHtml;
761 cacheEntriesHtml = entriesHtml;
762 }
763 } catch(e) {
764 error('displaying list:' + e + ', line ' + e.line);
765 return;
766 }
767 }
768
769 function updateScreen()
770 {
771 // check if opening fullscreen
772 if( window.innerHeight > 91 && mode == 0) {
773 mode = 1;
774 cacheEntriesHtml = '';
775 document.getElementById('body').style.backgroundImage = "";
776 showFullscreen();
777 }
778 else if (window.innerHeight <= 91 && mode != 0) {
779 mode = 0;
780 cacheEntriesHtml = '';
781 showHomescreen();
782 }
783
784 if (mode == 0)
785 updateHomescreen();
786 else if (mode == 1)
787 updateFullscreen();
788 }
789
790 function launchCalendar()
791 {
792 try {
793 widget.openApplication(config['calendarApp'].Value, "");
794 if (config['hideWidgetOnCalendarOpen'].Value)
795 window.close();
796 } catch(e) {
797 error('starting Calendar App');
798 return;
799 }
800 }
801
802 function init()
803 {
804 try {
805 // call calendar service
806 calendarService = device.getServiceObject("Service.Calendar", "IDataSource");
807 } catch(e) {
808 error('loading Calendar service');
809 return;
810 }
811
812 loadSettings();
813 updateCssClasses();
814 collectLocales();
815 //updateData();
816 requestNotification();
817 window.setInterval('updateData()', 1000 * 60 * config['updateDataInterval'].Value);
818
819 mode = 0;
820 showHomescreen();
821 updateScreen();
822 if (config['useBackgroundImage'].Value)
823 // check for screen rotation every 1 secs
824 window.setInterval('updateScreen()', 1000 * 1);
825 }
826
827 function createMenu()
828 {
829 window.menu.setLeftSoftkeyLabel("",null);
830 window.menu.setRightSoftkeyLabel("",null);
831 var id = 0;
832 var menuSettings = new MenuItem("Settings", id++);
833 var menuCallApp = new MenuItem("Open Calendar App", id++);
834 var menuAbout = new MenuItem("About", id++);
835 menuSettings.onSelect = showSettings;
836 menuAbout.onSelect = showAbout;
837 menuCallApp.onSelect = launchCalendar;
838 window.menu.clear();
839 window.menu.append(menuCallApp);
840 window.menu.append(menuSettings);
841 window.menu.append(menuAbout);
842 }
843
844 function showSettings()
845 {
846 mode = 2;
847 document.getElementById("homescreenView").style.display = "none";
848 document.getElementById("fullscreenView").style.display = "none";
849 document.getElementById("aboutView").style.display = "none";
850 document.getElementById("settingsView").style.display = "block";
851 document.onclick = null;
852
853 window.menu.setLeftSoftkeyLabel("Save", function()
854 {
855 for (var key in config) {
856 if (config[key].Type == 'String')
857 config[key].Value = document.forms[0].elements["settings." + key].value;
858 else if (config[key].Type == 'Int') {
859 config[key].Value = parseInt(document.forms[0].elements["settings." + key].value);
860 if (config[key].Value < 0)
861 config[key].Value = config[key].Default;
862 }
863 else if (config[key].Type == 'Bool')
864 config[key].Value = document.forms[0].elements["settings." + key].checked;
865 else if (config[key].Type == 'UID')
866 config[key].Value = parseInt(document.forms[0].elements["settings." + key].value);
867 else if (config[key].Type == 'Enum') {
868 config[key].Value = document.forms[0].elements["settings." + key].value;
869 if (config[key].ValidValues.indexOf(config[key].Value) == -1)
870 config[key].Value = config[key].Default;
871 }
872 }
873
874 updateCssClasses();
875
876 saveSettings();
877
878 mode = 1;
879 showFullscreen();
880 });
881 window.menu.setRightSoftkeyLabel("Cancel", function()
882 {
883 mode = 1;
884 showFullscreen();
885 });
886
887 var settingsHtml = '<form>';
888 for (var key in config) {
889 if (config[key].Type == 'String')
890 settingsHtml += '<table><tr><td>' + config[key].Name + '<br /><input class="textInput" name="settings.' + key + '" type="text" value="' + config[key].Value + '" /></td>' + printHintBox(config[key].Info) + '<hr />';
891 else if (config[key].Type == 'Int')
892 settingsHtml += '<table><tr><td>' + config[key].Name + '<br /><input class="textInput" name="settings.' + key + '" type="text" value="' + config[key].Value + '" /></td>' + printHintBox(config[key].Info) + '<hr />';
893 else if (config[key].Type == 'Bool')
894 settingsHtml += '<table><tr><td>' + config[key].Name + '<br /><input name="settings.' + key + '" type="checkbox" value="true" ' + (config[key].Value ? 'checked="checked"' : '') + '/></td>' + printHintBox(config[key].Info) + '<hr />';
895 else if (config[key].Type == 'UID')
896 settingsHtml += '<table><tr><td>' + config[key].Name + '<br /><input class="textInput" name="settings.' + key + '" type="text" value="0x' + config[key].Value.toString(16) + '" /></td>' + printHintBox(config[key].Info) + '<hr />';
897 else if (config[key].Type == 'Enum') {
898 settingsHtml += '<table><tr><td>' + config[key].Name + '<br /><select name="settings.' + key + '" size="1">';
899 for(var i = 0; i < config[key].ValidValues.length; i++)
900 settingsHtml += '<option label="' + config[key].ValidValues[i] + '"' + (config[key].Value == config[key].ValidValues[i] ? ' selected="selected"' : '') + '>' + config[key].ValidValues[i] + '</option>';
901 settingsHtml += '</select></div></td>' + printHintBox(config[key].Info) + '<hr />';
902 }
903 }
904 settingsHtml += '<input name="reset" type="button" value="Restore Defaults" onclick="javascript:restoreDefaultSettings();showSettings();" />';
905 settingsHtml += '</form>';
906 document.getElementById("settingsList").innerHTML = settingsHtml;
907 }
908
909 function changeCssClass(classname, properties)
910 {
911 for(var i = 0; i < document.styleSheets[0]['cssRules'].length; i++)
912 {
913 if (document.styleSheets[0]['cssRules'][i].selectorText == classname) {
914 document.styleSheets[0].deleteRule(i);
915 document.styleSheets[0].insertRule(classname + ' { ' + properties + ' }', document.styleSheets[0]['cssRules'].length);
916 break;
917 }
918 }
919 }
920
921 function updateCssClasses()
922 {
923 for(var key in config) {
924 changeCssClass(config[key].Name, config[key].Value);
925 }
926 }
927
928 function restoreDefaultSettings()
929 {
930 for (var key in config)
931 config[key].Value = config[key].Default;
932 }
933
934 function loadSettings()
935 {
936 for (var key in config) {
937 if (widget.preferenceForKey(key)) {
938 if (config[key].Type == 'Int')
939 config[key].Value = Number(widget.preferenceForKey(key));
940 else if (config[key].Type == 'String')
941 config[key].Value = widget.preferenceForKey(key);
942 else if (config[key].Type == 'Bool')
943 config[key].Value = (widget.preferenceForKey(key) == 'true')
944 else if (config[key].Type == 'Enum')
945 config[key].Value = widget.preferenceForKey(key);
946 else if (config[key].Type == 'UID')
947 config[key].Value = Number(widget.preferenceForKey(key));
948 }
949 else
950 config[key].Value = config[key].Default;
951
952 }
953 }
954
955 function saveSettings()
956 {
957 for (var key in config) {
958 if (config[key].Type == 'Int')
959 widget.setPreferenceForKey(config[key].Value.toString(), key);
960 else if (config[key].Type == 'String')
961 widget.setPreferenceForKey(config[key].Value, key);
962 else if (config[key].Type == 'Bool')
963 widget.setPreferenceForKey(config[key].Value ? 'true' : 'false', key);
964 else if (config[key].Type == 'Enum')
965 widget.setPreferenceForKey(config[key].Value, key);
966 else if (config[key].Type == 'UID')
967 widget.setPreferenceForKey(config[key].Value.toString(), key);
968 }
969 }
970
971 function toggleVisibility(elementId)
972 {
973 if (document.getElementById(elementId).style.display == "none")
974 document.getElementById(elementId).style.display = "block";
975 else
976 document.getElementById(elementId).style.display = "none";
977 }
978
979 var uniqueId = 0;
980 function printHintBox(text)
981 {
982 uniqueId++;
983 return '<td width="1%" align="right" onclick="javascript:toggleVisibility(\'info' + uniqueId + '\')">Help</td></tr></table>'+
984 '<div class="settingsInfo" id="info' + uniqueId + '">' + text + '</div>';
985 }
986
987 function showAbout()
988 {
989 mode = 3;
990 document.getElementById("homescreenView").style.display = "none";
991 document.getElementById("fullscreenView").style.display = "none";
992 document.getElementById("aboutView").style.display = "block";
993 document.getElementById("settingsView").style.display = "none";
994 document.onclick = null;
995
996 window.menu.setLeftSoftkeyLabel(" ", function(){});
997 window.menu.setRightSoftkeyLabel("Back", function()
998 {
999 mode = 1;
1000 showFullscreen();
1001 });
1002
1003 //document.getElementById("aboutView").innerHTML = 'aboutView';
1004 document.getElementById("name").innerHTML = "Coming Next " + version;
1005 }
1006
1007 function updateFullscreen()
1008 {
1009 }
1010
1011 function showFullscreen()
1012 {
1013 document.getElementById("homescreenView").style.display = "none";
1014 document.getElementById("fullscreenView").style.display = "block";
1015 document.getElementById("aboutView").style.display = "none";
1016 document.getElementById("settingsView").style.display = "none";
1017 document.getElementById('body').className = "backgroundFullscreen";
1018 document.onclick = launchCalendar;
1019 createMenu();
1020 updateData();
1021 }
1022
1023 function updateHomescreen()
1024 {
1025 if (config['useBackgroundImage'].Value) {
1026 // check for screen rotation
1027 if (orientation != 'portrait' && screen.width == 360 && screen.height == 640) {
1028 window.widget.prepareForTransition("fade");
1029 orientation = 'portrait';
1030 document.getElementById('body').style.backgroundImage = 'url(background_' + orientation + '.png)';
1031 document.getElementById('body').style.backgroundColor = 'none';
1032 window.widget.performTransition();
1033 } else if (orientation != 'landscape' && screen.width == 640 && screen.height == 360) {
1034 window.widget.prepareForTransition("fade");
1035 orientation = 'landscape';
1036 document.getElementById('body').style.backgroundImage = 'url(background_' + orientation + '.png)';
1037 document.getElementById('body').style.backgroundColor = 'none';
1038 window.widget.performTransition();
1039 }
1040 else if (document.getElementById('body').style.backgroundImage == "")
1041 {
1042 document.getElementById('body').style.backgroundImage = 'url(background_' + orientation + '.png)';
1043 }
1044 }
1045 }
1046
1047 function showHomescreen()
1048 {
1049 document.getElementById("homescreenView").style.display = "block";
1050 document.getElementById("fullscreenView").style.display = "none";
1051 document.getElementById("aboutView").style.display = "none";
1052 document.getElementById("settingsView").style.display = "none";
1053 document.getElementById('body').className = "background";
1054 document.onclick = null;
1055 updateData();
1056 }
1057 </script>
1058
1059 <style type="text/css">
1060 table { margin:0px; padding:0px; border-spacing:0px; }
1061 td { padding:0px 5px 0px 0px; white-space:nowrap; overflow:hidden; }
1062 hr { color:#ffffff; background-color:#ffffff; height:1px; text-align:left; border-style:none; }
1063 .settingsInfo { display:none; font-style:italic; }
1064 .title { font-weight:bold; font-size:14pt; }
1065 .textInput { width:90%; }
1066 #homescreenView { width: 315px; height:91px; overflow:hidden; }
1067 #calendarList { position:absolute; left:10px; top:4px; width:295px; height:75px; overflow:hidden; }
1068 #name { text-align:center; }
1069 #appicon { display: block; margin-left: auto; margin-right: auto; margin-top: 50px; }
1070 #smallappicon { width:22px; height:22px; margin-right:10px; float:left; }
1071 </style>
1072
1073 </head>
1074
1075 <body id="body" class="background">
1076 <div id="homescreenView">
1077 <div id="calendarList"></div>
1078 </div>
1079 <div id="fullscreenView" style="display:none;">
1080 <img src="Icon.png" id="smallappicon">
1081 <h1 class="title">Coming Next</h1>
1082 <hr />
1083 <div id="fullscreenCalendarList">loading...</div>
1084 </div>
1085 <div id="settingsView" style="display:none">
1086 <img src="Icon.png" id="smallappicon">
1087 <h1 class="title">Settings</h1>
1088 <hr />
1089 <div id="settingsList"></div>
1090 </div>
1091 <div id="aboutView" style="display:none">
1092 <img src="Icon.png" id="appicon">
1093 <h1 id="name">Coming Next</h1>
1094 <hr />
1095 <p>Created by Dr. Cochambre and Michael Prager.</p>
1096 <p>This software is open source and licensed under the GPLv3.</p>
1097 <p>Visit https://sourceforge.net/projects/comingnext/ for free updates.</p>
1098 <hr />
1099 </div>
1100 </body>
1101
1102 </html>