From 0a367cbe29a79f04577705d6a474e65b3e0db8a4 Mon Sep 17 00:00:00 2001 From: Thomas Date: Wed, 7 Sep 2011 17:40:20 +0200 Subject: [PATCH] View: 3.4: Fish-Eye View For Busy Days --- plugins/calendar/calendar_ui.js | 180 +++++++++++++++----- plugins/calendar/lib/fullcalendar-rc.patch | 38 +++-- plugins/calendar/lib/js/fullcalendar.js | 2 + plugins/calendar/localization/de_CH.inc | 2 +- plugins/calendar/localization/de_DE.inc | 2 +- plugins/calendar/localization/en_US.inc | 2 +- plugins/calendar/skins/default/calendar.css | 7 + 7 files changed, 181 insertions(+), 52 deletions(-) diff --git a/plugins/calendar/calendar_ui.js b/plugins/calendar/calendar_ui.js index e44d618d..a3c63c41 100644 --- a/plugins/calendar/calendar_ui.js +++ b/plugins/calendar/calendar_ui.js @@ -25,7 +25,6 @@ function rcube_calendar_ui(settings) this.selected_event = null; this.selected_calendar = null; this.search_request = null; - this.eventcount = []; this.saving_lock = null; @@ -251,7 +250,7 @@ function rcube_calendar_ui(settings) // event details dialog (show only) var event_show_dialog = function(event) { - var $dialog = $("#eventshow").removeClass().addClass('uidialog'); + var $dialog = $("#eventshow").dialog('close').removeClass().addClass('uidialog'); var calendar = event.calendar && me.calendars[event.calendar] ? me.calendars[event.calendar] : { editable:false }; me.selected_event = event; @@ -1564,9 +1563,126 @@ function rcube_calendar_ui(settings) return true; }; + /*** fullcalendar event handlers ***/ + + var fc_event_render = function(event, element, view) { + if (view.name != 'list' && view.name != 'table') { + var prefix = event.sensitivity != 0 ? String(sensitivitylabels[event.sensitivity]).toUpperCase()+': ' : ''; + element.attr('title', prefix + event.title); + } + if (view.name == 'month') { + // limit the number of events displayed + var sday = event.start.getMonth()*120 + event.start.getDate(); + var eday = event.end.getMonth()*120 + event.end.getDate(); + if (!view._eventcount[sday]) view._eventcount[sday] = 1; + else view._eventcount[sday]++; + if (!view._eventcount[eday]) view._eventcount[eday] = 1; + else if (eday != sday) view._eventcount[eday]++; + + if (view._eventcount[sday] >= view._maxevents) { + if (!view._morelink[sday]) { + view._morelink[sday] = view._morelink[event.id] = $('
') + .addClass('fc-event-more') + .css({ position:'absolute', left:element.css('left'), width:element.css('width') }) + .appendTo(element.parent()) + .data('overflow', 1); + } + else { + view._morelink[sday].data('overflow', view._eventcount[sday] - view._maxevents); + return false; + } + } + else if (view._eventcount[eday] >= view._maxevents || view._morelink[event.id]) { + return false; + } + } + else { + if (event.location) { + element.find('div.fc-event-title').after('
@ ' + Q(event.location) + '
'); + } + if (event.sensitivity != 0) + element.find('div.fc-event-time').append(''); + if (event.recurrence) + element.find('div.fc-event-time').append(''); + if (event.alarms) + element.find('div.fc-event-time').append(''); + } + }; + /*** public methods ***/ - + + // opens calendar day-view in a popup + this.fisheye_view = function(date) + { + $('#fish-eye-view').dialog('close'); + + // create list of active event sources + var src, cals = {}, sources = []; + for (var id in this.calendars) { + src = $.extend({}, this.calendars[id]); + src.editable = false; + src.url = null; + src.events = []; + + if (cal.active) { + cals[id] = src; + sources.push(src); + } + } + + // copy events already loaded + var events = fc.fullCalendar('clientEvents'); + for (var event, i=0; i< events.length; i++) { + event = events[i]; + if (event.source && (src = cals[event.source.id])) { + src.events.push(event); + } + } + + var h = $(window).height() - 50; + var dialog = $('
') + .attr('id', 'fish-eye-view') + .dialog({ + modal: true, + width: 680, + height: h, + title: $.fullCalendar.formatDate(date, 'dddd ' + settings['date_long']), + close: function(){ + dialog.dialog("destroy"); + me.fisheye_date = null; + } + }) + .fullCalendar({ + header: { left: '', center: '', right: '' }, + height: h - 50, + defaultView: 'agendaDay', + date: date.getDate(), + month: date.getMonth(), + year: date.getFullYear(), + ignoreTimezone: true, // will treat the given date strings as in local (browser's) timezone + eventSources: sources, + monthNames : settings['months'], + monthNamesShort : settings['months_short'], + dayNames : settings['days'], + dayNamesShort : settings['days_short'], + firstDay : settings['first_day'], + firstHour : settings['first_hour'], + slotMinutes : 60/settings['timeslots'], + timeFormat: { '': settings['time_format'] }, + axisFormat : settings['time_format'], + columnFormat: { day: 'dddd ' + settings['date_short'] }, + titleFormat: { day: 'dddd ' + settings['date_long'] }, + allDayText: rcmail.gettext('all-day', 'calendar'), + eventRender: fc_event_render, + eventClick: function(event) { + event_show_dialog(event); + } + }); + + this.fisheye_date = date; + }; + //public method to show the print dialog. this.print_calendars = function(view) { @@ -1813,6 +1929,9 @@ function rcube_calendar_ui(settings) }); } } + + if (this.fisheye_date) + this.fisheye_view(this.fisheye_date); }; // resize and reposition (center) the dialog window @@ -1955,37 +2074,19 @@ function rcube_calendar_ui(settings) me.events_loaded($(this).fullCalendar('clientEvents').length); }, // event rendering - eventRender: function(event, element, view) { - if (view.name != 'list' && view.name != 'table') { - var prefix = event.sensitivity != 0 ? String(sensitivitylabels[event.sensitivity]).toUpperCase()+': ' : ''; - element.attr('title', prefix + event.title); - } - if (view.name == 'month') { -/* attempt to limit the number of events displayed - (could also be used to init fish-eye-view) - var max = 4; // to be derrived from window size - var sday = event.start.getMonth()*12 + event.start.getDate(); - var eday = event.end.getMonth()*12 + event.end.getDate(); - if (!me.eventcount[sday]) me.eventcount[sday] = 1; - else me.eventcount[sday]++; - if (!me.eventcount[eday]) me.eventcount[eday] = 1; - else if (eday != sday) me.eventcount[eday]++; - - if (me.eventcount[sday] > max || me.eventcount[eday] > max) - return false; -*/ - } - else { - if (event.location) { - element.find('div.fc-event-title').after('
@ ' + Q(event.location) + '
'); - } - if (event.sensitivity != 0) - element.find('div.fc-event-time').append(''); - if (event.recurrence) - element.find('div.fc-event-time').append(''); - if (event.alarms) - element.find('div.fc-event-time').append(''); + eventRender: fc_event_render, + eventAfterRender: function(event, element, view) { + // adjust position of the more... element + var link; + if (view.name == 'month' && (link = view._morelink[event.id]) && !link.data('date') && link.data('overflow') > 1) { + link.html(rcmail.gettext('andnmore', 'calendar').replace('$nr', link.data('overflow'))) + .css({ left:element.css('left'), top:element.css('top') }) + .data('date', new Date(event.start.getTime())) + .click(function(e){ me.fisheye_view($(this).data('date')); }); + element.remove(); } + else if (link) + link.remove(); }, // callback for date range selection select: function(start, end, allDay, e, view) { @@ -2066,14 +2167,13 @@ function rcube_calendar_ui(settings) update_event_confirm('resize', event, data); }, viewDisplay: function(view) { - me.eventcount = []; - if (!bw.ie) - window.setTimeout(function(){ $('div.fc-content').css('overflow', view.name == 'month' ? 'auto' : 'hidden') }, 10); if (minical) window.setTimeout(function(){ minical.datepicker('setDate', fc.fullCalendar('getDate')); }, exec_deferred); }, - windowResize: function(view) { - me.eventcount = []; + viewRender: function(view) { + view._maxevents = Math.floor((view.element.parent().height()-18) / 108) - 1; + view._eventcount = []; + view._morelink = []; } }); @@ -2387,6 +2487,10 @@ window.rcmail && rcmail.addEventListener('init', function(evt) { event.source = source; // link with source fc.fullCalendar('renderEvent', event); } + + // refresh fish-eye view + if (cal.fisheye_date) + cal.fisheye_view(cal.fisheye_date); } // remove temp events diff --git a/plugins/calendar/lib/fullcalendar-rc.patch b/plugins/calendar/lib/fullcalendar-rc.patch index e821ce4a..cf723b38 100644 --- a/plugins/calendar/lib/fullcalendar-rc.patch +++ b/plugins/calendar/lib/fullcalendar-rc.patch @@ -1,5 +1,5 @@ --- js/fullcalendar.js.orig 2011-04-09 14:13:16.000000000 +0200 -+++ js/fullcalendar.js 2011-08-07 18:43:34.000000000 +0200 ++++ js/fullcalendar.js 2011-09-07 11:53:03.000000000 +0200 @@ -47,12 +47,16 @@ titleFormat: { month: 'MMMM yyyy', @@ -49,7 +49,15 @@ // jquery-ui theming theme: false, -@@ -500,8 +524,8 @@ +@@ -424,6 +448,7 @@ + setSize(); + unselect(); + currentView.clearEvents(); ++ currentView.trigger('viewRender', currentView); + currentView.renderEvents(events); + currentView.sizeDirty = false; + } +@@ -500,8 +525,8 @@ } @@ -60,7 +68,15 @@ } -@@ -632,6 +656,8 @@ +@@ -523,6 +548,7 @@ + markEventsDirty(); + if (elementVisible()) { + currentView.clearEvents(); ++ currentView.trigger('viewRender', currentView); + currentView.renderEvents(events, modifiedEventID); + currentView.eventsDirty = false; + } +@@ -632,6 +658,8 @@ if (name == 'height' || name == 'contentHeight' || name == 'aspectRatio') { options[name] = value; updateSize(); @@ -69,7 +85,7 @@ } } -@@ -897,15 +923,16 @@ +@@ -897,15 +925,16 @@ } @@ -90,7 +106,7 @@ } } -@@ -1579,10 +1606,23 @@ +@@ -1579,10 +1608,23 @@ return 'th'; } return ['st', 'nd', 'rd'][date%10-1] || 'th'; @@ -115,7 +131,7 @@ fc.applyAll = applyAll; -@@ -3534,10 +3574,10 @@ +@@ -3534,10 +3576,10 @@ function slotSelectionMousedown(ev) { if (ev.which == 1 && opt('selectable')) { // ev.which==1 means left mouse button unselect(ev); @@ -128,7 +144,7 @@ var d1 = cellDate(origCell); var d2 = cellDate(cell); dates = [ -@@ -3762,7 +3802,8 @@ +@@ -3762,7 +3804,8 @@ height, slotSegmentContainer = getSlotSegmentContainer(), rtl, dis, dit, @@ -138,7 +154,7 @@ if (rtl = opt('isRTL')) { dis = -1; -@@ -3789,8 +3830,11 @@ +@@ -3789,8 +3832,11 @@ outerWidth = availWidth / (levelI + forward + 1); }else{ if (forward) { @@ -152,7 +168,7 @@ }else{ // can be entire width, aligned left outerWidth = availWidth; -@@ -3801,7 +3845,7 @@ +@@ -3801,7 +3847,7 @@ * dis + (rtl ? availWidth - outerWidth : 0); // rtl seg.top = top; seg.left = left; @@ -161,7 +177,7 @@ seg.outerHeight = bottom - top; html += slotSegHtml(event, seg); } -@@ -4260,7 +4304,7 @@ +@@ -4260,7 +4306,7 @@ function opt(name, viewNameOverride) { var v = options[name]; @@ -170,7 +186,7 @@ return smartProperty(v, viewNameOverride || viewName); } return v; -@@ -5204,5 +5248,561 @@ +@@ -5204,5 +5250,561 @@ }; } diff --git a/plugins/calendar/lib/js/fullcalendar.js b/plugins/calendar/lib/js/fullcalendar.js index fe387c8b..9874ee07 100644 --- a/plugins/calendar/lib/js/fullcalendar.js +++ b/plugins/calendar/lib/js/fullcalendar.js @@ -448,6 +448,7 @@ function Calendar(element, options, eventSources) { setSize(); unselect(); currentView.clearEvents(); + currentView.trigger('viewRender', currentView); currentView.renderEvents(events); currentView.sizeDirty = false; } @@ -547,6 +548,7 @@ function Calendar(element, options, eventSources) { markEventsDirty(); if (elementVisible()) { currentView.clearEvents(); + currentView.trigger('viewRender', currentView); currentView.renderEvents(events, modifiedEventID); currentView.eventsDirty = false; } diff --git a/plugins/calendar/localization/de_CH.inc b/plugins/calendar/localization/de_CH.inc index 44afacc6..3c4b91a5 100644 --- a/plugins/calendar/localization/de_CH.inc +++ b/plugins/calendar/localization/de_CH.inc @@ -64,7 +64,7 @@ $labels['printdescriptions'] = 'Beschrieb drucken'; $labels['parentcalendar'] = 'Übergeordneter Kalender'; $labels['searchearlierdates'] = '« Frühere Termine suchen'; $labels['searchlaterdates'] = 'Spätere Termine suchen »'; -$labels['andnmore'] = 'und $nr weitere'; +$labels['andnmore'] = '$nr weitere...'; $labels['togglerole'] = 'Klick zum Ändern der Rolle'; // alarm/reminder settings diff --git a/plugins/calendar/localization/de_DE.inc b/plugins/calendar/localization/de_DE.inc index 42ea4c1b..e15784dd 100644 --- a/plugins/calendar/localization/de_DE.inc +++ b/plugins/calendar/localization/de_DE.inc @@ -64,7 +64,7 @@ $labels['printdescriptions'] = 'Beschrieb drucken'; $labels['parentcalendar'] = 'Übergeordneter Kalender'; $labels['searchearlierdates'] = '« Frühere Termine suchen'; $labels['searchlaterdates'] = 'Spätere Termine suchen »'; -$labels['andnmore'] = 'und $nr weitere'; +$labels['andnmore'] = '$nr weitere...'; // alarm/reminder settings $labels['showalarms'] = 'Erinnerungen anzeigen'; diff --git a/plugins/calendar/localization/en_US.inc b/plugins/calendar/localization/en_US.inc index 986068db..2103fac3 100644 --- a/plugins/calendar/localization/en_US.inc +++ b/plugins/calendar/localization/en_US.inc @@ -64,7 +64,7 @@ $labels['printdescriptions'] = 'Print descriptions'; $labels['parentcalendar'] = 'Superior calendar'; $labels['searchearlierdates'] = '« Search for earlier events'; $labels['searchlaterdates'] = 'Search for later events »'; -$labels['andnmore'] = 'and $nr more'; +$labels['andnmore'] = '$nr more...'; $labels['togglerole'] = 'Click to toggle role'; // alarm/reminder settings diff --git a/plugins/calendar/skins/default/calendar.css b/plugins/calendar/skins/default/calendar.css index febc8b03..a8b9a09a 100644 --- a/plugins/calendar/skins/default/calendar.css +++ b/plugins/calendar/skins/default/calendar.css @@ -1026,6 +1026,13 @@ div.fc-event-location { font-size: 90%; } +.fc-event-more { + color: #999; + font-size: 90%; + padding-top: 1px; + cursor: pointer; +} + .fc-agenda-slots td div { height: 22px; }