tine20/Calendar/js/CalendarPanelSplitPlugin.js
Rama main · réplica de lectura del repositorio original.
Tine.Calendar.CalendarPanelSplitPlugin = function() {
};
/**
* @TODO ui for active view?
* @TODO we could also create views beforeload for better performace
*
*/
Tine.Calendar.CalendarPanelSplitPlugin.prototype = {
/**
* @property app
* @type Tine.Calendar.Application
*/
app: null,
/**
* @property attendeeViews
* @type Ext.util.MixedCollection
*/
attendeeViews: null,
/**
* @property mainStore store of main view
* @type Ext.Store
*/
mainStore: null,
init: function(calPanel) {
if (! Tine.Tinebase.appMgr.get('Calendar').featureEnabled('featureSplitView')) {
return;
}
this.calPanel = calPanel;
this.app = Tine.Tinebase.appMgr.get('Calendar');
this.attendeeViews = new Ext.util.MixedCollection();
// NOTE: we can't use a normal hbox layout as it can't deal with minWidth and overflow.
// As ext has no suiteable layout for this, we do a little hack
this.calPanel.layout = new Ext.layout.HBoxLayout({
align : 'stretch',
pack : 'start'
});
this.calPanel.layout.onLayout = this.calPanel.layout.onLayout.createInterceptor(function() {
var viewCount = this.attendeeViews.getCount(),
availableWidth = this.calPanel.getWidth(),
minViewWidth = this.calPanel.view.boxMinWidth;
var width = availableWidth/viewCount < minViewWidth ? minViewWidth * viewCount : availableWidth;
this.calPanel.body.setWidth(width);
this.calPanel.body.setStyle('overflow-x', width > availableWidth ? 'scroll' : 'hidden');
}, this);
this.calPanel.on('afterlayout', function() {
this.calPanel.body.setWidth(this.calPanel.getWidth());
}, this);
this.mainStore = this.calPanel.view.store;
this.mainStore.on('load', this.onMainStoreLoad, this);
this.mainStore.on('beforeload', this.onMainStoreBeforeLoad, this);
// NOTE: we remove from items to avoid autoDestroy
this.calPanel.items.remove(this.calPanel.view);
this.calPanel.getView = this.getActiveView.createDelegate(this);
this.calPanel.on('show', this.onCalPanelShow, this);
this.calPanel.on('hide', this.onCalPanelHide, this);
this.calPanel.on('afterrender', function() {
this.attendeeFilterGrid = this.app.getMainScreen().getWestPanel().getAttendeeFilter();
this.attendeeFilterGrid.on('sortchange', this.onAttendeeFilterSortChange, this);
}, this);
// hook into getDefaultData
this.originalGetDefaultData = Tine.Calendar.Model.Event.getDefaultData;
Tine.Calendar.Model.Event.getDefaultData = this.getDefaultData.createDelegate(this);
},
getDefaultData: function() {
var defaultData = this.originalGetDefaultData(),
useSplit = Tine.Calendar.CalendarPanelSplitPlugin.splitBtn && Tine.Calendar.CalendarPanelSplitPlugin.splitBtn.pressed,
centerPanel = this.app.getMainScreen().getCenterPanel(),
activeCalPanel = centerPanel.getCalendarPanel(centerPanel.activeView),
activeView = this.getActiveView(),
attendee = activeView && activeView.ownerCt ? activeView.ownerCt.attendee : null;
if (useSplit && attendee && this.calPanel == activeCalPanel) {
Ext.apply(attendee.data, {
status: 'ACCEPTED'
});
defaultData.attendee = [attendee.data];
}
return defaultData;
},
onCalPanelShow: function() {
if (Tine.Calendar.CalendarPanelSplitPlugin.splitBtn) {
Tine.Calendar.CalendarPanelSplitPlugin.splitBtn.enable();
} else {
Tine.Calendar.CalendarPanelSplitPlugin.SplitBtn.prototype.disabled = false;
}
},
onCalPanelHide: function() {
if (Tine.Calendar.CalendarPanelSplitPlugin.splitBtn) {
Tine.Calendar.CalendarPanelSplitPlugin.splitBtn.disable();
}
},
onMainStoreBeforeLoad: function(store, options) {
var ret = true;
this.attendeeViews.each(function(attendeeView) {
ret = ret && attendeeView.store.fireEvent('beforeload', store, options);
}, this);
return ret;
},
onMainStoreLoad: function(store, options) {
var cp = this.app.getMainScreen().getCenterPanel();
if (store !== cp.getCalendarPanel(cp.activeView).getStore()) {
Tine.log.debug('Tine.Calendar.CalendarPanelSplitPlugin::onMainStoreLoad try again with active subview');
cp.onStoreLoad(this.getActiveView().store, options);
}
if (! this.attendeeFilterGrid) {
return;
}
// create view for each attendee
var filteredAttendee = this.attendeeFilterGrid.getValue(),
attendeeStore = Tine.Calendar.Model.Attender.getAttendeeStore(filteredAttendee),
useSplit = Tine.Calendar.CalendarPanelSplitPlugin.splitBtn && Tine.Calendar.CalendarPanelSplitPlugin.splitBtn.pressed;
// remove views not longer in filter
this.calPanel.items.each(function(view) {
if (view.attendee && (! useSplit || ! Tine.Calendar.Model.Attender.getAttendeeStore.getAttenderRecord(attendeeStore, view.attendee))) {
this.removeAttendeeView(view.attendee);
}
}, this);
this.manageSplitViews(filteredAttendee);
// manage main (main is shown if no split criteria is present)
if (! filteredAttendee.length || ! useSplit) {
if (! this.attendeeViews.get('main')) {
var view = this.createView({
store: this.cloneStore(false)
});
this.attendeeViews.add('main', view);
this.calPanel.add({
layout: 'fit',
border: false,
flex: 1,
height: this.calPanel.getHeight(), // <- initialHeight
items: view
});
} else {
this.attendeeViews.get('main').initData(this.cloneStore(false));
this.attendeeViews.get('main').onLoad();
}
} else {
var main = this.attendeeViews.get('main');
if (main && main.ownerCt) {
this.calPanel.remove(main.ownerCt);
}
this.attendeeViews.removeKey('main');
}
this.calPanel.doLayout();
},
addAttendeeView: function(attendee, pos) {
var attendeeName = Tine.Calendar.AttendeeGridPanel.prototype.renderAttenderName.call(Tine.Calendar.AttendeeGridPanel.prototype, attendee.get('user_id'), false, attendee),
attendeeViewId = this.getAttendeeViewId(attendee);
var filter = function(r) {
var attendeeStore = Tine.Calendar.Model.Attender.getAttendeeStore(r.get('attendee'));
return Tine.Calendar.Model.Attender.getAttendeeStore.getAttenderRecord(attendeeStore, attendee);
};
var store = this.cloneStore(filter);
var view = this.createView({
title: attendeeName,
store: store,
print: this.onPrint.createDelegate(this)
});
// manage active views
view.on('render', function(v){
v.mon(v.getEl(), 'mousedown', this.setActiveAttendeeView.createDelegate(this, [attendeeViewId]), this);
}, this);
this.attendeeViews.add(attendeeViewId, view);
this.calPanel.insert(pos, {
xtype: 'tabpanel',
style: 'padding: 5px;',
id: attendeeViewId,
attendee: attendee,
plain: true,
flex: 1,
height: this.calPanel.getHeight(), // <- initialHeight
activeItem: 0,
items: view
});
},
onAttendeeFilterSortChange: function() {
var filteredAttendee = this.attendeeFilterGrid.getValue();
this.manageSplitViews(filteredAttendee);
this.calPanel.doLayout();
},
manageSplitViews: function(filteredAttendee) {
// create view for each attendee
var useSplit = Tine.Calendar.CalendarPanelSplitPlugin.splitBtn && Tine.Calendar.CalendarPanelSplitPlugin.splitBtn.pressed;
if (useSplit) {
// add subviews new to filter
// NOTE: we don't iterate the attendeeStore as we would loose attendee order than
Ext.each(filteredAttendee, function(attendeeData, idx) {
var attendee = new Tine.Calendar.Model.Attender(attendeeData, attendeeData.id);
var attendeeView = this.attendeeViews.get(this.getAttendeeViewId(attendee));
if (! attendeeView) {
this.addAttendeeView(attendee, idx);
} else {
//assert position
this.calPanel.items.remove(attendeeView.ownerCt);
this.calPanel.items.insert(idx, attendeeView.ownerCt);
var filterFn = attendeeView.store.filterFn;
attendeeView.initData(this.cloneStore(filterFn));
attendeeView.onLoad();
}
}, this);
}
},
getAttendeeViewId: function(attendee) {
return this.calPanel.id + '-' + attendee.get('user_type') + '-' + attendee.getUserId();
},
removeAttendeeView: function(attendee) {
var attendeeViewId = this.getAttendeeViewId(attendee);
//@TODO remove relayed events?
this.attendeeViews.removeKey(attendeeViewId);
this.calPanel.remove(attendeeViewId);
},
createView: function(config) {
var view = Ext.create(Ext.apply({
xtype: this.calPanel.view.getXType(),
startDate: this.calPanel.getTopToolbar().periodPicker.getPeriod().from,
numOfDays: this.calPanel.view.numOfDays,
period: this.calPanel.getTopToolbar().periodPicker.getPeriod(),
updatePeriod: this.updatePeriod.createDelegate(this)
}, config));
// NOTE: this is already done by the view itself - don't have dublicate events in mainscreen!
// this.calPanel.relayEvents(view, ['changeView', 'changePeriod', 'addEvent', 'updateEvent', 'click', 'dblclick', 'contextmenu', 'keydown']);
this.calPanel.view.relayEvents(view, ['changeView', 'changePeriod', 'addEvent', 'updateEvent', 'click', 'dblclick', 'contextmenu', 'keydown']);
this.calPanel.view.getSelectionModel().relayEvents(view.getSelectionModel(), 'selectionchange');
view.getSelectionModel().on('selectionchange', this.onSelectionChange.createDelegate(this, [view]));
if (view.onBeforeScroll) {
view.onBeforeScroll = view.onBeforeScroll.createSequence(this.onScroll.createDelegate(this, [view], 0));
}
view.on('onBeforeAllDayScrollerResize', this.onAllDayAreaResize, this);
return view;
},
/**
* is called on scroll of a grid, scrolls the other grids
*
* @param {Object} activeView
* @param {Object} e Event
*/
onScroll: function(activeView, e) {
if (! (activeView && activeView.scroller && activeView.scroller.dom && activeView.getHeight() > 100)) {
return;
}
var scrollTop = activeView.scroller.dom.scrollTop;
this.attendeeViews.each(function(view) {
if (activeView.id != view.id && view.scroller) {
view.scroller.dom.scrollTop = scrollTop;
}
}, this);
},
getActiveView: function() {
if (! this.attendeeViews.getCount()) {
return this.calPanel.view;
}
if (! this.activeAttendeeView || this.attendeeViews.indexOf(this.activeAttendeeView) < 0) {
this.activeAttendeeView = this.attendeeViews.itemAt(0);
}
return this.activeAttendeeView;
},
onPrint: function(printMode) {
var renderer = new Tine.Calendar.Printer.SplitViewRenderer({printMode: printMode});
renderer.print(this);
},
onSelectionChange: function(view) {
view = Ext.isString(view) ? this.attendeeViews.get(view) : view;
this.setActiveAttendeeView(view);
this.attendeeViews.each(function(v) {
if (v !== view) {
v.getSelectionModel().clearSelections(true);
}
}, this);
if (this.calPanel.mainScreen) {
this.calPanel.mainScreen.updateEventActions.call(this.calPanel.mainScreen);
}
},
setActiveAttendeeView: function(view) {
view = Ext.isString(view) ? this.attendeeViews.get(view) : view;
if (view != this.activeAttendeeView && this.activeAttendeeView.endEditSummary && this.activeAttendeeView.editing) {
this.activeAttendeeView.endEditSummary();
}
this.activeAttendeeView = view;
},
updatePeriod: function() {
var origArgs = arguments;
this.attendeeViews.each(function(view) {
view.constructor.prototype.updatePeriod.apply(view, origArgs);
}, this);
},
onAllDayAreaResize: function (originView, rzEvent) {
if (this.attendeeViews.items.length > 1) {
var maxWholeDayAreaSize = 10;
this.attendeeViews.each(function(view) {
if (view.wholeDayArea) {
var allDayAreaHeight = view.computeAllDayAreaHeight();
if (allDayAreaHeight > maxWholeDayAreaSize) {
maxWholeDayAreaSize = allDayAreaHeight;
}
}
}, this);
rzEvent.wholeDayScrollerHeight = Math.min(maxWholeDayAreaSize, rzEvent.maxAllowedHeight);
this.attendeeViews.each(function(view) {
if (view.wholeDayArea) {
var allDayAreaEl = Ext.get(view.wholeDayArea),
allDayAreaHeight = allDayAreaEl.getHeight();
view.wholeDayScroller.setHeight(rzEvent.wholeDayScrollerHeight);
allDayAreaEl.setHeight(Math.max(allDayAreaHeight, maxWholeDayAreaSize));
}
}, this);
}
},
cloneStore: function(filterFn) {
var clone = new Ext.data.Store({
fields: Tine.Calendar.Model.Event,
load: this.mainStore.load.createDelegate(this.mainStore),
proxy: this.mainStore.proxy,
filterFn: filterFn,
replaceRecord: function(o, n) {
var r = this.getById(o.id); // refetch record as it might be outdated in the meantime
var idx = this.indexOf(r);
this.remove(r);
this.insert(idx, n);
}
});
var rs = [];
this.mainStore.each(function(r) {
if (! filterFn || filterFn(r)) {
rs.push(r.copy());
}
}, this);
clone.add(rs);
clone.on('add', this.onCloneStoreAdd, this);
clone.on('update', this.onCloneStoreUpdate, this);
clone.on('remove', this.onCloneStoreRemove, this);
return clone;
},
onCloneStoreAdd: function(store, rs) {
Ext.each(rs, function(r){
this.attendeeViews.each(function(view) {
if (view.store != store) {
if (! view.store.filterFn || view.store.filterFn(r)) {
view.store.un('add', this.onCloneStoreAdd, this);
view.store.add([r.copy()]);
view.store.on('add', this.onCloneStoreAdd, this);
}
}
}, this);
// check if events fits into view @see Tine.Calendar.MainScreenCenterPanel::congruenceFilterCheck
if (store.filterFn && !store.filterFn(r)) {
(function() {
if (this.ui && this.ui.rendered) {
this.ui.markOutOfFilter();
}
}).defer(25, r)
}
}, this);
},
onCloneStoreUpdate: function(store, r) {
this.attendeeViews.each(function(view) {
if (view.store != store) {
view.store.un('add', this.onCloneStoreAdd, this);
view.store.un('remove', this.onCloneStoreRemove, this);
var cr = view.store.getById(r.id);
if (cr) {
view.store.remove(cr);
view.store.add([r.copy()]);
}
view.store.on('add', this.onCloneStoreAdd, this);
view.store.on('remove', this.onCloneStoreRemove, this);
}
}, this);
},
onCloneStoreRemove: function(store, r) {
this.attendeeViews.each(function(view) {
if (view.store != store) {
view.store.un('remove', this.onCloneStoreRemove, this);
view.store.remove(view.store.getById(r.id));
view.store.on('remove', this.onCloneStoreRemove, this);
}
}, this);
}
};
Ext.preg('Calendar.CalendarPanelSplitPlugin', Tine.Calendar.CalendarPanelSplitPlugin);
Tine.Calendar.CalendarPanelSplitPlugin.SplitBtn = Ext.extend(Ext.Button, {
enableToggle: true,
pressed: false,
disabled: true,
scale: 'medium',
rowspan: 2,
iconCls: 'cal-split-view',
iconAlign: 'top',
text: 'Split',
stateful: true,
stateId: 'cal-calpanel-split-btn',
stateEvents: ['toggle'],
initComponent: function() {
if (! Tine.Tinebase.appMgr.get('Calendar').featureEnabled('featureSplitView')) {
// hide button and make sure it isn't pressed
Tine.log.info('Split view feature is deactivated');
this.hidden = true;
this.pressed = false;
}
Tine.Calendar.CalendarPanelSplitPlugin.SplitBtn.superclass.initComponent.apply(this, arguments);
Tine.Calendar.CalendarPanelSplitPlugin.splitBtn = this;
},
handler: function() {
Tine.Tinebase.appMgr.get('Calendar').getMainScreen().getCenterPanel().refresh();
},
getState: function() {
return {pressed: this.pressed}
}
});
Tine.Calendar.Printer.SplitViewRenderer = Ext.extend(Tine.Calendar.Printer.BaseRenderer, {
getAdditionalHeaders: Tine.Calendar.Printer.DaysViewRenderer.prototype.getAdditionalHeaders,
generateBody: function(splitView) {
var viewRenderer = splitView.calPanel.view.printRenderer,
htmlArray = [],
me = this,
_ = window.lodash;
me.rendererArray = [];
me.viewArray = [];
me.paperHeight = viewRenderer.paperHeight;
me.useHtml2Canvas = me.printMode == 'sheet' && splitView.calPanel.view.cls != "cal-monthview";
return _.reduce(splitView.attendeeViews.items, function(promise, view, i) {
return promise.then(function() {
var renderer = new view.printRenderer({printMode: me.printMode});
me.rendererArray.push(renderer);
me.viewArray.push(view);
renderer.extraTitle = view.title + ' // ';
renderer.titleStyle = i > 0 ? 'page-break-before:always' : '';
return renderer.generateBody(view);
}).then(function(html) {
htmlArray.push('<div class="page">' + html + '</div>');
})
}, Promise.resolve('')).then(function(){
return htmlArray.join('');
});
},
onBeforePrint: function(doc, view) {
Ext.each(this.rendererArray, function(renderer, i) {
renderer.onBeforePrint(doc, this.viewArray[i]);
}, this)
}
});
// self register
Tine.Calendar.CalendarPanel.prototype.plugins = '[{"ptype": "Calendar.CalendarPanelSplitPlugin"}]';
Ext.ux.ItemRegistry.registerItem('Calendar-MainScreenPanel-ViewBtnGrp', Tine.Calendar.CalendarPanelSplitPlugin.SplitBtn, -10);