oxjs/source/Ox.UI/js/Calendar/Ox.Calendar.js

1030 lines
32 KiB
JavaScript
Raw Normal View History

2011-04-23 16:45:50 +00:00
// vim: et:ts=4:sw=4:sts=4:ft=js
2011-05-05 18:02:56 +00:00
/*@
2011-05-16 08:24:46 +00:00
Ox.Calendar <f:Ox.Element> Basic calendar object
2011-05-05 18:02:56 +00:00
() -> <f> Calendar object
(options) -> <f> Calendar object
(options, self) -> <f> Calendar object
options <o> Options object
date <d|new Date()> UTC Date on which the calendar is centered
2011-05-25 09:22:16 +00:00
events <[o]|[]> Event objects to be displayed
alternativeNames <[s]> Array of alternative names
end <s> End of the event (UTC Date, as string)
id <s> Id of the event
name <s> Name of the event
start <s> Start of the event (UTC Date, as string)
type <s> Type of the event (like "person")
2011-05-05 18:02:56 +00:00
height <n|256> Height in px
range <[n]|[1000, 3000]> Start and end year of the calendar
2011-05-25 09:22:16 +00:00
selected <s|''> Id of the selected event
2011-05-05 18:02:56 +00:00
width <n|256> Width in px
zoom <n|8> Initial zoom level
self <o> Shared private variable
@*/
// Fixme: switch to UTC
2011-05-25 09:22:16 +00:00
// Fixme: create a variable-resolution event type (with end that is _inclusive_)
2011-05-05 18:02:56 +00:00
2011-04-22 22:03:10 +00:00
Ox.Calendar = function(options, self) {
self = self || {};
var that = new Ox.Element({}, self)
.defaults({
date: new Date(),
2011-05-25 09:22:16 +00:00
events: [],
2011-05-05 18:02:56 +00:00
height: 256,
range: [1000, 3000],
2011-05-25 09:22:16 +00:00
selected: '',
showTypes: ['date', 'place', 'person', 'other'],
2011-05-05 18:02:56 +00:00
width: 256,
2011-04-22 22:03:10 +00:00
zoom: 8
})
.options(options || {})
.addClass('OxCalendar')
.css({
width: self.options.width + 'px',
height: self.options.height + 'px'
})
.bindEvent({
anyclick: function(e) {
!$(e.target).is('.OxInput') && that.gainFocus();
},
key_0: function() {
panToSelected();
},
key_down: function() {
scrollBy(1);
},
key_equal: function() {
zoomBy(1);
},
key_escape: function() {
selectEvent('');
},
key_left: function() {
panBy(-self.$content.width() / 2 * getSecondsPerPixel() * 1000);
},
key_minus: function() {
zoomBy(-1);
},
key_right: function() {
panBy(self.$content.width() / 2 * getSecondsPerPixel() * 1000);
},
key_shift_0: function() {
zoomToSelected();
},
key_shift_down: function() {
scrollTo(1000000, true);
},
key_shift_up: function() {
scrollTo(0, true);
},
key_up: function() {
scrollBy(-1);
}
2011-04-22 22:03:10 +00:00
});
2011-05-25 09:22:16 +00:00
self.options.events.forEach(function(event) {
2011-05-26 18:18:20 +00:00
if (!event.end) {
event.end = Ox.formatDate(new Date(), '%Y-%m-%d');
event.current = true;
}
2011-05-25 09:22:16 +00:00
event.id = Ox.isUndefined(event.id) ? Ox.uid() : event.id;
event.startTime = Ox.parseDate(event.start, true);
event.endTime = Ox.parseDate(event.end, true);
event.rangeText = Ox.formatDateRange(event.start, event.end, true);
event.durationText = Ox.formatDateRangeDuration(event.start, event.end, true);
2011-05-26 18:18:20 +00:00
if (event.current) {
event.rangeText = event.rangeText.split(' - ').shift() + ' - ...';
}
2011-05-25 09:22:16 +00:00
});
2011-04-22 22:03:10 +00:00
self.maxZoom = 32;
self.minLabelWidth = 80;
/*
We need to iterate over irregular intervals, like months or years.
The idea is to put this logic into a data structure, the units.
Just like the 0-th second is 1970-01-01 00:00:00, the 0th month
is 1970-01, or the 0-th century is the 20th century.
A month unit, for example, has the following properties:
- seconds: average number of seconds (used to compute width at zoom)
- date: returns the start date of the index-th month
- name: returns a string representation of the index-th month
- value: returns the month index for a given date
*/
2011-04-22 22:03:10 +00:00
self.units = [
{
id: 'millennium',
seconds: 365242.5 * 86400,
date: function(i) {
return Ox.parseDate((i + 1) * 1000, true);
2011-04-22 22:03:10 +00:00
},
name: function(i) {
return i > -2
? Ox.formatOrdinal(i + 2) + ' Millennium'
: Ox.formatOrdinal(-i - 1) + ' Millennium BC'
2011-04-22 22:03:10 +00:00
},
value: function(date) {
2011-04-25 09:12:02 +00:00
return Math.floor(date.getUTCFullYear() / 1000) - 1;
2011-04-22 22:03:10 +00:00
}
},
{
id: 'century',
seconds: 36524.25 * 86400,
date: function(i) {
return Ox.parseDate((i + 19) * 100, true);
2011-04-22 22:03:10 +00:00
},
name: function(i) {
return i > -20
? Ox.formatOrdinal(i + 20) + ' Century'
: Ox.formatOrdinal(-i - 19) + ' Century BC'
2011-04-22 22:03:10 +00:00
},
value: function(date) {
2011-04-25 09:12:02 +00:00
return Math.floor(date.getUTCFullYear() / 100) - 19;
2011-04-22 22:03:10 +00:00
}
},
{
id: 'decade',
seconds: 3652.425 * 86400,
date: function(i) {
return Ox.parseDate((i + 197) * 10, true);
2011-04-22 22:03:10 +00:00
},
name: function(i) {
return i > -198
? (i + 197) + '0s'
: (-i - 198) + '0s BC';
2011-04-22 22:03:10 +00:00
},
value: function(date) {
2011-04-25 09:12:02 +00:00
return Math.floor(date.getUTCFullYear() / 10) - 197;
2011-04-22 22:03:10 +00:00
}
},
{
id: 'year',
seconds: 365.2425 * 86400,
date: function(i) {
return Ox.parseDate(i + 1970, true);
2011-04-22 22:03:10 +00:00
},
name: function(i) {
return Ox.formatDate(Ox.parseDate(i + 1970, true), '%x', true);
2011-04-22 22:03:10 +00:00
},
value: function(date) {
2011-04-25 09:12:02 +00:00
return date.getUTCFullYear() - 1970;
2011-04-22 22:03:10 +00:00
}
},
{
id: 'month',
seconds: 365.2425 / 12 * 86400,
date: function(i) {
return Ox.parseDate(
(Math.floor(i / 12) + 1970) + '-' + (Ox.mod(i, 12) + 1), true
);
2011-04-22 22:03:10 +00:00
},
name: function(i) {
return Ox.formatDate(Ox.parseDate(
(Math.floor(i / 12 + 1970)) + '-' + (Ox.mod(i, 12) + 1), true
), '%b %x', true);
2011-04-22 22:03:10 +00:00
},
value: function(date) {
2011-04-25 09:12:02 +00:00
return (date.getUTCFullYear() - 1970) * 12 + date.getUTCMonth();
2011-04-22 22:03:10 +00:00
}
},
{
id: 'week',
seconds: 7 * 86400,
date: function(i) {
return new Date((i * 7 - 3) * 86400000);
2011-04-22 22:03:10 +00:00
},
name: function(i) {
return Ox.formatDate(new Date((i * 7 - 3) * 86400000), '%a, %b %e', true);
2011-04-22 22:03:10 +00:00
},
value: function(date) {
2011-04-25 09:12:02 +00:00
return Math.floor((date / 86400000 + 4) / 7);
2011-04-22 22:03:10 +00:00
}
},
{
id: 'day',
seconds: 86400,
date: function(i) {
return new Date(i * 86400000);
2011-04-22 22:03:10 +00:00
},
name: function(i) {
return Ox.formatDate(new Date(i * 86400000), '%b %e, %x', true);
2011-04-22 22:03:10 +00:00
},
value: function(date) {
return Math.floor(date / 86400000);
}
},
{
id: 'six_hours',
seconds: 21600,
date: function(i) {
return new Date(i * 21600000);
2011-04-22 22:03:10 +00:00
},
name: function(i) {
2011-04-25 09:12:02 +00:00
return Ox.formatDate(new Date(i * 21600000), '%b %e, %H:00', true);
2011-04-22 22:03:10 +00:00
},
value: function(date) {
2011-04-25 09:12:02 +00:00
return Math.floor(date / 21600000);
2011-04-22 22:03:10 +00:00
}
},
{
id: 'hour',
seconds: 3600,
date: function(i) {
return new Date(i * 3600000);
2011-04-22 22:03:10 +00:00
},
name: function(i) {
2011-04-25 09:12:02 +00:00
return Ox.formatDate(new Date(i * 3600000), '%b %e, %H:00', true);
2011-04-22 22:03:10 +00:00
},
value: function(date) {
return Math.floor(date / 3600000);
}
},
{
id: 'five_minutes',
seconds: 300,
date: function(i) {
return new Date(i * 300000);
2011-04-22 22:03:10 +00:00
},
name: function(i) {
2011-04-25 09:12:02 +00:00
return Ox.formatDate(new Date(i * 300000), '%b %e, %H:%M', true);
2011-04-22 22:03:10 +00:00
},
value: function(date) {
return Math.floor(date / 300000);
}
},
{
id: 'minute',
seconds: 60,
date: function(i) {
return new Date(i * 60000);
2011-04-22 22:03:10 +00:00
},
name: function(i) {
2011-04-25 09:12:02 +00:00
return Ox.formatDate(new Date(i * 60000), '%b %e, %H:%M', true);
2011-04-22 22:03:10 +00:00
},
value: function(date) {
return Math.floor(date / 60000);
}
},
{
id: 'five_seconds',
seconds: 5,
date: function(i) {
return new Date(i * 5000);
2011-04-22 22:03:10 +00:00
},
name: function(i) {
2011-04-25 09:12:02 +00:00
return Ox.formatDate(new Date(i * 5000), '%H:%M:%S', true);
2011-04-22 22:03:10 +00:00
},
value: function(date) {
return Math.floor(date / 5000);
}
},
{
id: 'second',
seconds: 1,
date: function(i) {
return new Date(i * 1000);
2011-04-22 22:03:10 +00:00
},
name: function(i) {
2011-04-25 09:12:02 +00:00
return Ox.formatDate(new Date(i * 1000), '%H:%M:%S', true);
2011-04-22 22:03:10 +00:00
},
value: function(date) {
return Math.floor(date / 1000);
}
}
];
self.$toolbar = Ox.Bar({
size: 24
})
.appendTo(that);
self.$typeSelect = Ox.Select({
items: [
{id: 'date', title: 'Dates', checked: self.options.showTypes.indexOf('date') > -1},
{id: 'place', title: 'Places', checked: self.options.showTypes.indexOf('place') > -1},
{id: 'person', title: 'People', checked: self.options.showTypes.indexOf('person') > -1},
{id: 'other', title: 'Other', checked: self.options.showTypes.indexOf('other') > -1}
],
max: -1,
min: 1,
title: 'Show...',
width: 80
})
.css({float: 'left', margin: '4px'})
.bindEvent({
change: function(data) {
self.options.showTypes = data.selected.map(function(type) {
return type.id;
});
renderCalendar();
}
})
.appendTo(self.$toolbar);
self.$dateInput = Ox.Input({
clear: true,
//placeholder: 'Date',
value: Ox.formatDate(self.options.date, '%Y-%m-%d %H:%M:%S', true),
width: 160
})
.css({float: 'right', margin: '4px'})
.bindEvent({
change: function(data) {
panTo(Ox.parseDate(data.value, true))
}
})
.appendTo(self.$toolbar);
self.$scalebar = new Ox.Element()
.addClass('OxTimeline')
.css({
posision: 'absolute',
top: '24px'
})
.appendTo(that);
2011-04-22 22:03:10 +00:00
self.$container = new Ox.Element()
.addClass('OxCalendarContainer')
.css({
top: '40px',
bottom: '16px'
2011-04-22 22:03:10 +00:00
})
.bind({
mouseleave: mouseleave,
mousemove: mousemove,
mousewheel: mousewheel
})
.bindEvent({
doubleclick: doubleclick,
dragstart: dragstart,
drag: drag,
dragpause: dragpause,
dragend: dragend,
singleclick: singleclick
})
.appendTo(that);
self.$content = new Ox.Element()
.addClass('OxCalendarContent')
.appendTo(self.$container);
self.$background = new Ox.Element()
.addClass('OxBackground')
.appendTo(self.$content);
self.$scrollbar = new Ox.Element()
.addClass('OxTimeline')
.css({
posision: 'absolute',
bottom: '16px'
2011-04-22 22:03:10 +00:00
})
.appendTo(that);
self.$zoombar = new Ox.Element()
.css({
position: 'absolute',
bottom: 0,
2011-04-22 22:03:10 +00:00
height: '16px'
})
.appendTo(that);
2011-04-22 22:03:10 +00:00
self.$zoomInput = new Ox.Range({
arrows: true,
max: self.maxZoom,
min: 0,
size: self.options.width,
thumbSize: 32,
thumbValue: true,
value: self.options.zoom
})
.bindEvent({
change: changeZoom
})
.appendTo(self.$zoombar);
self.$tooltip = new Ox.Tooltip({
animate: false
})
.css({
textAlign: 'center'
});
2011-04-25 12:14:03 +00:00
2011-04-22 22:03:10 +00:00
renderCalendar();
function changeDate() {
}
function changeZoom(event, data) {
self.options.zoom = data.value;
renderCalendar();
}
function doubleclick(event, e) {
var $target = $(e.target),
id = $target.data('id');
if ($target.is('.OxLine > .OxEvent')) {
selectEvent(id, $target);
zoomToSelected();
} else {
2011-04-22 22:03:10 +00:00
if (self.options.zoom < self.maxZoom) {
self.options.date = new Date(
(+self.options.date + +getMouseDate(e)) / 2
);
self.options.zoom++;
}
renderCalendar();
}
}
function dragstart(event, e) {
//if ($(e.target).is(':not(.OxLine > .OxEvent)')) {
self.drag = {
top: self.$container.$element[0].scrollTop,
x: e.clientX
};
//}
2011-04-22 22:03:10 +00:00
}
function drag(event, e) {
if (self.drag) {
///*
var marginLeft = e.clientX - self.drag.x,
scrollbarFactor = getScrollbarFactor();
self.$scalebar.css({
marginLeft: marginLeft + 'px'
});
2011-04-22 22:03:10 +00:00
self.$content.css({
marginLeft: marginLeft + 'px'
2011-04-22 22:03:10 +00:00
});
self.$scrollbar.css({
marginLeft: Math.round(marginLeft / scrollbarFactor) + 'px'
2011-04-22 22:03:10 +00:00
});
scrollTo(self.drag.top - e.clientDY);
// fixme: after dragging too far in one direction,
// dragging in the opposite direction should work immediately
2011-04-22 22:03:10 +00:00
}
}
function dragpause(event, e) {
if (self.drag) {
dragafter(e);
self.drag.x = e.clientX;
2011-04-22 22:03:10 +00:00
}
}
function dragend(event, e) {
if (self.drag) {
dragafter(e);
self.drag = null;
}
}
function dragafter(e) {
self.$scalebar.css({marginLeft: 0});
self.$content.css({marginLeft: 0});
self.$scrollbar.css({marginLeft: 0});
2011-04-22 22:03:10 +00:00
self.options.date = new Date(
+self.options.date - (e.clientX - self.drag.x) * getSecondsPerPixel() * 1000
);
renderCalendar();
}
function dragstartScrollbar(event, e) {
self.drag = {x: e.clientX};
}
function dragScrollbar(event, e) {
var marginLeft = e.clientX - self.drag.x,
scrollbarFactor = getScrollbarFactor();
self.$scalebar.css({
marginLeft: (marginLeft * scrollbarFactor) + 'px'
});
2011-04-22 22:03:10 +00:00
self.$content.css({
marginLeft: (marginLeft * scrollbarFactor) + 'px'
2011-04-22 22:03:10 +00:00
});
self.$scrollbar.css({
marginLeft: marginLeft + 'px'
2011-04-22 22:03:10 +00:00
});
}
function dragpauseScrollbar(event, e) {
dragafterScrollbar(e);
self.drag = {x: e.clientX};
}
function dragendScrollbar(event, e) {
dragafterScrollbar(e);
self.drag = null;
}
function dragafterScrollbar(e) {
// fixme: duplicated
self.$scalebar.css({marginLeft: 0});
self.$content.css({marginLeft: 0});
self.$scrollbar.css({marginLeft: 0});
2011-04-22 22:03:10 +00:00
self.options.date = new Date(
2011-05-26 18:49:48 +00:00
+self.options.date + (self.drag.x - e.clientX) * getSecondsPerPixel() * 1000 * getScrollbarFactor()
2011-04-22 22:03:10 +00:00
);
renderCalendar();
}
function getBackgroundElements(zoom) {
// fixme: duplicated (or at least similar to getTimelineElements)
var $elements = [],
units = getUnits(zoom),
n, value, width;
[1, 0].forEach(function(u) {
var unit = units[u],
value = unit.value(self.options.date),
width = Math.round(unit.seconds * getPixelsPerSecond(zoom)),
n = Math.ceil(self.options.width * 1.5/* * 16*/ / width);
Ox.loop(-n, n + 1, function(i) {
if (u == 0 || Ox.mod(value + i, 2)) {
$elements.push(
new Ox.Element()
.addClass(
u == 0 ? 'line' : ''
)
.css({
left: getPosition(unit.date(value + i), zoom) + 'px',
width: (u == 0 ? 1 : width) + 'px'
})
);
}
});
});
return $elements;
}
function getCalendarEvent(zoom) {
var ms = self.options.width * getSecondsPerPixel(zoom) * 1000;
2011-04-22 22:03:10 +00:00
return {
startTime: new Date(+self.options.date - ms / 2),
endTime: new Date(+self.options.date + ms / 2)
2011-04-22 22:03:10 +00:00
};
}
2011-05-25 09:22:16 +00:00
function getEventById(id) {
var event = null;
Ox.forEach(self.options.events, function(v) {
if (v.id == id) {
event = v;
2011-04-22 22:03:10 +00:00
return false;
}
});
2011-05-25 09:22:16 +00:00
return event;
2011-04-22 22:03:10 +00:00
}
function getEventCenter(event) {
return new Date(+event.startTime + getEventDuration(event) / 2);
}
function getEventDuration(event) {
return event.endTime - event.startTime;
}
2011-05-25 09:22:16 +00:00
function getEventElement(event, zoom) {
var left = Math.max(getPosition(event.startTime, zoom), -10000),
paddingLeft = (event.type && left < 0 ? -left : 0),
width = Ox.limit(getPosition(event.endTime, zoom) - left, 1, 20000) - paddingLeft;
2011-04-22 22:03:10 +00:00
return new Ox.Element()
.addClass('OxEvent' +
(event.type ? ' Ox' + Ox.toTitleCase(event.type) : '' ) +
2011-05-26 18:18:20 +00:00
(event.current ? ' OxCurrent' : '') +
(event.id == self.options.selected ? ' OxSelected' : '')
)
2011-04-22 22:03:10 +00:00
.css({
left: left + 'px',
width: width + 'px',
paddingLeft: paddingLeft + 'px'
2011-04-22 22:03:10 +00:00
})
.data({
2011-05-25 09:22:16 +00:00
id: event.id
2011-04-22 22:03:10 +00:00
})
.html('&nbsp;' + event.name + '&nbsp;')
2011-04-22 22:03:10 +00:00
}
2011-05-25 09:22:16 +00:00
function getEventElementById(id) {
var $element;
$('.OxLine > .OxEvent').each(function() {
var $this = $(this);
if ($this.data('id') == id) {
$element = $this;
return false;
}
});
return $element;
2011-05-25 09:22:16 +00:00
};
2011-04-22 22:03:10 +00:00
function getMouseDate(e) {
return new Date(+self.options.date + (
e.clientX - that.offset().left - self.options.width / 2 - 1
) * getSecondsPerPixel() * 1000);
}
function getOverlayWidths() {
var width = Math.round(self.options.width / getScrollbarFactor());
return [
Math.floor((self.options.width - width) / 2),
width,
Math.ceil((self.options.width - width) / 2),
];
}
2011-04-22 22:03:10 +00:00
function getPixelsPerSecond(zoom) {
return Math.pow(2, (
!Ox.isUndefined(zoom) ? zoom : self.options.zoom
) - (self.maxZoom - 4));
2011-04-22 22:03:10 +00:00
}
function getPosition(date, zoom) {
return Math.round(
self.options.width / 2 +
(date - self.options.date) / 1000 *
getPixelsPerSecond(zoom)
2011-04-22 22:03:10 +00:00
);
}
function getScrollbarFactor() {
return Math.pow(2, Math.min(self.options.zoom, 4));
}
2011-04-22 22:03:10 +00:00
function getSecondsPerPixel(zoom) {
return 1 / getPixelsPerSecond(zoom);
}
function getSelectedEvent() {
var event = null;
if (self.options.selected !== '') {
event = getEventById(self.options.selected);
}
return event;
}
function getSelectedEventElement() {
var $element = null;
if (self.options.selected !== '') {
$element = getEventElementById(self.options.selected);
}
return $element;
}
2011-04-22 22:03:10 +00:00
function getTimelineElements(zoom) {
var $elements = [],
unit = getUnits(zoom)[0],
value = unit.value(self.options.date),
width = unit.seconds * getPixelsPerSecond(zoom),
2011-04-22 22:03:10 +00:00
n = Math.ceil(self.options.width * 1.5/* * 16*/ / width);
2011-05-25 21:31:10 +00:00
//Ox.print(zoom, getUnits(zoom).map(function(u) {return u.name(value)}).join('/'))
//Ox.print('VALUE', value)
2011-04-22 22:03:10 +00:00
Ox.loop(-n, n + 1, function(i) {
$elements.push(
2011-05-25 09:22:16 +00:00
getEventElement({
2011-04-22 22:03:10 +00:00
name: unit.name(value + i),
startTime: unit.date(value + i),
endTime: unit.date(value + i + 1)
2011-04-22 22:03:10 +00:00
}, zoom)
.addClass(Ox.mod(value + i, 2) == 0 ? 'even' : 'odd')
);
});
return $elements;
}
function getUnits(zoom) {
// returns array of 2 units
// units[0] for timeline
// units[1] for background
var pixelsPerSecond = getPixelsPerSecond(zoom),
units;
self.units.reverse();
2011-04-22 22:03:10 +00:00
Ox.forEach(self.units, function(v, i) {
width = Math.round(v.seconds * pixelsPerSecond);
if (width >= self.minLabelWidth) {
units = [self.units[i], self.units[i - 1]];
return false;
}
});
self.units.reverse();
2011-04-22 22:03:10 +00:00
return units;
}
function mouseleave() {
self.$tooltip.hide();
}
function mousemove(e) {
var $target = $(e.target),
2011-05-25 09:22:16 +00:00
event, title;
if ($target.is('.OxLine > .OxEvent')) {
event = getEventById($target.data('id'));
title = '<span class="OxBright">' + event.name + '</span><br/>' +
event.rangeText + '<br>' + event.durationText;
2011-04-22 22:03:10 +00:00
} else {
title = Ox.formatDate(getMouseDate(e), '%a, %b %e, %x, %H:%M:%S', true);
2011-04-22 22:03:10 +00:00
}
self.$tooltip.options({
title: title
})
.show(e.clientX, e.clientY);
}
function mousewheel(e, delta, deltaX, deltaY) {
//Ox.print('mousewheel', delta, deltaX, deltaY);
var deltaZ = 0;
if (!self.mousewheel && deltaY && Math.abs(deltaY) > Math.abs(deltaX)) {
if (deltaY < 0 && self.options.zoom > 0) {
deltaZ = -1
} else if (deltaY > 0 && self.options.zoom < self.maxZoom) {
deltaZ = 1
}
if (deltaZ) {
self.options.date = deltaZ == -1 ?
new Date(2 * +self.options.date - +getMouseDate(e)) :
new Date((+self.options.date + +getMouseDate(e)) / 2)
zoomBy(deltaZ);
2011-04-22 22:03:10 +00:00
}
}
self.mousewheel = true;
setTimeout(function() {
self.mousewheel = false;
}, 250);
}
2011-05-25 09:22:16 +00:00
function overlaps(eventA, eventB) {
2011-04-22 22:03:10 +00:00
return (
eventA.startTime >= eventB.startTime && eventA.startTime < eventB.endTime
2011-04-22 22:03:10 +00:00
) || (
eventB.startTime >= eventA.startTime && eventB.startTime < eventA.endTime
2011-04-22 22:03:10 +00:00
);
}
function panBy(ms) {
panTo(new Date(+self.options.date + ms));
}
function panTo(date, line) {
var delta = (date - self.options.date) / 1000 * getPixelsPerSecond(),
// 250 ms for half the width of the visible area
ms = 250 * Math.min(Math.abs(delta) / (self.$content.width() / 2), 1);
self.$scalebar.animate({
marginLeft: -delta + 'px'
}, ms);
self.$content.animate({
marginLeft: -delta + 'px'
}, ms, function() {
self.$scalebar.stop().css({marginLeft: 0});
self.$content.css({marginLeft: 0});
self.$scrollbar.stop().css({marginLeft: 0});
self.options.date = date;
renderCalendar();
});
self.$scrollbar.animate({
marginLeft: -delta / getScrollbarFactor() + 'px'
}, ms);
if (!Ox.isUndefined(line)) {
scrollTo(line * 16 + 8 - self.$container.height() / 2, true);
}
};
function panToSelected() {
if (self.options.selected !== '') {
Ox.print('sos', self.options.selected, getSelectedEventElement())
var line = getSelectedEventElement().data('line');
panTo(getEventCenter(getSelectedEvent()), line);
}
}
function renderBackground() {
getBackgroundElements(self.options.zoom).forEach(function($element) {
$element.appendTo(self.$background);
});
}
2011-04-22 22:03:10 +00:00
function renderCalendar() {
$('.OxBackground').empty();
2011-05-25 09:22:16 +00:00
$('.OxEvent').remove();
2011-04-22 22:03:10 +00:00
renderBackground();
renderTimelines();
renderOverlay();
2011-05-25 09:22:16 +00:00
renderEvents();
self.$dateInput.options({
value: Ox.formatDate(self.options.date, '%Y-%m-%d %H:%M:%S', true)
});
2011-04-22 22:03:10 +00:00
}
2011-05-25 09:22:16 +00:00
function renderEvents() {
var calendarEvent = getCalendarEvent(),
height,
lineEvents = [];
//types = ['date', 'place', 'person', 'other'];
2011-05-25 09:22:16 +00:00
self.options.events.filter(function(event) {
// filter out events with types not shown
// and events outside the visible area <-- commented out to keep layout from changing
return self.options.showTypes.indexOf(event.type) > -1
/*&& overlaps(event, calendarEvent)*/;
2011-04-22 22:03:10 +00:00
}).sort(function(a, b) {
// sort events
if (a.type == 'date' && b.type != 'date') {
return -1;
} else if (a.type != 'date' && b.type == 'date') {
return 1;
} else if (a.startTime < b.startTime || a.startTime > b.startTime) {
return a.startTime - b.startTime;
} else {
return (b.endTime - b.startTime) - (a.endTime - a.startTime);
}
2011-05-25 09:22:16 +00:00
}).forEach(function(event, i) {
var line = lineEvents.length;
2011-04-22 22:03:10 +00:00
// traverse lines
2011-05-25 09:22:16 +00:00
Ox.forEach(lineEvents, function(events, line_) {
2011-04-22 22:03:10 +00:00
var fits = true;
2011-05-25 09:22:16 +00:00
// traverse events in line
Ox.forEach(events, function(event_) {
2011-04-22 22:03:10 +00:00
// if overlaps, check next line
2011-05-25 09:22:16 +00:00
if (overlaps(event, event_)) {
2011-04-22 22:03:10 +00:00
fits = false;
return false;
}
});
if (fits) {
line = line_;
return false;
}
});
2011-05-25 09:22:16 +00:00
if (line == lineEvents.length) {
lineEvents[line] = [];
2011-04-22 22:03:10 +00:00
}
2011-05-25 09:22:16 +00:00
lineEvents[line].push(event);
2011-04-22 22:03:10 +00:00
});
self.$content.find('.OxLine').remove();
height = lineEvents.length * 16;
self.$background.$element.children().css({height: height + 'px'});
self.$content.css({height: height + 'px'});
2011-05-25 09:22:16 +00:00
lineEvents.forEach(function(events, line) {
2011-04-22 22:03:10 +00:00
var $line = new Ox.Element()
.addClass('OxLine')
.css({
top: (line * 16) + 'px'
2011-04-22 22:03:10 +00:00
})
.appendTo(self.$content);
2011-05-25 09:22:16 +00:00
events.sort(function(a, b) {
// sort events by start, ascending
return a.startTime - b.startTime;
2011-05-25 09:22:16 +00:00
}).forEach(function(event) {
// append if selected or visible
if (
event.id == self.options.selected
|| overlaps(event, calendarEvent)
) {
getEventElement(event).data({line: line}).appendTo($line);
}
2011-04-22 22:03:10 +00:00
});
});
}
function renderOverlay() {
var widths = getOverlayWidths();
that.find('.OxOverlay').remove();
Ox.Element()
.addClass('OxOverlay')
.css({
bottom: '16px'
})
.append(
$('<div>').css({
width: widths[0] + 'px'
})
)
.append(
$('<div>').css({
left: widths[0] + 'px',
width: widths[1] + 'px'
})
)
.append(
$('<div>').css({
left: (widths[0] + widths[1]) + 'px',
width: widths[2] + 'px'
})
)
.bindEvent({
dragstart: dragstartScrollbar,
drag: dragScrollbar,
dragpause: dragpauseScrollbar,
dragend: dragendScrollbar
})
.appendTo(that);
}
2011-04-22 22:03:10 +00:00
function renderTimelines() {
Ox.print(self.options.zoom, Math.max(self.options.zoom - 4, 0))
getTimelineElements(self.options.zoom).forEach(function($element) {
$element.appendTo(self.$scalebar.$element);
});
getTimelineElements(Math.max(self.options.zoom - 4, 0)).forEach(function($element) {
$element.appendTo(self.$scrollbar.$element);
});
}
function scrollBy(delta) {
scrollTo(
self.$container.$element[0].scrollTop
+ delta * self.$container.height() / 2, true
);
}
function scrollTo(top, animate) {
var containerHeight = self.$container.height(),
contentHeight = self.$content.height(),
scrollTop = self.$container.$element[0].scrollTop,
min = 0,
max = Math.ceil(contentHeight - containerHeight / 2),
top = Ox.limit(top, min, max),
delta = top - scrollTop,
ms = 250 * Math.min(Math.abs(delta) / (containerHeight / 2), 1);
if (animate) {
self.$container.animate({
scrollTop: top
}, ms);
} else {
self.$container.$element[0].scrollTop = top;
}
Ox.print('scrollTo', top)
}
function selectEvent(id, $element) {
self.$content.find('.OxSelected').removeClass('OxSelected');
if (id) {
self.options.selected = id;
($element || getEventElementById(id)).addClass('OxSelected');
// fixme: map event should also be 'select', not 'selectplace'
that.triggerEvent('select', {id: id});
} else {
if (self.options.selected !== '') {
self.options.selected = '';
that.triggerEvent('select', {id: ''});
}
}
2011-05-25 09:22:16 +00:00
}
2011-04-22 22:03:10 +00:00
function singleclick(event, e) {
2011-05-25 09:22:16 +00:00
var $target = $(e.target),
id = $target.data('id');
if ($target.is('.OxLine > .OxEvent')) {
if (id == self.options.selected) {
panToSelected();
} else {
selectEvent(id, $target);
}
2011-05-25 09:22:16 +00:00
} else {
selectEvent('');
panTo(getMouseDate(e));
}
}
function zoomBy(delta) {
zoomTo(self.options.zoom + delta);
}
function zoomTo(zoom) {
self.options.zoom = Ox.limit(zoom, 0, self.maxZoom);
self.$zoomInput.options({value: self.options.zoom});
renderCalendar();
}
function zoomToSelected() {
if (self.options.selected !== '') {
var event = getSelectedEvent(),
eventDuration = getEventDuration(event),
zoom = getZoom();
zoom != self.options.zoom && zoomTo(zoom);
panToSelected();
}
function getZoom() {
var zoom;
Ox.loop(self.maxZoom, 0, function(z) {
var calendarDuration = getEventDuration(getCalendarEvent(z));
if (calendarDuration > eventDuration) {
zoom = z;
return false;
}
});
return zoom;
2011-04-22 22:03:10 +00:00
}
}
2011-04-29 12:40:51 +00:00
self.setOption = function(key, val) {
2011-04-22 22:03:10 +00:00
if (key == 'date') {
} else if (key == 'zoom') {
}
};
return that;
};