0.6
0*60
24*60
0
24
this.relayout();
aDayEndHour ||
aDayEndHour * 60 > this.mEndMin) {
throw Components.results.NS_ERROR_INVALID_ARG;
}
if (this.mDayStartHour != aDayStartHour ||
this.mDayEndHour != aDayEndHour) {
this.mDayEndHour = aDayEndHour;
this.mDayStartHour = aDayStartHour;
var topbox = document.getAnonymousElementByAttribute(this, "anonid", "topbox");
if (topbox.childNodes.length) {
// This only needs to be done if the initial relayout has
// already happened, otherwise it will be done then.
for (var hour = this.mStartMin / 60; hour < this.mEndMin / 60; hour++) {
if (hour < this.mDayStartHour || hour >= this.mDayEndHour) {
topbox.childNodes[hour].setAttribute("off-time", "true");
} else {
topbox.childNodes[hour].removeAttribute("off-time");
}
}
}
}
]]>
0) {
var dur;
if (this.mEndMin - theMin < 60) {
dur = this.mEndMin - theMin;
} else {
dur = theMin % 60;
}
theMin += dur;
if (dur == 0) dur = 60;
// calculate duration pixel as the difference between
// start pixel and end pixel to avoid rounding errors.
var startPix = Math.round(theMin * this.mPixPerMin);
var endPix = Math.round((theMin + dur) * this.mPixPerMin);
var durPix = endPix - startPix;
var box;
if (dur != 60) {
box = makeTimeBox("", durPix);
} else {
timeString = formatter.FormatTime("",
Components.interfaces.nsIScriptableDateFormat.timeFormatNoSeconds,
theHour, 0, 0);
box = makeTimeBox(timeString, durPix);
}
// Set up workweek hours
if (theHour < this.mDayStartHour || theHour >= this.mDayEndHour) {
box.setAttribute("off-time", "true");
}
box.setAttribute("class", "calendar-time-bar-box-" + (theHour % 2 == 0 ? "even" : "odd"));
topbox.appendChild(box);
durLeft -= dur;
theMin += dur;
theHour++;
}
]]>
0.6
0*60
24*60
8*60
17*60
new Array()
null
null
null
null
null
0
null
new Array()
false
null
new Object()
false
null
null
false
[]
0)
return;
this.clear();
let orient = this.getAttribute("orient");
this.bgbox.setAttribute("orient", orient);
// bgbox is used mainly for drawing the grid. at some point it may
// also be used for all-day events.
let otherorient = getOtherOrientation(orient);
let configBox = document.getAnonymousElementByAttribute(this, "anonid", "config-box");
configBox.removeAttribute("hidden");
let minSize = configBox.getOptimalMinSize();
configBox.setAttribute("hidden", "true");
this.mMinDuration = Components.classes["@mozilla.org/calendar/duration;1"]
.createInstance(Components.interfaces.calIDuration);
this.mMinDuration.minutes = parseInt(minSize/this.mPixPerMin);
let theMin = this.mStartMin;
while (theMin < this.mEndMin) {
let dur = theMin % 60;
theMin += dur;
if (dur == 0) dur = 60;
let box = createXULElement("spacer");
// we key off this in a CSS selector
box.setAttribute("orient", orient);
box.setAttribute("class", "calendar-event-column-linebox");
if (this.mSelected) {
box.setAttribute("selected", "true");
}
if (this.mDayOff) {
box.setAttribute("weekend", "true");
}
if (theMin < this.mDayStartMin || theMin >= this.mDayEndMin) {
box.setAttribute("off-time", "true");
}
// Carry forth the day relation
box.setAttribute("relation", this.getAttribute("relation"));
// calculate duration pixel as the difference between
// start pixel and end pixel to avoid rounding errors.
let startPix = Math.round(theMin * this.mPixPerMin);
let endPix = Math.round((theMin + dur) * this.mPixPerMin);
let durPix = endPix - startPix;
if (orient == "vertical")
box.setAttribute("height", durPix);
else
box.setAttribute("width", durPix);
box.setAttribute("style", "min-width: 1px; min-height: 1px;");
this.bgbox.appendChild(box);
theMin += 60;
}
// fgbox is used for dragging events
this.fgboxes.box.setAttribute("orient", orient);
document.getAnonymousElementByAttribute(this, "anonid", "fgdragspacer").setAttribute("orient", orient);
// this one is set to otherorient, since it will contain
// child boxes set to "orient" (one for each set of
// overlapping event areas)
this.topbox.setAttribute("orient", otherorient);
this.mEventMap = this.computeEventMap();
this.mEventBoxes = new Array();
if (!this.mEventMap.length) {
return;
}
// First of all we create a xul:stack which
// will hold all events for this event column.
// The stack will be grouped below .../calendar-event-column/stack/topbox.
let stack = createXULElement("stack");
stack.setAttribute("flex", "1");
this.topbox.appendChild(stack);
let boxToEdit;
let columnCount = 1;
let spanTotal = 0;
for each (let layer in this.mEventMap) {
// The event-map (this.mEventMap) contains an array of layers.
// For each layer we create a box below the stack just created above.
// So each different layer lives in a box that's contained in the stack.
let xulColumn = createXULElement("box");
xulColumn.setAttribute("orient", otherorient);
xulColumn.setAttribute("flex", "1");
xulColumn.setAttribute("style", "min-width: 1px; min-height: 1px;");
stack.appendChild(xulColumn);
let numBlocksInserted = 0;
// column count determined by layer with no special span columns
if (layer.every(function (e) !e.specialSpan)) {
columnCount = layer.length;
}
spanTotal = 0;
// Each layer contains a list of the columns that
// need to be created for a span.
for each (let column in layer) {
let innerColumn = createXULElement("box");
innerColumn.setAttribute("orient", orient);
let colFlex = column.specialSpan ? (columnCount * column.specialSpan) : 1;
innerColumn.setAttribute("flex", colFlex);
spanTotal += colFlex;
innerColumn.style.minWidth = "1px";
innerColumn.style.minHeight = "1px";
innerColumn.style.width = colFlex + "px";
innerColumn.style.height = colFlex + "px";
xulColumn.appendChild(innerColumn);
let curTime = 0;
for each (let chunk in column) {
var duration = chunk.duration;
if (!duration) {
continue;
}
if (chunk.event) {
let chunkBox = createXULElement("calendar-event-box");
let durMinutes = duration.inSeconds / 60;
let size = Math.max(durMinutes * this.mPixPerMin, minSize);
if (orient == "vertical") {
chunkBox.setAttribute("height", size);
} else {
chunkBox.setAttribute("width", size);
}
chunkBox.setAttribute("context",
this.getAttribute("item-context") ||
this.getAttribute("context"));
chunkBox.setAttribute("orient", orient);
// Set the gripBars visibility in the chunk. Keep it
// hidden for tasks with only entry date OR due date.
if ((chunk.event.entryDate || !chunk.event.dueDate) &&
(!chunk.event.entryDate || chunk.event.dueDate)) {
let startGripVisible = (chunk.event.startDate || chunk.event.entryDate)
.compare(chunk.startDate) == 0;
let endGripVisible = (chunk.event.endDate || chunk.event.dueDate)
.compare(chunk.endDate) <= 0;
if (startGripVisible && endGripVisible) {
chunkBox.setAttribute("gripBars", "both");
} else if (endGripVisible) {
chunkBox.setAttribute("gripBars", "end");
} else if (startGripVisible) {
chunkBox.setAttribute("gripBars", "start");
}
}
innerColumn.appendChild(chunkBox);
chunkBox.calendarView = this.calendarView;
chunkBox.occurrence = chunk.event;
chunkBox.parentColumn = this;
if (chunk.event.hashId in this.mSelectedItemIds) {
chunkBox.selected = true;
this.mSelectedChunks.push(chunkBox);
}
this.mEventBoxes.push(chunkBox);
if (this.mEventToEdit &&
chunkBox.occurrence.hashId == this.mEventToEdit.hashId) {
boxToEdit = chunkBox;
}
} else {
let chunkBox = createXULElement("spacer");
chunkBox.setAttribute("context", this.getAttribute("context"));
chunkBox.setAttribute("style", "min-width: 1px; min-height: 1px;");
chunkBox.setAttribute("orient", orient);
chunkBox.setAttribute("class", "calendar-empty-space-box");
innerColumn.appendChild(chunkBox);
let durMinutes = duration.inSeconds / 60;
if (orient == "vertical") {
chunkBox.setAttribute("height", durMinutes * this.mPixPerMin);
} else {
chunkBox.setAttribute("width", durMinutes * this.mPixPerMin);
}
}
}
numBlocksInserted++;
curTime += duration;
}
// add last empty column if necessary
if (spanTotal < columnCount) {
let lastColumn = createXULElement("box");
lastColumn.setAttribute("orient", orient);
lastColumn.setAttribute("flex", columnCount - spanTotal);
lastColumn.style.minWidth = "1px";
lastColumn.style.minHeight = "1px";
lastColumn.style.width = (columnCount - spanTotal) + "px";
lastColumn.style.height = (columnCount - spanTotal) + "px";
xulColumn.appendChild(lastColumn);
}
if (boxToEdit) {
this.mCreatedNewEvent = false;
this.mEventToEdit = null;
boxToEdit.startEditing();
}
if (numBlocksInserted == 0) {
// if we didn't insert any blocks, then
// forget about this column
xulColumn.remove();
}
}
]]>
end.nativeTime) {
aEventInfo.layoutEnd = secEnd;
} else {
aEventInfo.layoutEnd = end;
}
return aEventInfo;
});
this.mEventInfos.sort(sortByStart);
// The end time of the last ending event in the entire blob
var latestItemEnd;
// This array keeps track of the last (latest ending) item in each of
// the columns of the current blob. We could reconstruct this data at
// any time by looking at the items in the blob, but that would hurt
// perf.
var colEndArray = new Array();
/* Go through a 3 step process to try and place each item.
* Step 1: Look for an existing column with room for the item.
* Step 2: Look for a previously placed item that can be shrunk in
* width to make room for the item.
* Step 3: Give up and create a new column for the item.
*
* (The steps are explained in more detail as we come to them)
*/
for (var i in this.mEventInfos) {
var curItemInfo = {event: this.mEventInfos[i].event,
layoutStart: this.mEventInfos[i].layoutStart,
layoutEnd: this.mEventInfos[i].layoutEnd};
if (!latestItemEnd) {
latestItemEnd = curItemInfo.layoutEnd;
}
if (currentBlob.length && latestItemEnd &&
curItemInfo.layoutStart.compare(latestItemEnd) != -1) {
// We're done with this current blob because item starts
// after the last event in the current blob ended.
blobs.push({blob: currentBlob, totalCols: colEndArray.length});
// Reset our variables
currentBlob = new Array();
colEndArray = new Array();
}
// Place the item in its correct place in the blob
var placedItem = false;
// Step 1
// Look for a possible column in the blob that has been left open. This
// would happen if we already have multiple columns but some of
// the cols have events before latestItemEnd. For instance
// | | |
// |______| |
// |ev1 |______|
// | |ev2 |
// |______| |
// | | |
// |OPEN! | |<--Our item's start time might be here
// | |______|
// | | |
//
// Remember that any time we're starting a new blob, colEndArray
// will be empty, but that's ok.
for (var ii = 0; ii < colEndArray.length; ++ii) {
var colStart = colEndArray[ii].layoutStart;
var colEnd = colEndArray[ii].layoutEnd;
if (colEnd.compare(curItemInfo.layoutStart) != 1) {
// Yay, we can jump into this column
colEndArray[ii] = curItemInfo;
// Check and see if there are any adjacent columns we can
// jump into as well.
var lastCol = Number(ii) + 1;
while (lastCol < colEndArray.length) {
var nextColStart = colEndArray[lastCol].layoutStart;
var nextColEnd = colEndArray[lastCol].layoutEnd;
// If the next column's item ends after we start, we
// can't expand any further
if (nextColEnd.compare(curItemInfo.layoutStart) == 1) {
break;
}
colEndArray[lastCol] = curItemInfo;
lastCol++;
}
// Now construct the info we need to push into the blob
currentBlob.push({itemInfo: curItemInfo,
startCol: ii,
colSpan: lastCol - ii});
// Update latestItemEnd
if (latestItemEnd &&
curItemInfo.layoutEnd.compare(latestItemEnd) == 1) {
latestItemEnd = curItemInfo.layoutEnd;
}
placedItem = true;
break; // Stop iterating through colEndArray
}
}
if (placedItem) {
// Go get the next item
continue;
}
// Step 2
// OK, all columns (if there are any) overlap us. Look if the
// last item in any of the last items in those columns is taking
// up 2 or more cols. If so, shrink it and stick the item in the
// created space. For instance
// |______|______|______|
// |ev1 |ev3 |ev4 |
// | | | |
// | |______| |
// | | |______|
// | |_____________|
// | |ev2 |
// |______| |<--If our item's start time is
// | |_____________| here, we can shrink ev2 and jump
// | | | | in column #3
//
for (var jj=1; jj
= layers.length) {
layers.push([]);
}
while (data.startCol >= layers[layerIndex].length) {
layers[layerIndex].push([]);
if (specialSpan) {
layers[layerIndex][layers[layerIndex].length - 1].specialSpan = 1 / glob.totalCols;
}
}
// we now retrieve the column from 'layerIndex' and 'startCol'.
var col = layers[layerIndex][data.startCol];
if (specialSpan) {
col.specialSpan = specialSpan;
}
// take into account that items can span several days.
// that's why i'm clipping the start- and end-time to the
// timespan of this column.
var start = data.itemInfo.layoutStart;
var end = data.itemInfo.layoutEnd;
if (start.year != this.date.year ||
start.month != this.date.month ||
start.day != this.date.day) {
start = start.clone();
start.resetTo(this.date.year,
this.date.month,
this.date.day,
0,this.mStartMin,0,
start.timezone);
}
if (end.year != this.date.year ||
end.month != this.date.month ||
end.day != this.date.day) {
end = end.clone();
end.resetTo(this.date.year,
this.date.month,
this.date.day,
0,this.mEndMin,0,
end.timezone);
}
var prevEnd;
if (col.length > 0) {
// Fill in time gaps with a placeholder
prevEnd = col[col.length - 1].endDate.clone();
} else {
// First event in the column, add a placeholder for the
// blank time from this.mStartMin to the event's start
prevEnd = start.clone();
prevEnd.hour = 0;
prevEnd.minute = this.mStartMin;
}
prevEnd.timezone = floating();
// the reason why we need to calculate time durations
// based on floating timezones is that we need avoid
// dst gaps in this case. converting the date/times to
// floating conveys this idea in a natural way. note that
// we explicitly don't use getInTimezone() as it would
// be slightly more expensive in terms of performance.
var floatstart = start.clone();
floatstart.timezone = floating();
var dur = floatstart.subtractDate(prevEnd);
if (dur.inSeconds) {
col.push({duration: dur});
}
var floatend = end.clone();
floatend.timezone = floating();
col.push({event: data.itemInfo.event,
endDate: end,
startDate: start,
duration: floatend.subtractDate(floatstart)});
}
layerOffset = layers.length;
}
return layers;
]]>
selected shadows
startMin: startMin, // First shadow start minute
endMin: aEnd % this.mEndMin // Last shadow end minute
};
]]>
0) {
firstCol = firstCol.previousSibling;
firstIndex--;
}
let lastShadow = (aShadows != null ? aShadows : this.mDragState.shadows);
while (lastCol.nextSibling && lastIndex < lastShadow - 1) {
lastCol = lastCol.nextSibling;
lastIndex++;
}
// returns first and last column with shadows that are visible in the
// week and the positions of these (visible) columns in the set of
// columns shadows of the occurrence
return { firstCol: firstCol,
firstIndex: firstIndex,
lastCol: lastCol,
lastIndex: lastIndex };
]]>
aCurrentOffset && firstCol.previousSibling) {
firstCol.previousSibling.fgboxes.dragbox.removeAttribute("dragging");
firstCol.previousSibling.fgboxes.box.removeAttribute("dragging");
}
let currentOffsetEndSide = aCurrentShadows - 1 - aCurrentOffset;
if ((this.mDragState.shadows - 1 - this.mDragState.offset) > currentOffsetEndSide &&
lastCol.nextSibling) {
lastCol.nextSibling.fgboxes.dragbox.removeAttribute("dragging");
lastCol.nextSibling.fgboxes.box.removeAttribute("dragging");
}
}
// set shadow boxes size for every part of the occurrence
let firstShadowSize = (aCurrentShadows == 1) ? aEnd - aStart : this.mEndMin - aStart;
let column = firstCol;
for (let i = firstIndex; column && i <= lastIndex; i++) {
column.fgboxes.box.setAttribute("dragging", "true");
column.fgboxes.dragbox.setAttribute("dragging", "true");
if (i == 0) {
// first shadow
column.fgboxes.dragspacer.setAttribute(aSizeattr, aStart * column.mPixPerMin);
column.fgboxes.dragbox.setAttribute(aSizeattr, firstShadowSize * column.mPixPerMin);
} else if (i == (aCurrentShadows - 1)) {
// last shadow
column.fgboxes.dragspacer.setAttribute(aSizeattr, 0);
column.fgboxes.dragbox.setAttribute(aSizeattr, aEnd * column.mPixPerMin);
} else {
// an intermediate shadow (full day)
column.fgboxes.dragspacer.setAttribute(aSizeattr, 0);
column.fgboxes.dragbox.setAttribute(aSizeattr, this.mEndMin * column.mPixPerMin);
}
column = column.nextSibling;
}
]]>
(event.target.boxObject.x + event.target.boxObject.width) ||
event.clientY < (event.target.boxObject.y) ||
event.clientY > (event.target.boxObject.y + event.target.boxObject.height)) {
// Remove the drag state
for (let column = firstCol, i = firstIndex;
column && i < col.mDragState.shadows;
column = column.nextSibling, i++) {
column.fgboxes.dragbox.removeAttribute("dragging");
column.fgboxes.box.removeAttribute("dragging");
}
window.removeEventListener("mousemove", col.onEventSweepMouseMove, false);
window.removeEventListener("mouseup", col.onEventSweepMouseUp, false);
window.removeEventListener("keypress", col.onEventSweepKeypress, false);
document.calendarEventColumnDragging = null;
col.mDragState = null;
// the multiday view currently exhibits a less than optimal strategy
// in terms of item selection. items don't get automatically selected
// when clicked and dragged, as to differentiate inline editing from
// the act of selecting an event. but the application internal drop
// targets will ask for selected items in order to pull the data from
// the packets. that's why we need to make sure at least the currently
// dragged event is contained in the set of selected items.
let selectedItems = this.getSelectedItems({});
if (!selectedItems.some(
function (aItem) {
return (aItem.hashId == item.hashId);
})) {
col.calendarView.setSelectedItems(1,
[event.ctrlKey ? item.parentItem : item]);
}
invokeEventDragSession(dragState.dragOccurrence, col);
return;
}
col.fgboxes.box.setAttribute("dragging", "true");
col.fgboxes.dragbox.setAttribute("dragging", "true");
let minutesInDay = col.mEndMin - col.mStartMin;
// check if we need to jump a column
let jumpedColumns;
let newcol = col.calendarView.findColumnForClientPoint(event.screenX, event.screenY);
if (newcol && newcol != col) {
// Find how many columns we are jumping by subtracting the dates.
let dur = newcol.mDate.subtractDate(col.mDate);
jumpedColumns = dur.days;
jumpedColumns *= dur.isNegative ? -1 : 1;
if (dragState.dragType == "modify-start") {
// prevent dragging the start date after the end date in a new column
if ((dragState.limitEndMin - minutesInDay * jumpedColumns) < 0) {
return;
}
dragState.limitEndMin -= minutesInDay * jumpedColumns;
} else if (dragState.dragType == "modify-end") {
// prevent dragging the end date before the start date in a new column
if ((dragState.limitStartMin - minutesInDay * jumpedColumns) > minutesInDay) {
return;
}
dragState.limitStartMin -= minutesInDay * jumpedColumns;
} else if (dragState.dragType == "new") {
dragState.limitEndMin -= minutesInDay * jumpedColumns;
dragState.limitStartMin -= minutesInDay * jumpedColumns;
dragState.jumpedColumns += jumpedColumns;
}
// kill our drag state
for (let column = firstCol, i = firstIndex;
column && i < col.mDragState.shadows;
column = column.nextSibling, i++) {
column.fgboxes.dragbox.removeAttribute("dragging");
column.fgboxes.box.removeAttribute("dragging");
}
// jump ship
newcol.acceptInProgressSweep(dragState);
// restart event handling
col.onEventSweepMouseMove(event);
return;
}
let mousePos;
let sizeattr;
if (col.getAttribute("orient") == "vertical") {
mousePos = event.screenY - col.parentNode.boxObject.screenY;
sizeattr = "height";
} else {
mousePos = event.screenX - col.parentNode.boxObject.screenX;
sizeattr = "width";
}
// don't let mouse position go outside the window edges
let pos = Math.max(0, mousePos) - dragState.mouseOffset;
// snap interval: 15 minutes or 1 minute if modifier key is pressed
let snapIntMin = (event.shiftKey &&
!event.ctrlKey &&
!event.altKey &&
!event.metaKey) ? 1 : 15;
let interval = col.mPixPerMin * snapIntMin;
let curmin = Math.floor(pos / interval) * snapIntMin;
let deltamin = curmin - dragState.origMin;
let shadowElements;
if (dragState.dragType == "new") {
// Extend deltamin in a linear way over the columns
deltamin += minutesInDay * dragState.jumpedColumns;
if (deltamin < 0) {
// create a new event modifying the start. End time is fixed
shadowElements = {shadows: 1 - dragState.jumpedColumns,
offset: 0,
startMin: curmin,
endMin: dragState.origMin};
} else {
// create a new event modifying the end. Start time is fixed
shadowElements = {shadows: dragState.jumpedColumns + 1,
offset: dragState.jumpedColumns,
startMin: dragState.origMin,
endMin: curmin};
}
dragState.startMin = shadowElements.startMin;
dragState.endMin = shadowElements.endMin;
} else if (dragState.dragType == "move") {
// if we're moving, we modify startMin and endMin of the shadow.
shadowElements = col.getShadowElements(dragState.origMinStart + deltamin,
dragState.origMinEnd + deltamin);
dragState.startMin = shadowElements.startMin;
dragState.endMin = shadowElements.endMin;
// Keep track of the last start position because it will help to
// build the event at the end of the drag session.
dragState.lastStart = dragState.origMinStart + deltamin;
} else if (dragState.dragType == "modify-start") {
// if we're modifying the start, the end time is fixed.
shadowElements = col.getShadowElements(dragState.origMin + deltamin, dragState.limitEndMin);
dragState.startMin = shadowElements.startMin;
dragState.endMin = shadowElements.endMin;
// but we need to not go past the end; if we hit
// the end, then we'll clamp to the previous snap interval minute
if (dragState.startMin >= dragState.limitEndMin) {
dragState.startMin = Math.ceil((dragState.limitEndMin - snapIntMin) / snapIntMin) * snapIntMin;
}
} else if (dragState.dragType == "modify-end") {
// if we're modifying the end, the start time is fixed.
shadowElements = col.getShadowElements(dragState.limitStartMin, dragState.origMin + deltamin);
dragState.startMin = shadowElements.startMin;
dragState.endMin = shadowElements.endMin;
// but we need to not go past the start; if we hit
// the start, then we'll clamp to the next snap interval minute
if (dragState.endMin <= dragState.limitStartMin) {
dragState.endMin = Math.floor((dragState.limitStartMin + snapIntMin) / snapIntMin) * snapIntMin;
}
}
let currentOffset = shadowElements.offset;
let currentShadows = shadowElements.shadows;
// now we can update the shadow boxes position and size
col.updateShadowsBoxes(dragState.startMin, dragState.endMin,
currentOffset, currentShadows,
sizeattr);
// update the labels
lateralColumns = col.firstLastShadowColumns(currentOffset, currentShadows);
col.updateDragLabels(lateralColumns.firstCol, lateralColumns.lastCol);
col.mDragState.offset = currentOffset;
col.mDragState.shadows = currentShadows;
]]>
0);
newStart = draggedForward ? startDay.clone() : dragDay.clone();
newEnd = draggedForward ? dragDay.clone() : startDay.clone();
newStart.isDate = false;
newEnd.isDate = false;
newStart.resetTo(newStart.year, newStart.month, newStart.day,
0, dragState.startMin + col.mStartMin, 0,
newStart.timezone);
newEnd.resetTo(newEnd.year, newEnd.month, newEnd.day,
0, dragState.endMin + col.mStartMin, 0,
newEnd.timezone);
// Edit the event title on the first of the new event's occurrences
if (draggedForward) {
dragState.origColumn.mCreatedNewEvent = true;
} else {
col.mCreatedNewEvent = true;
}
} else if (dragState.dragType == "move") {
// Figure out the new date-times of the event by adding the duration
// of the total movement (days and minutes) to the old dates.
let duration = dragDay.subtractDate(dragState.origColumn.mDate);
let minutes = dragState.lastStart - dragState.realStart;
// Since both boxDate and beginMove are dates (note datetimes),
// subtractDate will only give us a non-zero number of hours on
// DST changes. While strictly speaking, subtractDate's behavior
// is correct, we need to move the event a discrete number of
// days here. There is no need for normalization here, since
// addDuration does the job for us. Also note, the duration used
// here is only used to move over multiple days. Moving on the
// same day uses the minutes from the dragState.
if (duration.hours == 23) {
// entering DST
duration.hours++;
} else if (duration.hours == 1) {
// leaving DST
duration.hours--;
}
if (duration.isNegative) {
// Adding negative minutes to a negative duration makes the
// duration more positive, but we want more negative, and
// vice versa.
minutes *= -1;
}
duration.minutes = minutes;
duration.normalize();
newStart.addDuration(duration);
newEnd.addDuration(duration);
}
// If we tweaked tzs, put times back in their original ones
if (startTZ) {
newStart = newStart.getInTimezone(startTZ);
}
if (endTZ) {
newEnd = newEnd.getInTimezone(endTZ);
}
if (dragState.dragType == "new") {
// We won't pass a calendar, since the display calendar is the
// composite anyway. createNewEvent() will use the selected
// calendar.
// TODO We might want to get rid of the extra displayCalendar
// member.
col.calendarView.controller.createNewEvent(null,
newStart,
newEnd);
} else if (dragState.dragType == "move" ||
dragState.dragType == "modify-start" ||
dragState.dragType == "modify-end")
{
col.calendarView.controller.modifyOccurrence(dragState.dragOccurrence,
newStart, newEnd);
}
document.calendarEventColumnDragging = null;
col.mDragState = null;
]]>
aDayEndMin ||
aDayEndMin > this.mEndMin) {
throw Components.results.NS_ERROR_INVALID_ARG;
}
if (this.mDayStartMin != aDayStartMin ||
this.mDayEndMin != aDayEndMin) {
this.mDayStartMin = aDayStartMin;
this.mDayEndMin = aDayEndMin;
}
]]>
null
textContent so it can wrap.
evl.textContent = item.title;
} else {
evl.textContent = calGetString("calendar", "eventUntitled");
}
var gripbar = document.getAnonymousElementByAttribute(this, "anonid", "gripbar1").boxObject.height;
var height = document.getAnonymousElementByAttribute(this, "anonid", "eventbox").boxObject.height;
evl.setAttribute("style", "max-height: " + Math.max(0, height-gripbar*2) + "px");
]]>
9) {
if (this.parentColumn) {
if (this.editingTimer) {
clearTimeout(this.editingTimer);
this.editingTimer = null;
}
this.calendarView.setSelectedItems(1, [this.mOccurrence]);
this.mEditing = false;
this.parentColumn.startSweepingToModifyEvent(this, this.mOccurrence, "middle", this.mMouseX, this.mMouseY);
this.mInMouseDown = false;
}
}
]]>
null
null
0.6
0.1
null
null
0*60
24*60
0
0
9*60
null
15
null
0
0) return this.mDateList[0];
else return null;
]]>
0) return this.mDateList[this.mDateList.length-1];
else return null;
]]>
= 0)
return;
} else if (this.mDateList) {
for each (var d in this.mDateList) {
// if date is already visible, nothing to do
if (d.compare(targetDate) == 0)
return;
}
}
// if we're only showing one date, then continue
// to only show one date; otherwise, show the week.
if (this.numVisibleDates == 1) {
this.setDateRange(aDate, aDate);
} else {
this.setDateRange(aDate.startOfWeek, aDate.endOfWeek);
}
this.selectedDay = targetDate;
]]>
0) {
let start = item.startDate || item.entryDate || item.dueDate;
for each (let col in cols) {
if (start.isDate) {
col.header.selectOccurrence(occ);
} else {
col.column.selectOccurrence(occ);
}
}
}
}
}
if (!aSuppressEvent) {
this.fireEvent("itemselect", this.mSelectedItems);
}
]]>
0) {
occStart = this.startDate;
}
if (this.queryEndDate.compare(occEnd) < 0) {
occEnd = this.queryEndDate;
}
// Convert to display timezone if different
if (occStart.timezone != displayTZ) {
occStart = occStart.getInTimezone(displayTZ);
}
if (occEnd.timezone != displayTZ) {
occEnd = occEnd.getInTimezone(displayTZ);
}
// If crosses midnite in current TZ, set end just
// before midnite after start so start/title usually visible.
if (!sameDay(occStart, occEnd)) {
occEnd = occStart.clone();
occEnd.day = occStart.day;
occEnd.hour = 23;
occEnd.minute = 59;
}
// Ensure range shows occ
lowMinute = Math.min(occStart.hour * 60 + occStart.minute,
lowMinute);
highMinute = Math.max(occEnd.hour * 60 + occEnd.minute,
highMinute);
}
}
var displayDuration = highMinute - lowMinute;
if (this.mSelectedItems.length &&
displayDuration >= 0) {
let minute;
if (displayDuration <= this.mVisibleMinutes) {
minute = lowMinute + (displayDuration - this.mVisibleMinutes) / 2
} else if (this.mSelectedItems.length == 1) {
// If the displayDuration doesn't fit into the visible
// minutes, but only one event is selected, then go ahead and
// center the event start.
minute = Math.max(0, lowMinute - (this.mVisibleMinutes / 2));
}
this.scrollToMinute(minute);
}
]]>
1) {
headerDayBox.setAttribute("todaylastinview", "true");
}
break;
case 1:
dayHeaderBox.setAttribute("relation", "future");
dayEventsBox.setAttribute("relation", "future");
labelbox.setAttribute("relation", "future");
break;
}
// We don't want to actually mess with our original dates, plus
// they're likely to be immutable.
var d2 = d.clone();
d2.isDate = true;
d2.makeImmutable();
this.mDateColumns.push ( { date: d2, column: dayEventsBox, header: dayHeaderBox } );
counter++;
}
// Remove any extra columns that may have been hanging around
function removeExtraKids(elem) {
while (counter < elem.childNodes.length) {
elem.childNodes[counter].remove();
}
}
removeExtraKids(daybox);
removeExtraKids(headerdaybox);
removeExtraKids(this.labeldaybox);
if (updateTimeIndicator) {
this.updateTimeIndicatorPosition();
}
// fix pixels-per-minute
this.onResize();
for each (let col in this.mDateColumns) {
col.column.endLayoutBatchChange();
}
// Adjust scrollbar spacers
this.adjustScrollBarSpacers();
// Store the start and end of current view. Next time when
// setDateRange is called, it will use mViewStart and mViewEnd to
// check if view range has been changed.
this.mViewStart = this.mStartDate;
this.mViewEnd = this.mEndDate;
let toggleStatus = 0;
if (this.mTasksInView) {
toggleStatus |= this.mToggleStatusFlag.TasksInView;
}
if (this.mWorkdaysOnly) {
toggleStatus |= this.mToggleStatusFlag.WorkdaysOnly;
}
if (this.mShowCompleted) {
toggleStatus |= this.mToggleStatusFlag.ShowCompleted;
}
this.mToggleStatus = toggleStatus;
]]>
= 0 &&
endDate.compare(this.mEndDate) <= 0) {
for (var i = startDate.day; i <= endDate.day; i++) {
occMap[i] = true;
}
}
}
return this.mDateColumns.filter(function(col) {
return (col.date.day in occMap);
});
]]>
= bo.screenX) && (aClientX <= (bo.screenX + bo.width)) &&
(aClientY >= bo.screenY) && (aClientY <= (bo.screenY + bo.height)))
{
return col.column;
}
}
return null;
]]>
= headerDayBoxMaxHeight) {
// If the headerDayBox is just as high as the max-height, then
// there is already a scrollbar and we don't need to show the
// headerScrollbarSpacer. This is only valid for the non-rotated
// view.
headerPropertyValue = 0;
}
}
// set the same width/height for the label and header box spacers
var headerScrollBarSpacer = document.getAnonymousElementByAttribute(
this, "anonid", "headerscrollbarspacer");
headerScrollBarSpacer.setAttribute(propertyName, headerPropertyValue);
var labelScrollBarSpacer = document.getAnonymousElementByAttribute(
this, "anonid", "labelscrollbarspacer");
labelScrollBarSpacer.setAttribute(propertyName, propertyValue);
]]>
0
(24*60 - minutes_showed_in_the_view) but
// we consider 25 hours instead of 24 to let the view scroll until
// showing events that start just before 0.00
let maxFirstMin = 25 * 60 - Math.round(scrollBoxObject.height / this.mPixPerMin);
aMinute = Math.min(maxFirstMin, Math.max(0, aMinute));
if (scrollBoxObject && scrollbox.scrollHeight > 0) {
let x = {}, y = {};
scrollBoxObject.getPosition(x, y);
let pos = Math.round(aMinute * this.mPixPerMin);
if (scrollbox.getAttribute("orient") == "horizontal") {
scrollBoxObject.scrollTo(x.value, pos);
} else {
scrollBoxObject.scrollTo(pos, y.value);
}
}
// Set the first visible minute in any case, we want to move to the
// right minute as soon as possible if we couldn't do so above.
this.mFirstVisibleMinute = aMinute;
]]>
aDayEndMin ||
aDayEndMin > this.mEndMin) {
throw Components.results.NS_ERROR_INVALID_ARG;
}
if (this.mDayStartMin != aDayStartMin ||
this.mDayEndMin != aDayEndMin) {
this.mDayStartMin = aDayStartMin;
this.mDayEndMin = aDayEndMin;
// Also update on the time-bar
document.getAnonymousElementByAttribute(this, "anonid", "timebar")
.setDayStartEndHours(this.mDayStartMin / 60,
this.mDayEndMin / 60);
}
]]>
(this.mEndMin - this.mStartMin)) {
throw Components.results.NS_ERROR_INVALID_ARG;
}
if (this.mVisibleMinutes != aVisibleMinutes) {
this.mVisibleMinutes = aVisibleMinutes;
}
return this.mVisibleMinutes;
]]>
0) {
// We need to update the first visible minute, but only if the
// scrollbox has been sized.
let x = {}, y = {};
scrollBoxObject.getPosition(x, y);
if (scrollbox.getAttribute("orient") == "horizontal") {
this.mFirstVisibleMinute = Math.round(y.value/this.mPixPerMin);
} else {
this.mFirstVisibleMinute = Math.round(x.value/this.mPixPerMin);
}
}
]]>