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