You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2400 lines
63 KiB
2400 lines
63 KiB
define([ |
|
"dojo/_base/declare", |
|
"dojo/_base/array", |
|
"dojo/_base/event", |
|
"dojo/_base/lang", |
|
"dojo/_base/sniff", |
|
"dojo/_base/fx", |
|
"dojo/_base/html", |
|
"dojo/on", |
|
"dojo/dom", |
|
"dojo/dom-class", |
|
"dojo/dom-style", |
|
"dojo/dom-geometry", |
|
"dojo/dom-construct", |
|
"dojo/query", |
|
"dojo/i18n", |
|
"./ViewBase", |
|
"dojo/text!./templates/MatrixView.html", |
|
"dijit/_TemplatedMixin"], |
|
|
|
function( |
|
declare, |
|
arr, |
|
event, |
|
lang, |
|
has, |
|
fx, |
|
html, |
|
on, |
|
dom, |
|
domClass, |
|
domStyle, |
|
domGeometry, |
|
domConstruct, |
|
query, |
|
i18n, |
|
ViewBase, |
|
template, |
|
_TemplatedMixin){ |
|
|
|
/*===== |
|
var __HeaderClickEventArgs = { |
|
// summary: |
|
// A column click event. |
|
// index: Integer |
|
// The column index. |
|
// date: Date |
|
// The date displayed by the column. |
|
// triggerEvent: Event |
|
// The origin event. |
|
}; |
|
=====*/ |
|
|
|
/*===== |
|
var __ExpandRendererClickEventArgs = { |
|
// summary: |
|
// A expand renderer click event. |
|
// columnIndex: Integer |
|
// The column index of the cell. |
|
// rowIndex: Integer |
|
// The row index of the cell. |
|
// date: Date |
|
// The date displayed by the cell. |
|
// triggerEvent: Event |
|
// The origin event. |
|
}; |
|
=====*/ |
|
|
|
return declare("dojox.calendar.MatrixView", [ViewBase, _TemplatedMixin], { |
|
|
|
// summary: |
|
// The matrix view is a calendar view that displaying a matrix where each cell is a day. |
|
|
|
templateString: template, |
|
|
|
baseClass: "dojoxCalendarMatrixView", |
|
|
|
_setTabIndexAttr: "domNode", |
|
|
|
// viewKind: String |
|
// Type of the view. Used by the calendar widget to determine how to configure the view. |
|
// This view kind is "matrix". |
|
viewKind: "matrix", |
|
|
|
// renderData: Object |
|
// The render data object contains all the data needed to render the widget. |
|
renderData: null, |
|
|
|
// startDate: Date |
|
// The start date of the time interval displayed. |
|
// If not set at initialization time, will be set to current day. |
|
startDate: null, |
|
|
|
// refStartTime: Date? |
|
// (Optional) Start of the time interval of interest. |
|
// It is used to style differently the displayed rows out of the |
|
// time interval of interest. |
|
refStartTime: null, |
|
|
|
// refStartTime: Date? |
|
// (Optional) End of the time interval of interest. |
|
// It is used to style differently the displayed rows out of the |
|
// time interval of interest. |
|
refEndTime: null, |
|
|
|
// columnCount: Integer |
|
// The number of column to display (from the startDate). |
|
columnCount: 7, |
|
|
|
// rowCount: Integer |
|
// The number of rows to display (from the startDate). |
|
rowCount: 5, |
|
|
|
// horizontalRenderer: Class |
|
// The class use to create horizontal renderers. |
|
horizontalRenderer: null, |
|
|
|
// labelRenderer: Class |
|
// The class use to create label renderers. |
|
labelRenderer: null, |
|
|
|
// expandRenderer: Class |
|
// The class use to create drill down renderers. |
|
expandRenderer: null, |
|
|
|
// expandRenderer: Class |
|
// The class use to create horizontal decoration renderers. |
|
horizontalDecorationRenderer: null, |
|
|
|
// percentOverlap: Integer |
|
// The percentage of the renderer width used to superimpose one item renderers on another |
|
// when two events are overlapping. By default 0. |
|
percentOverlap: 0, |
|
|
|
// verticalGap: Integer |
|
// The number of pixels between two item renderers that are overlapping each other if the percentOverlap property is 0. |
|
verticalGap: 2, |
|
|
|
// horizontalRendererHeight: Integer |
|
// The height in pixels of the horizontal and label renderers that is applied by the layout. |
|
horizontalRendererHeight: 17, |
|
|
|
// horizontalRendererHeight: Integer |
|
// The height in pixels of the horizontal and label renderers that is applied by the layout. |
|
labelRendererHeight: 14, |
|
|
|
// expandRendererHeight: Integer |
|
// The height in pixels of the expand/collapse renderers that is applied by the layout. |
|
expandRendererHeight: 15, |
|
|
|
// cellPaddingTop: Integer |
|
// The top offset in pixels of each cell applied by the layout. |
|
cellPaddingTop: 16, |
|
|
|
// expandDuration: Integer |
|
// Duration of the animation when expanding or collapsing a row. |
|
expandDuration: 300, |
|
|
|
// expandEasing: Function |
|
// Easing function of the animation when expanding or collapsing a row (null by default). |
|
expandEasing: null, |
|
|
|
// layoutDuringResize: Boolean |
|
// Indicates if the item renderers' position and size is updated or if they are hidden during a resize of the widget. |
|
layoutDuringResize: false, |
|
|
|
// roundToDay: Boolean |
|
// For horizontal renderers that are not filling entire days, whether fill the day or not. |
|
roundToDay: true, |
|
|
|
// showCellLabel: Boolean |
|
// Whether display or not the grid cells label (usually the day of month). |
|
showCellLabel: true, |
|
|
|
// scrollable: [private] Boolean |
|
scrollable: false, |
|
|
|
// resizeCursor: [private] Boolean |
|
resizeCursor: "e-resize", |
|
|
|
constructor: function(){ |
|
this.invalidatingProperties = |
|
["columnCount", "rowCount", "startDate", "horizontalRenderer", "horizontalDecaorationRenderer", |
|
"labelRenderer", "expandRenderer", "rowHeaderDatePattern", "columnHeaderLabelLength", |
|
"cellHeaderShortPattern", "cellHeaderLongPattern", "percentOverlap", "verticalGap", |
|
"horizontalRendererHeight", "labelRendererHeight", "expandRendererHeight", "cellPaddingTop", |
|
"roundToDay", "itemToRendererKindFunc", "layoutPriorityFunction", "formatItemTimeFunc", "textDir", "items"]; |
|
|
|
this._ddRendererList = []; |
|
this._ddRendererPool = []; |
|
this._rowHeaderHandles = []; |
|
}, |
|
|
|
destroy: function(preserveDom){ |
|
this._cleanupRowHeader(); |
|
this.inherited(arguments); |
|
}, |
|
|
|
postCreate: function(){ |
|
this.inherited(arguments); |
|
this._initialized = true; |
|
if(!this.invalidRendering){ |
|
this.refreshRendering(); |
|
} |
|
}, |
|
|
|
_createRenderData: function(){ |
|
|
|
var rd = {}; |
|
|
|
rd.dateLocaleModule = this.dateLocaleModule; |
|
rd.dateClassObj = this.dateClassObj; |
|
rd.dateModule = this.dateModule; // arithmetics on Dates |
|
|
|
rd.dates = []; |
|
|
|
rd.columnCount = this.get("columnCount"); |
|
rd.rowCount = this.get("rowCount"); |
|
|
|
rd.sheetHeight = this.itemContainer.offsetHeight; |
|
|
|
this._computeRowsHeight(rd); |
|
|
|
var d = this.get("startDate"); |
|
|
|
if(d == null){ |
|
d = new rd.dateClassObj(); |
|
} |
|
|
|
d = this.floorToDay(d, false, rd); |
|
|
|
this.startDate = d; |
|
|
|
for(var row = 0; row < rd.rowCount ; row++){ |
|
rd.dates.push([]); |
|
for(var col = 0; col < rd.columnCount ; col++){ |
|
rd.dates[row].push(d); |
|
d = this.addAndFloor(d, "day", 1); |
|
} |
|
} |
|
|
|
rd.startTime = this.newDate(rd.dates[0][0], rd); |
|
rd.endTime = this.newDate(rd.dates[rd.rowCount-1][rd.columnCount-1], rd); |
|
rd.endTime = rd.dateModule.add(rd.endTime, "day", 1); |
|
rd.endTime = this.floorToDay(rd.endTime, true); |
|
|
|
if(this.displayedItemsInvalidated && !this._isEditing){ |
|
// while editing in no live layout we must not to recompute items (duplicate renderers) |
|
this.displayedItemsInvalidated = false; |
|
this._computeVisibleItems(rd); |
|
|
|
}else if(this.renderData){ |
|
rd.items = this.renderData.items; |
|
} |
|
|
|
if(this.displayedDecorationItemsInvalidated){ |
|
rd.decorationItems = this.decorationStoreManager._computeVisibleItems(rd); |
|
|
|
}else if (this.renderData){ |
|
rd.decorationItems = this.renderData.decorationItems; |
|
} |
|
|
|
rd.rtl = !this.isLeftToRight(); |
|
|
|
return rd; |
|
}, |
|
|
|
_validateProperties: function(){ |
|
|
|
this.inherited(arguments); |
|
|
|
if(this.columnCount<1 || isNaN(this.columnCount)){ |
|
this.columnCount = 1; |
|
} |
|
|
|
if(this.rowCount<1 || isNaN(this.rowCount)){ |
|
this.rowCount = 1; |
|
} |
|
|
|
if(isNaN(this.percentOverlap) || this.percentOverlap < 0 || this.percentOverlap > 100){ |
|
this.percentOverlap = 0; |
|
} |
|
|
|
if(isNaN(this.verticalGap) || this.verticalGap < 0){ |
|
this.verticalGap = 2; |
|
} |
|
|
|
if(isNaN(this.horizontalRendererHeight) || this.horizontalRendererHeight < 1){ |
|
this.horizontalRendererHeight = 17; |
|
} |
|
|
|
if(isNaN(this.labelRendererHeight) || this.labelRendererHeight < 1){ |
|
this.labelRendererHeight = 14; |
|
} |
|
|
|
if(isNaN(this.expandRendererHeight) || this.expandRendererHeight < 1){ |
|
this.expandRendererHeight = 15; |
|
} |
|
|
|
}, |
|
|
|
_setStartDateAttr: function(value){ |
|
this.displayedItemsInvalidated = true; |
|
this._set("startDate", value); |
|
}, |
|
|
|
_setColumnCountAttr: function(value){ |
|
this.displayedItemsInvalidated = true; |
|
this._set("columnCount", value); |
|
}, |
|
|
|
_setRowCountAttr: function(value){ |
|
this.displayedItemsInvalidated = true; |
|
this._set("rowCount", value); |
|
}, |
|
|
|
__fixEvt:function(e){ |
|
e.sheet = "primary"; |
|
e.source = this; |
|
return e; |
|
}, |
|
|
|
////////////////////////////////////////// |
|
// |
|
// Formatting functions |
|
// |
|
////////////////////////////////////////// |
|
|
|
_formatRowHeaderLabel: function(/*Date*/d){ |
|
// summary: |
|
// Computes the row header label for the specified time of day. |
|
// By default the getWeekNumberLabel() function is called. |
|
// The rowHeaderDatePattern property can be used to set a |
|
// custom date pattern to the formatter. |
|
// d: Date |
|
// The date to format |
|
// tags: |
|
// protected |
|
|
|
if(this.rowHeaderDatePattern){ |
|
return this.renderData.dateLocaleModule.format(d, { |
|
selector: 'date', |
|
datePattern: this.rowHeaderDatePattern |
|
}); |
|
}else{ |
|
return this.getWeekNumberLabel(d); |
|
} |
|
|
|
}, |
|
|
|
_formatColumnHeaderLabel: function(d){ |
|
// summary: |
|
// Computes the column header label for the specified date. |
|
// By default a formatter is used, optionally the <code>columnHeaderLabelLength</code> |
|
// property can be used to specify the length of the string. |
|
// d: Date |
|
// The date to format |
|
// tags: |
|
// protected |
|
|
|
return this.renderData.dateLocaleModule.getNames('days', this.columnHeaderLabelLength ? this.columnHeaderLabelLength : 'wide', 'standAlone')[d.getDay()]; |
|
}, |
|
|
|
// cellHeaderShortPattern: String |
|
// Custom date/time pattern for grid cell label to override default one coming from the CLDR. |
|
// See dojo/date/locale documentation for format string. |
|
cellHeaderShortPattern: null, |
|
|
|
// cellHeaderLongPattern: String |
|
// Custom date/time pattern for grid cell label to override default one coming from the CLDR. |
|
// The long pattern is used for the first day of month or the first displayed day of a month. |
|
// See dojo/date/locale documentation for format string. |
|
cellHeaderLongPattern: null, |
|
|
|
_formatGridCellLabel: function(d, row, col){ |
|
// summary: |
|
// Computes the column header label for the specified date. |
|
// By default a formatter is used, optionally the <code>cellHeaderLongPattern</code> and <code>cellHeaderShortPattern</code> |
|
// properties can be used to set a custom date pattern to the formatter. |
|
// d: Date |
|
// The date to format. |
|
// row: Integer |
|
// The row that displays the current date. |
|
// col: Integer |
|
// The column that displays the current date. |
|
// tags: |
|
// protected |
|
|
|
var isFirstDayOfMonth = row == 0 && col == 0 || d.getDate() == 1; |
|
var format, rb; |
|
if(isFirstDayOfMonth){ |
|
if(this.cellHeaderLongPattern){ |
|
format = this.cellHeaderLongPattern; |
|
}else{ |
|
rb = i18n.getLocalization("dojo.cldr", this._calendar); |
|
format = rb["dateFormatItem-MMMd"]; |
|
} |
|
}else{ |
|
if(this.cellHeaderShortPattern){ |
|
format = this.cellHeaderShortPattern; |
|
}else{ |
|
rb = i18n.getLocalization("dojo.cldr", this._calendar); |
|
format = rb["dateFormatItem-d"]; |
|
} |
|
} |
|
return this.renderData.dateLocaleModule.format(d, { |
|
selector: 'date', |
|
datePattern: format |
|
}); |
|
}, |
|
|
|
//////////////////////////////////////////// |
|
// |
|
// HTML structure management |
|
// |
|
/////////////////////////////////////////// |
|
|
|
refreshRendering: function(){ |
|
this.inherited(arguments); |
|
|
|
if(!this.domNode){ |
|
return; |
|
} |
|
|
|
this._validateProperties(); |
|
|
|
var oldRd = this.renderData; |
|
var rd = this.renderData = this._createRenderData(); |
|
|
|
this._createRendering(rd, oldRd); |
|
|
|
this._layoutDecorationRenderers(rd); |
|
this._layoutRenderers(rd); |
|
}, |
|
|
|
_createRendering: function(renderData, oldRenderData){ |
|
// summary: |
|
// Creates the HTML structure (grid, place holders, headers, etc) |
|
// renderData: Object |
|
// The new render data |
|
// oldRenderData: Object |
|
// The previous render data |
|
// tags: |
|
// private |
|
|
|
if(renderData.rowHeight <= 0){ |
|
renderData.columnCount = 1; |
|
renderData.rowCount = 1; |
|
renderData.invalidRowHeight = true; |
|
return; |
|
} |
|
|
|
if(oldRenderData){ |
|
// make sure to have correct rowCount |
|
if(this.itemContainerTable){ |
|
var rows = query(".dojoxCalendarItemContainerRow", this.itemContainerTable); |
|
oldRenderData.rowCount = rows.length; |
|
} |
|
} |
|
|
|
this._buildColumnHeader(renderData, oldRenderData); |
|
this._buildRowHeader(renderData, oldRenderData); |
|
this._buildGrid(renderData, oldRenderData); |
|
this._buildItemContainer(renderData, oldRenderData); |
|
|
|
if(this.buttonContainer && this.owner != null && this.owner.currentView == this){ |
|
domStyle.set(this.buttonContainer, {"right":0, "left":0}); |
|
} |
|
}, |
|
|
|
_buildColumnHeader: function(/*Object*/ renderData, /*Object*/oldRenderData){ |
|
// summary: |
|
// Creates incrementally the HTML structure of the column header and configures its content. |
|
// |
|
// renderData: |
|
// The render data to display. |
|
// |
|
// oldRenderData: |
|
// The previously render data displayed, if any. |
|
// tags: |
|
// private |
|
var table = this.columnHeaderTable; |
|
|
|
if(!table){ |
|
return; |
|
} |
|
|
|
var count = renderData.columnCount - (oldRenderData ? oldRenderData.columnCount : 0); |
|
|
|
if(has("ie") == 8){ |
|
// workaround Internet Explorer 8 bug. |
|
// if on the table, width: 100% and table-layout: fixed are set |
|
// and columns are removed, width of remaining columns is not |
|
// recomputed: must rebuild all. |
|
if(this._colTableSave == null){ |
|
this._colTableSave = lang.clone(table); |
|
}else if(count < 0){ |
|
this.columnHeader.removeChild(table); |
|
domConstruct.destroy(table); |
|
table = lang.clone(this._colTableSave); |
|
this.columnHeaderTable = table; |
|
this.columnHeader.appendChild(table); |
|
count = renderData.columnCount; |
|
} |
|
|
|
} // else incremental dom add/remove for real browsers. |
|
|
|
var tbodies = query("tbody", table); |
|
var trs = query("tr", table); |
|
var tbody, tr, td; |
|
|
|
if(tbodies.length == 1){ |
|
tbody = tbodies[0]; |
|
}else{ |
|
tbody = html.create("tbody", null, table); |
|
} |
|
|
|
if(trs.length == 1){ |
|
tr = trs[0]; |
|
}else{ |
|
tr = domConstruct.create("tr", null, tbody); |
|
} |
|
|
|
// Build HTML structure (incremental) |
|
if(count > 0){ // creation |
|
for(var i=0; i < count; i++){ |
|
td = domConstruct.create("td", null, tr); |
|
} |
|
}else{ // deletion |
|
count = -count; |
|
for(var i=0; i < count; i++){ |
|
tr.removeChild(tr.lastChild); |
|
} |
|
} |
|
|
|
// fill & configure |
|
query("td", table).forEach(function(td, i){ |
|
td.className = ""; |
|
var d = renderData.dates[0][i]; |
|
this._setText(td, this._formatColumnHeaderLabel(d)); |
|
if(i == 0){ |
|
domClass.add(td, "first-child"); |
|
}else if(i == this.renderData.columnCount-1){ |
|
domClass.add(td, "last-child"); |
|
} |
|
this.styleColumnHeaderCell(td, d, renderData); |
|
}, this); |
|
|
|
if(this.yearColumnHeaderContent){ |
|
var d = renderData.dates[0][0]; |
|
this._setText(this.yearColumnHeaderContent, renderData.dateLocaleModule.format(d, |
|
{selector: "date", datePattern:"yyyy"})); |
|
} |
|
}, |
|
|
|
styleColumnHeaderCell: function(node, date, renderData){ |
|
// summary: |
|
// Styles the CSS classes to the node that displays a column header cell. |
|
// By default this method is setting the "dojoxCalendarWeekend" if the day of week represents a weekend. |
|
// node: Node |
|
// The DOM node that displays the column in the grid. |
|
// date: Date |
|
// The date displayed by this column |
|
// renderData: Object |
|
// The render data. |
|
// tags: |
|
// protected |
|
|
|
domClass.add(node, this._cssDays[date.getDay()]); |
|
|
|
if(this.isWeekEnd(date)){ |
|
domClass.add(node, "dojoxCalendarWeekend"); |
|
} |
|
}, |
|
|
|
_rowHeaderHandles: null, |
|
|
|
_cleanupRowHeader: function(){ |
|
// tags: |
|
// private |
|
|
|
while(this._rowHeaderHandles.length > 0){ |
|
var list = this._rowHeaderHandles.pop(); |
|
while(list.length>0){ |
|
list.pop().remove(); |
|
} |
|
} |
|
}, |
|
|
|
|
|
_rowHeaderClick: function(e){ |
|
// tags: |
|
// private |
|
|
|
var index = query("td", this.rowHeaderTable).indexOf(e.currentTarget); |
|
this._onRowHeaderClick({ |
|
index: index, |
|
date: this.renderData.dates[index][0], |
|
triggerEvent: e |
|
}); |
|
}, |
|
|
|
_buildRowHeader: function(renderData, oldRenderData){ |
|
|
|
// summary: |
|
// Creates incrementally the HTML structure of the row header and configures its content. |
|
// |
|
// renderData: |
|
// The render data to display. |
|
// |
|
// oldRenderData: |
|
// The previously render data displayed, if any. |
|
// tags: |
|
// private |
|
|
|
var rowHeaderTable = this.rowHeaderTable; |
|
|
|
if(!rowHeaderTable){ |
|
return; |
|
} |
|
|
|
var tbodies = query("tbody", rowHeaderTable); |
|
var tbody, tr, td; |
|
|
|
if(tbodies.length == 1){ |
|
tbody = tbodies[0]; |
|
}else{ |
|
tbody = domConstruct.create("tbody", null, rowHeaderTable); |
|
} |
|
|
|
var count = renderData.rowCount - (oldRenderData ? oldRenderData.rowCount : 0); |
|
|
|
// Build HTML structure |
|
if(count>0){ // creation |
|
for(var i=0; i < count; i++){ |
|
tr = domConstruct.create("tr", null, tbody); |
|
td = domConstruct.create("td", null, tr); |
|
|
|
var h = []; |
|
|
|
h.push(on(td, "click", lang.hitch(this, this._rowHeaderClick))); |
|
|
|
if(!has("touch")){ |
|
h.push(on(td, "mousedown", function(e){ |
|
domClass.add(e.currentTarget, "Active"); |
|
})); |
|
|
|
h.push(on(td, "mouseup", function(e){ |
|
domClass.remove(e.currentTarget, "Active"); |
|
})); |
|
|
|
h.push(on(td, "mouseover", function(e){ |
|
domClass.add(e.currentTarget, "Hover"); |
|
})); |
|
|
|
h.push(on(td, "mouseout", function(e){ |
|
domClass.remove(e.currentTarget, "Hover"); |
|
})); |
|
} |
|
this._rowHeaderHandles.push(h); |
|
} |
|
}else{ |
|
count = -count; |
|
// deletion of existing nodes |
|
for(var i=0; i < count; i++){ |
|
tbody.removeChild(tbody.lastChild); |
|
var list = this._rowHeaderHandles.pop(); |
|
while(list.length>0){ |
|
list.pop().remove(); |
|
} |
|
} |
|
} |
|
|
|
// fill labels |
|
|
|
query("tr", rowHeaderTable).forEach(function(tr, i){ |
|
|
|
domStyle.set(tr, "height", this._getRowHeight(i) + "px"); |
|
|
|
var d = renderData.dates[i][0]; |
|
|
|
var td = query("td", tr)[0]; |
|
td.className = ""; |
|
if(i == 0){ |
|
domClass.add(td, "first-child"); |
|
} |
|
if(i == this.renderData.rowCount-1){ |
|
domClass.add(td, "last-child"); |
|
} |
|
|
|
this.styleRowHeaderCell(td, d, renderData); |
|
|
|
this._setText(td, this._formatRowHeaderLabel(d)); |
|
}, this); |
|
|
|
}, |
|
|
|
styleRowHeaderCell: function(node, date, renderData){ |
|
// summary: |
|
// Styles the CSS classes to the node that displays a row header cell. |
|
// By default this method is doing nothing. |
|
// node: Node |
|
// The DOM node that displays the column in the grid. |
|
// date: Date |
|
// The date in the week. |
|
// renderData: Object |
|
// The render data. |
|
// tags: |
|
// protected |
|
|
|
|
|
}, |
|
|
|
_buildGrid: function (renderData, oldRenderData){ |
|
// summary: |
|
// Creates incrementally the HTML structure of the grid and configures its content. |
|
// |
|
// renderData: |
|
// The render data to display. |
|
// |
|
// oldRenderData: |
|
// The previously render data displayed, if any. |
|
// tags: |
|
// private |
|
|
|
var table = this.gridTable; |
|
|
|
if(!table){ |
|
return; |
|
} |
|
|
|
var currentTR = query("tr", table); |
|
|
|
var rowDiff = renderData.rowCount - currentTR.length; |
|
var addRows = rowDiff > 0; |
|
|
|
var colDiff = renderData.columnCount - (currentTR ? query("td", currentTR[0]).length : 0); |
|
|
|
if(has("ie") == 8){ |
|
// workaround Internet Explorer 8 bug. |
|
// if on the table, width: 100% and table-layout: fixed are set |
|
// and columns are removed, width of remaining columns is not |
|
// recomputed: must rebuild all. |
|
if(this._gridTableSave == null){ |
|
this._gridTableSave = lang.clone(table); |
|
}else if(colDiff < 0){ |
|
this.grid.removeChild(table); |
|
domConstruct.destroy(table); |
|
table = lang.clone(this._gridTableSave); |
|
this.gridTable = table; |
|
this.grid.appendChild(table); |
|
colDiff = renderData.columnCount; |
|
rowDiff = renderData.rowCount; |
|
addRows = true; |
|
} |
|
} |
|
|
|
var tbodies = query("tbody", table); |
|
var tbody; |
|
|
|
if(tbodies.length == 1){ |
|
tbody = tbodies[0]; |
|
}else{ |
|
tbody = domConstruct.create("tbody", null, table); |
|
} |
|
|
|
// Build rows HTML structure (incremental) |
|
if(addRows){ // creation |
|
for(var i=0; i<rowDiff; i++){ |
|
domConstruct.create("tr", null, tbody); |
|
} |
|
}else{ // deletion |
|
rowDiff = -rowDiff; |
|
for(var i=0; i<rowDiff; i++){ |
|
tbody.removeChild(tbody.lastChild); |
|
} |
|
} |
|
|
|
var rowIndex = renderData.rowCount - rowDiff; |
|
|
|
var addCols = addRows || colDiff >0; |
|
colDiff = addCols ? colDiff : -colDiff; |
|
|
|
query("tr", table).forEach(function(tr, i){ |
|
|
|
if(addCols){ // creation |
|
var len = i >= rowIndex ? renderData.columnCount : colDiff; |
|
for(var i=0; i<len; i++){ |
|
var td = domConstruct.create("td", null, tr); |
|
domConstruct.create("span", null, td); |
|
} |
|
}else{ // deletion |
|
for(var i=0; i<colDiff; i++){ |
|
tr.removeChild(tr.lastChild); |
|
} |
|
} |
|
}); |
|
|
|
// Set the CSS classes |
|
|
|
query("tr", table).forEach(function (tr, row){ |
|
|
|
domStyle.set(tr, "height", this._getRowHeight(row) + "px"); |
|
|
|
tr.className = ""; |
|
// compatibility layer for IE7 & 8 that does not support :first-child and :last-child pseudo selectors |
|
if(row == 0){ |
|
domClass.add(tr, "first-child"); |
|
} |
|
if(row == renderData.rowCount-1){ |
|
domClass.add(tr, "last-child"); |
|
} |
|
|
|
query("td", tr).forEach(function (td, col){ |
|
|
|
td.className = ""; |
|
|
|
if(col == 0){ |
|
domClass.add(td, "first-child"); |
|
} |
|
|
|
if(col == renderData.columnCount-1){ |
|
domClass.add(td, "last-child"); |
|
} |
|
|
|
var d = renderData.dates[row][col]; |
|
|
|
var span = query("span", td)[0]; |
|
this._setText(span, this.showCellLabel ? this._formatGridCellLabel(d, row, col): null); |
|
|
|
this.styleGridCell(td, d, renderData); |
|
}, this); |
|
}, this); |
|
|
|
}, |
|
|
|
// styleGridCellFunc: Function |
|
// Custom function to customize the appearance of a grid cell by installing custom CSS class on the node. |
|
// The signature of the function must be the same then the styleGridCell one. |
|
// By default the defaultStyleGridCell function is used. |
|
styleGridCellFunc: null, |
|
|
|
defaultStyleGridCell: function(node, date, renderData){ |
|
// summary: |
|
// Styles the CSS classes to the node that displays a cell. |
|
// By default this method is setting the following CSS classes: |
|
// - "dojoxCalendarToday" class name if the date displayed is the current date, |
|
// - "dojoxCalendarWeekend" if the date represents a weekend or |
|
// - "dojoxCalendarDayDisabled" if the date is out of the [refStartTime, refEndTime] interval. |
|
// - the CSS class corresponding of the displayed day of week ("Sun", "Mon" and so on). |
|
// node: Node |
|
// The DOM node that displays the cell in the grid. |
|
// date: Date |
|
// The date displayed by this cell. |
|
// renderData: Object |
|
// The render data. |
|
// tags: |
|
// protected |
|
|
|
domClass.add(node, this._cssDays[date.getDay()]); |
|
|
|
var cal = this.dateModule; |
|
if(this.isToday(date)){ |
|
domClass.add(node, "dojoxCalendarToday"); |
|
}else if(this.refStartTime != null && this.refEndTime != null && |
|
(cal.compare(date, this.refEndTime) >= 0 || |
|
cal.compare(cal.add(date, "day", 1), this.refStartTime) <= 0)){ |
|
domClass.add(node, "dojoxCalendarDayDisabled"); |
|
}else if(this.isWeekEnd(date)){ |
|
domClass.add(node, "dojoxCalendarWeekend"); |
|
} |
|
}, |
|
|
|
styleGridCell: function(node, date, renderData){ |
|
// summary: |
|
// Styles the CSS classes to the node that displays a cell. |
|
// Delegates to styleGridCellFunc if defined or defaultStyleGridCell otherwise. |
|
// node: Node |
|
// The DOM node that displays the cell in the grid. |
|
// date: Date |
|
// The date displayed by this cell. |
|
// renderData: Object |
|
// The render data. |
|
// tags: |
|
// protected |
|
if(this.styleGridCellFunc){ |
|
this.styleGridCellFunc(node, date, renderData); |
|
}else{ |
|
this.defaultStyleGridCell(node, date, renderData); |
|
} |
|
}, |
|
|
|
_buildItemContainer: function(renderData, oldRenderData){ |
|
// summary: |
|
// Creates the HTML structure of the item container and configures its content. |
|
// |
|
// renderData: |
|
// The render data to display. |
|
// |
|
// oldRenderData: |
|
// The previously render data displayed, if any. |
|
// tags: |
|
// private |
|
|
|
var table = this.itemContainerTable; |
|
|
|
if(!table){ |
|
return; |
|
} |
|
|
|
var rows = []; |
|
|
|
var count = renderData.rowCount - (oldRenderData ? oldRenderData.rowCount : 0) |
|
|
|
if(has("ie") == 8){ |
|
// workaround Internet Explorer 8 bug. |
|
// if on the table, width: 100% and table-layout: fixed are set |
|
// and columns are removed, width of remaining columns is not |
|
// recomputed: must rebuild all. |
|
if(this._itemTableSave == null){ |
|
this._itemTableSave = lang.clone(table); |
|
}else if(count < 0){ |
|
this.itemContainer.removeChild(table); |
|
this._recycleItemRenderers(true); |
|
this._recycleExpandRenderers(true); |
|
domConstruct.destroy(table); |
|
table = lang.clone(this._itemTableSave); |
|
this.itemContainerTable = table; |
|
this.itemContainer.appendChild(table); |
|
count = renderData.columnCount; |
|
} |
|
|
|
} // else incremental dom add/remove for real browsers. |
|
|
|
var tbodies = query("tbody", table); |
|
var tbody, tr, td, div; |
|
|
|
if(tbodies.length == 1){ |
|
tbody = tbodies[0]; |
|
}else{ |
|
tbody = domConstruct.create("tbody", null, table); |
|
} |
|
|
|
// Build HTML structure (incremental) |
|
if(count>0){ // creation |
|
for(var i=0; i < count; i++){ |
|
tr = domConstruct.create("tr", null, tbody); |
|
domClass.add(tr, "dojoxCalendarItemContainerRow"); |
|
td = domConstruct.create("td", null, tr); |
|
div = domConstruct.create("div", null, td); |
|
domClass.add(div, "dojoxCalendarContainerRow"); |
|
} |
|
}else{ // deletion |
|
count = -count; |
|
for(var i=0; i < count; i++){ |
|
tbody.removeChild(tbody.lastChild); |
|
} |
|
} |
|
|
|
query(".dojoxCalendarItemContainerRow", table).forEach(function(tr, i){ |
|
domStyle.set(tr, "height", this._getRowHeight(i) + "px"); |
|
rows.push(tr.childNodes[0].childNodes[0]); |
|
}, this); |
|
|
|
renderData.cells = rows; |
|
}, |
|
|
|
resize: function(changeSize){ |
|
this.inherited(arguments); |
|
this._resizeHandler(null, false); |
|
}, |
|
|
|
_resizeHandler: function(e, apply){ |
|
// summary: |
|
// Refreshes and apply the row height according to the widget height. |
|
// e: Event |
|
// The resize event (optional) |
|
// apply: Boolean |
|
// Whether take into account the layoutDuringResize flag to relayout item while resizing or not. |
|
// tags: |
|
// private |
|
|
|
var rd = this.renderData; |
|
|
|
if(rd == null){ |
|
this.refreshRendering(); |
|
return; |
|
} |
|
|
|
if(rd.sheetHeight != this.itemContainer.offsetHeight){ |
|
// refresh values |
|
rd.sheetHeight = this.itemContainer.offsetHeight; |
|
var expRow = this.getExpandedRowIndex(); |
|
if(expRow == -1){ |
|
this._computeRowsHeight(); |
|
this._resizeRows(); |
|
}else{ |
|
this.expandRow(rd.expandedRow, rd.expandedRowCol, 0, null, true); |
|
} |
|
if(rd.invalidRowHeight){ |
|
// complete recompute |
|
delete rd.invalidRowHeight; |
|
this.renderData = null; |
|
this.displayedItemsInvalidated = true; |
|
this.refreshRendering(); |
|
return; |
|
} |
|
} |
|
|
|
if(this.layoutDuringResize || apply){ |
|
// Use a time for FF (at least). In FF the cell size and position info are not ready yet. |
|
setTimeout(lang.hitch(this, function(){ |
|
this._layoutRenderers(this.renderData); |
|
this._layoutDecorationRenderers(this.renderData); |
|
}), 20); |
|
|
|
}else{ |
|
domStyle.set(this.itemContainer, "opacity", 0); |
|
this._recycleItemRenderers(); |
|
this._recycleExpandRenderers(); |
|
if(this._resizeTimer != undefined){ |
|
clearTimeout(this._resizeTimer); |
|
} |
|
this._resizeTimer = setTimeout(lang.hitch(this, function(){ |
|
delete this._resizeTimer; |
|
this._resizeRowsImpl(this.itemContainer, "tr"); |
|
this._layoutRenderers(this.renderData); |
|
this._layoutDecorationRenderers(this.renderData); |
|
if(this.resizeAnimationDuration == 0){ |
|
domStyle.set(this.itemContainer, "opacity", 1); |
|
}else{ |
|
fx.fadeIn({node:this.itemContainer, curve:[0, 1]}).play(this.resizeAnimationDuration); |
|
} |
|
}), 200); |
|
} |
|
|
|
}, |
|
|
|
// resizeAnimationDuration: Integer |
|
// Duration, in milliseconds, of the fade animation showing the item renderers after a widget resize. |
|
resizeAnimationDuration: 0, |
|
|
|
///////////////////////////////////////////// |
|
// |
|
// Row height management |
|
// |
|
////////////////////////////////////////////// |
|
|
|
getExpandedRowIndex: function(){ |
|
// summary: |
|
// Returns the index of the expanded row or -1 if there's no row expanded. |
|
return this.renderData.expandedRow == null ? -1 : this.renderData.expandedRow; |
|
}, |
|
|
|
collapseRow: function(duration, easing, apply){ |
|
// summary: |
|
// Collapses the expanded row, if any. |
|
// duration: Integer |
|
// Duration in milliseconds of the optional animation. |
|
// easing: Function |
|
// Easing function of the optional animation. |
|
|
|
var rd = this.renderData; |
|
|
|
if(apply == undefined){ |
|
apply = true; |
|
} |
|
if(duration == undefined){ |
|
duration = this.expandDuration; |
|
} |
|
|
|
if(rd && rd.expandedRow != null && rd.expandedRow != -1){ |
|
if(apply && duration){ |
|
var index = rd.expandedRow; |
|
var oldSize = rd.expandedRowHeight; |
|
delete rd.expandedRow; |
|
this._computeRowsHeight(rd); |
|
var size = this._getRowHeight(index); |
|
rd.expandedRow = index; |
|
|
|
this._recycleExpandRenderers(); |
|
this._recycleItemRenderers(); |
|
domStyle.set(this.itemContainer, "display", "none"); |
|
|
|
this._expandAnimation = new fx.Animation({ |
|
curve: [oldSize, size], |
|
duration: duration, |
|
easing: easing, |
|
onAnimate: lang.hitch(this, function(size) { |
|
this._expandRowImpl(Math.floor(size)); |
|
}), |
|
onEnd: lang.hitch(this, function(size) { |
|
this._expandAnimation = null; |
|
this._collapseRowImpl(false); |
|
this._resizeRows(); |
|
domStyle.set(this.itemContainer, "display", "block"); |
|
setTimeout(lang.hitch(this, function(){ |
|
this._layoutRenderers(rd); |
|
}), 100); |
|
this.onExpandAnimationEnd(false); |
|
}) |
|
}); |
|
|
|
this._expandAnimation.play(); |
|
}else{ |
|
this._collapseRowImpl(apply); |
|
} |
|
} |
|
}, |
|
|
|
_collapseRowImpl: function(apply){ |
|
// tags: |
|
// private |
|
|
|
var rd = this.renderData; |
|
delete rd.expandedRow; |
|
delete rd.expandedRowHeight; |
|
this._computeRowsHeight(rd); |
|
if(apply == undefined || apply){ |
|
this._resizeRows(); |
|
this._layoutRenderers(rd); |
|
} |
|
}, |
|
|
|
expandRow: function(rowIndex, colIndex, duration, easing, apply){ |
|
// summary: |
|
// Expands the specified row. |
|
// rowIndex: Integer |
|
// The index of the row to expand. |
|
// colIndex: Integer? |
|
// The column index of the expand renderer that triggers the action, optional. |
|
// duration: Integer? |
|
// Duration in milliseconds of the optional animation. |
|
// easing: Function? |
|
// Easing function of the optional animation. |
|
|
|
var rd = this.renderData; |
|
if(!rd || rowIndex < 0 || rowIndex >= rd.rowCount){ |
|
return -1; |
|
} |
|
if(colIndex == undefined || colIndex < 0 || colIndex >= rd.columnCount){ |
|
colIndex = -1; // ignore invalid values |
|
} |
|
if(apply == undefined){ |
|
apply = true; |
|
} |
|
if(duration == undefined){ |
|
duration = this.expandDuration; |
|
} |
|
if(easing == undefined){ |
|
easing = this.expandEasing; |
|
} |
|
|
|
var oldSize = this._getRowHeight(rowIndex); |
|
var size = rd.sheetHeight - Math.ceil(this.cellPaddingTop * (rd.rowCount-1)); |
|
|
|
rd.expandedRow = rowIndex; |
|
rd.expandedRowCol = colIndex; |
|
rd.expandedRowHeight = size; |
|
|
|
if(apply){ |
|
if(duration){ |
|
//debugger; |
|
this._recycleExpandRenderers(); |
|
this._recycleItemRenderers(); |
|
domStyle.set(this.itemContainer, "display", "none"); |
|
|
|
this._expandAnimation = new fx.Animation({ |
|
curve: [oldSize, size], |
|
duration: duration, |
|
delay:50, |
|
easing: easing, |
|
onAnimate: lang.hitch(this, function(size) { |
|
this._expandRowImpl(Math.floor(size)); |
|
}), |
|
onEnd: lang.hitch(this, function(){ |
|
this._expandAnimation = null; |
|
domStyle.set(this.itemContainer, "display", "block"); |
|
setTimeout(lang.hitch(this, function(){ |
|
this._expandRowImpl(size, true); |
|
}), 100); |
|
this.onExpandAnimationEnd(true); |
|
}) |
|
}); |
|
this._expandAnimation.play(); |
|
}else{ |
|
this._expandRowImpl(size, true); |
|
} |
|
} |
|
}, |
|
|
|
_expandRowImpl: function(size, layout){ |
|
// tags: |
|
// private |
|
|
|
var rd = this.renderData; |
|
rd.expandedRowHeight = size; |
|
this._computeRowsHeight(rd, rd.sheetHeight-size); |
|
this._resizeRows(); |
|
if(layout){ |
|
this._layoutRenderers(rd); |
|
} |
|
}, |
|
|
|
onExpandAnimationEnd: function(expand){ |
|
// summary: |
|
// Event dispatched at the end of an expand or collapse animation. |
|
// expand: Boolean |
|
// Whether the finished animation was an expand or a collapse animation. |
|
// tags: |
|
// callback |
|
|
|
}, |
|
|
|
_resizeRows: function(){ |
|
// summary: |
|
// Refreshes the height of the underlying HTML objects. |
|
// tags: |
|
// private |
|
|
|
if(this._getRowHeight(0) <= 0){ |
|
return; |
|
} |
|
|
|
if(this.rowHeaderTable){ |
|
this._resizeRowsImpl(this.rowHeaderTable, "tr"); |
|
} |
|
if(this.gridTable){ |
|
this._resizeRowsImpl(this.gridTable, "tr"); |
|
} |
|
if(this.itemContainerTable){ |
|
this._resizeRowsImpl(this.itemContainerTable, "tr"); |
|
} |
|
}, |
|
|
|
_computeRowsHeight:function(renderData, max){ |
|
// summary: |
|
// 1. Determine if it's better to add or remove pixels |
|
// 2. distribute added/removed pixels on first and last rows. |
|
// if rows are not too small, it is not noticeable. |
|
// tags: |
|
// private |
|
|
|
var rd = renderData == null ? this.renderData : renderData; |
|
|
|
max = max || rd.sheetHeight; |
|
|
|
max--; |
|
|
|
if(has("ie") == 7){ |
|
max -= rd.rowCount; |
|
} |
|
|
|
if(rd.rowCount == 1){ |
|
rd.rowHeight = max; |
|
rd.rowHeightFirst = max; |
|
rd.rowHeightLast = max; |
|
return; |
|
} |
|
|
|
var count = rd.expandedRow == null ? rd.rowCount : rd.rowCount-1; |
|
var rhx = max / count; |
|
var rhf, rhl, rh; |
|
|
|
var diffMin = max - (Math.floor(rhx) * count); |
|
var diffMax = Math.abs(max - (Math.ceil(rhx) * count)); |
|
var diff; |
|
|
|
var sign = 1; |
|
if(diffMin < diffMax){ |
|
rh = Math.floor(rhx); |
|
diff = diffMin; |
|
}else{ |
|
sign = -1; |
|
rh = Math.ceil(rhx); |
|
diff = diffMax; |
|
} |
|
rhf = rh + sign * Math.floor(diff/2); |
|
rhl = rhf + sign * (diff%2); |
|
|
|
rd.rowHeight = rh; |
|
rd.rowHeightFirst = rhf; |
|
rd.rowHeightLast = rhl; |
|
}, |
|
|
|
_getRowHeight: function(index){ |
|
// tags: |
|
// private |
|
|
|
var rd = this.renderData; |
|
if(index == rd.expandedRow){ |
|
return rd.expandedRowHeight; |
|
} else if(rd.expandedRow == 0 && index == 1 || index == 0){ |
|
return rd.rowHeightFirst; |
|
} else if(rd.expandedRow == this.renderData.rowCount-1 && |
|
index == this.renderData.rowCount-2 || |
|
index == this.renderData.rowCount-1){ |
|
return rd.rowHeightLast; |
|
}else{ |
|
return rd.rowHeight; |
|
} |
|
}, |
|
|
|
_resizeRowsImpl: function(tableNode, query){ |
|
// tags: |
|
// private |
|
dojo.query(query, tableNode).forEach(function(tr, i){ |
|
domStyle.set(tr, "height", this._getRowHeight(i)+"px"); |
|
}, this); |
|
}, |
|
|
|
//////////////////////////////////////////// |
|
// |
|
// Item renderers |
|
// |
|
/////////////////////////////////////////// |
|
|
|
_setHorizontalRendererAttr: function(value){ |
|
this._destroyRenderersByKind("horizontal"); |
|
this._set("horizontalRenderer", value); |
|
}, |
|
|
|
_setLabelRendererAttr: function(value){ |
|
this._destroyRenderersByKind("label"); |
|
this._set("labelRenderer", value); |
|
}, |
|
|
|
_destroyExpandRenderer: function(renderer){ |
|
// summary: |
|
// Destroys the expand renderer. |
|
// renderer: dojox/calendar/_RendererMixin |
|
// The item renderer to destroy. |
|
// tags: |
|
// protected |
|
|
|
if(renderer["destroyRecursive"]){ |
|
renderer.destroyRecursive(); |
|
} |
|
|
|
html.destroy(renderer.domNode); |
|
}, |
|
|
|
_setExpandRendererAttr: function(value){ |
|
while(this._ddRendererList.length>0){ |
|
this._destroyExpandRenderer(this._ddRendererList.pop()); |
|
} |
|
|
|
var pool = this._ddRendererPool; |
|
if(pool){ |
|
while(pool.length > 0){ |
|
this._destroyExpandRenderer(pool.pop()); |
|
} |
|
} |
|
this._set("expandRenderer", value); |
|
}, |
|
|
|
_ddRendererList: null, |
|
_ddRendererPool: null, |
|
|
|
_getExpandRenderer: function(date, items, rowIndex, colIndex, expanded){ |
|
// tags: |
|
// private |
|
|
|
if(this.expandRenderer == null){ |
|
return null; |
|
} |
|
|
|
var ir = this._ddRendererPool.pop(); |
|
if(ir == null){ |
|
ir = new this.expandRenderer(); |
|
} |
|
|
|
this._ddRendererList.push(ir); |
|
|
|
ir.set("owner", this); |
|
ir.set("date", date); |
|
ir.set("items", items); |
|
ir.set("rowIndex", rowIndex); |
|
ir.set("columnIndex", colIndex); |
|
ir.set("expanded", expanded); |
|
return ir; |
|
}, |
|
|
|
_recycleExpandRenderers: function(remove){ |
|
// tags: |
|
// private |
|
|
|
for(var i=0; i<this._ddRendererList.length; i++){ |
|
var ir = this._ddRendererList[i]; |
|
ir.set("Up", false); |
|
ir.set("Down", false); |
|
if(remove){ |
|
ir.domNode.parentNode.removeChild(ir.domNode); |
|
} |
|
domStyle.set(ir.domNode, "display", "none"); |
|
} |
|
this._ddRendererPool = this._ddRendererPool.concat(this._ddRendererList); |
|
this._ddRendererList = []; |
|
}, |
|
|
|
_defaultItemToRendererKindFunc:function(item){ |
|
// tags: |
|
// private |
|
var dur = Math.abs(this.renderData.dateModule.difference(item.startTime, item.endTime, "minute")); |
|
return dur >= 1440 ? "horizontal" : "label"; |
|
}, |
|
|
|
//////////////////////////////////////////// |
|
// |
|
// Layout |
|
// |
|
/////////////////////////////////////////// |
|
|
|
// naturalRowHeight: Integer[] |
|
// After an item layout has been done, contains for each row the natural height of the row. |
|
// Ie. the height, in pixels, needed to display all the item renderers. |
|
naturalRowsHeight: null, |
|
|
|
_roundItemToDay: function(item){ |
|
// tags: |
|
// private |
|
|
|
var s = item.startTime, e = item.endTime; |
|
|
|
if(!this.isStartOfDay(s)){ |
|
s = this.floorToDay(s, false, this.renderData); |
|
} |
|
if(!this.isStartOfDay(e)){ |
|
e = this.renderData.dateModule.add(e, "day", 1); |
|
e = this.floorToDay(e, true); |
|
} |
|
return {startTime:s, endTime:e}; |
|
}, |
|
|
|
_sortItemsFunction: function(a, b){ |
|
// tags: |
|
// private |
|
|
|
if(this.roundToDay){ |
|
a = this._roundItemToDay(a); |
|
b = this._roundItemToDay(b); |
|
} |
|
var res = this.dateModule.compare(a.startTime, b.startTime); |
|
if(res == 0){ |
|
res = -1 * this.dateModule.compare(a.endTime, b.endTime); |
|
} |
|
return res; |
|
}, |
|
|
|
_overlapLayoutPass3: function(lanes){ |
|
// summary: |
|
// Third pass of the overlap layout (optional). Compute the number of lanes used by sub interval. |
|
// lanes: Object[] |
|
// The array of lanes. |
|
// tags: |
|
// private |
|
|
|
var pos=0, posEnd=0; |
|
var res = []; |
|
|
|
var refPos = domGeometry.position(this.gridTable).x; |
|
|
|
for(var col=0; col<this.renderData.columnCount; col++){ |
|
|
|
var stop = false; |
|
var colPos = domGeometry.position(this._getCellAt(0, col)); |
|
pos = colPos.x - refPos; |
|
posEnd = pos + colPos.w; |
|
|
|
for(var lane=lanes.length-1; lane>=0 && !stop; lane--){ |
|
for (var i=0; i<lanes[lane].length; i++){ |
|
var item = lanes[lane][i]; |
|
stop = item.start < posEnd && pos < item.end; |
|
if(stop){ |
|
res[col] = lane + 1; |
|
break; |
|
} |
|
} |
|
} |
|
|
|
if(!stop){ |
|
res[col] = 0; |
|
} |
|
} |
|
|
|
return res; |
|
}, |
|
|
|
applyRendererZIndex: function(item, renderer, hovered, selected, edited, focused){ |
|
// summary: |
|
// Applies the z-index to the renderer based on the state of the item. |
|
// This methods is setting a z-index of 20 is the item is selected or edited |
|
// and the current lane value computed by the overlap layout (i.e. the renderers |
|
// are stacked according to their lane). |
|
// item: Object |
|
// The render item. |
|
// renderer: Object |
|
// A renderer associated with the render item. |
|
// hovered: Boolean |
|
// Whether the item is hovered or not. |
|
// selected: Boolean |
|
// Whether the item is selected or not. |
|
// edited: Boolean |
|
// Whether the item is being edited not not. |
|
// focused: Boolean |
|
// Whether the item is focused not not. |
|
// tags: |
|
// private |
|
|
|
domStyle.set(renderer.container, {"zIndex": edited || selected ? renderer.renderer.mobile ? 100 : 0: item.lane == undefined ? 1 : item.lane+1}); |
|
}, |
|
|
|
_layoutDecorationRenderers: function(renderData){ |
|
// tags: |
|
// private |
|
if(renderData == null || renderData.decorationItems == null || renderData.rowHeight <= 0){ |
|
return; |
|
} |
|
|
|
if(!this.gridTable || this._expandAnimation != null || |
|
this.horizontalDecorationRenderer == null){ |
|
this.decorationRendererManager.recycleItemRenderers(); |
|
return; |
|
} |
|
|
|
this._layoutStep = renderData.columnCount; |
|
this.renderData.gridTablePosX = domGeometry.position(this.gridTable).x; |
|
|
|
this.inherited(arguments); |
|
}, |
|
|
|
_layoutRenderers: function(renderData){ |
|
// tags: |
|
// private |
|
if(renderData == null || renderData.items == null || renderData.rowHeight <= 0){ |
|
return; |
|
} |
|
|
|
if(!this.gridTable || this._expandAnimation != null || |
|
(this.horizontalRenderer == null && this.labelRenderer == null)){ |
|
this._recycleItemRenderers(); |
|
return; |
|
} |
|
|
|
this.renderData.gridTablePosX = domGeometry.position(this.gridTable).x; |
|
this._layoutStep = renderData.columnCount; |
|
this._recycleExpandRenderers(); |
|
this._hiddenItems = []; |
|
this._offsets = []; |
|
this.naturalRowsHeight = []; |
|
|
|
this.inherited(arguments); |
|
}, |
|
|
|
_offsets: null, |
|
|
|
_layoutInterval: function(/*Object*/renderData, /*Integer*/index, /*Date*/start, /*Date*/end, /*Object[]*/items, /*String*/itemsType){ |
|
// tags: |
|
// private |
|
|
|
if(this.renderData.cells == null){ |
|
return; |
|
} |
|
|
|
if(itemsType === "dataItems"){ |
|
|
|
var horizontalItems = []; |
|
var labelItems = []; |
|
|
|
for(var i=0; i<items.length; i++){ |
|
var item = items[i]; |
|
var kind = this._itemToRendererKind(item); |
|
if(kind == "horizontal"){ |
|
horizontalItems.push(item); |
|
}else if(kind == "label"){ |
|
labelItems.push(item); |
|
} |
|
} |
|
|
|
var expIndex = this.getExpandedRowIndex(); |
|
|
|
if(expIndex != -1 && expIndex != index){ |
|
return; // when row is expanded, layout only expanded row |
|
} |
|
|
|
var offsets; |
|
|
|
var hiddenItems = []; |
|
|
|
var hItems = null; |
|
var hOffsets = []; |
|
if(horizontalItems.length > 0 && this.horizontalRenderer){ |
|
var hItems = this._createHorizontalLayoutItems(index, start, end, horizontalItems, itemsType); |
|
var hOverlapLayout = this._computeHorizontalOverlapLayout(hItems, hOffsets); |
|
} |
|
|
|
var lItems; |
|
var lOffsets = []; |
|
if(labelItems.length > 0 && this.labelRenderer){ |
|
lItems = this._createLabelLayoutItems(index, start, end, labelItems); |
|
this._computeLabelOffsets(lItems, lOffsets); |
|
} |
|
|
|
var hasHiddenItems = this._computeColHasHiddenItems(index, hOffsets, lOffsets); |
|
|
|
if(hItems != null){ |
|
this._layoutHorizontalItemsImpl(index, hItems, hOverlapLayout, hasHiddenItems, hiddenItems, itemsType); |
|
} |
|
|
|
if(lItems != null){ |
|
this._layoutLabelItemsImpl(index, lItems, hasHiddenItems, hiddenItems, hOffsets, itemsType); |
|
} |
|
|
|
this._layoutExpandRenderers(index, hasHiddenItems, hiddenItems); |
|
|
|
this._hiddenItems[index] = hiddenItems; |
|
}else{ // itemsType === "decorationItems" |
|
|
|
if(this.horizontalDecorationRenderer){ |
|
var hItems = this._createHorizontalLayoutItems(index, start, end, items, itemsType); |
|
if(hItems != null){ |
|
this._layoutHorizontalItemsImpl(index, hItems, null, false, null, itemsType); |
|
} |
|
} |
|
} |
|
}, |
|
|
|
_createHorizontalLayoutItems: function(/*Integer*/index, /*Date*/startTime, /*Date*/endTime, /*Object[]*/items, /*String*/itemsType){ |
|
// tags: |
|
// private |
|
|
|
var rd = this.renderData; |
|
var cal = rd.dateModule; |
|
var sign = rd.rtl ? -1 : 1; |
|
var layoutItems = []; |
|
var isDecoration = itemsType === "decorationItems"; |
|
|
|
// step 1: compute projected position and size |
|
for(var i = 0; i < items.length; i++){ |
|
|
|
var item = items[i]; |
|
var overlap = this.computeRangeOverlap(rd, item.startTime, item.endTime, startTime, endTime); |
|
|
|
var startOffset = cal.difference(startTime, this.floorToDay(overlap[0], false, rd), "day"); |
|
var dayStart = rd.dates[index][startOffset]; |
|
|
|
var celPos = domGeometry.position(this._getCellAt(index, startOffset, false)); |
|
var start = celPos.x - rd.gridTablePosX; |
|
if(rd.rtl){ |
|
start += celPos.w; |
|
} |
|
|
|
if(isDecoration && !item.isAllDay || !isDecoration && !this.roundToDay && !item.allDay){ |
|
start += sign * this.computeProjectionOnDate(rd, dayStart, overlap[0], celPos.w); |
|
} |
|
|
|
start = Math.ceil(start); |
|
|
|
var endOffset = cal.difference(startTime, this.floorToDay(overlap[1], false, rd), "day"); |
|
|
|
var end; |
|
if(endOffset > rd.columnCount-1){ |
|
celPos = domGeometry.position(this._getCellAt(index, rd.columnCount-1, false)); |
|
if(rd.rtl){ |
|
end = celPos.x - rd.gridTablePosX; |
|
}else{ |
|
end = celPos.x - rd.gridTablePosX + celPos.w; |
|
} |
|
}else{ |
|
dayStart = rd.dates[index][endOffset]; |
|
celPos = domGeometry.position(this._getCellAt(index, endOffset, false)); |
|
end = celPos.x - rd.gridTablePosX; |
|
|
|
if(rd.rtl){ |
|
end += celPos.w; |
|
} |
|
|
|
if(!isDecoration && this.roundToDay){ |
|
if(!this.isStartOfDay(overlap[1])){ |
|
end += sign * celPos.w; |
|
} |
|
}else{ |
|
end += sign * this.computeProjectionOnDate(rd, dayStart, overlap[1], celPos.w); |
|
} |
|
} |
|
|
|
end = Math.floor(end); |
|
|
|
if(rd.rtl){ |
|
var t = end; |
|
end = start; |
|
start = t; |
|
} |
|
|
|
if(end > start){ // invalid items are not displayed |
|
var litem = lang.mixin({ |
|
start: start, |
|
end: end, |
|
range: overlap, |
|
item: item, |
|
startOffset: startOffset, |
|
endOffset: endOffset |
|
}, item); |
|
layoutItems.push(litem); |
|
} |
|
} |
|
return layoutItems; |
|
}, |
|
|
|
_computeHorizontalOverlapLayout: function(layoutItems, offsets){ |
|
// tags: |
|
// private |
|
|
|
var rd = this.renderData; |
|
var irHeight = this.horizontalRendererHeight; |
|
var overlapLayoutRes = this.computeOverlapping(layoutItems, this._overlapLayoutPass3); |
|
var vOverlap = this.percentOverlap / 100; |
|
|
|
for(var i=0; i<rd.columnCount; i++){ |
|
var numLanes = overlapLayoutRes.addedPassRes[i]; |
|
var index = rd.rtl ? rd.columnCount - i - 1 : i; |
|
if(vOverlap == 0){ |
|
offsets[index] = numLanes == 0 ? 0 : numLanes == 1 ? irHeight : irHeight + (numLanes-1) * (irHeight + this.verticalGap); |
|
}else{ |
|
offsets[index] = numLanes == 0 ? 0 : numLanes * irHeight - (numLanes-1) * (vOverlap * irHeight) + this.verticalGap; |
|
} |
|
offsets[index] += this.cellPaddingTop; |
|
} |
|
return overlapLayoutRes; |
|
}, |
|
|
|
_createLabelLayoutItems: function(/*Integer*/index, /*Date*/startTime, /*Date*/endTime, /*Object[]*/items){ |
|
// tags: |
|
// private |
|
|
|
if(this.labelRenderer == null){ |
|
return; |
|
} |
|
|
|
var d; |
|
var rd = this.renderData; |
|
var cal = rd.dateModule; |
|
|
|
var layoutItems = []; |
|
|
|
for(var i = 0; i < items.length; i++){ |
|
var item = items[i]; |
|
|
|
d = this.floorToDay(item.startTime, false, rd); |
|
|
|
var comp = this.dateModule.compare; |
|
|
|
// iterate on columns overlapped by this item to create one item per column |
|
//while(d < item.endTime && d < rd.endTime){ |
|
while(comp(d, item.endTime) == -1 && comp(d, endTime) == -1){ |
|
|
|
var dayEnd = cal.add(d, "day", 1); |
|
dayEnd = this.floorToDay(dayEnd, true); |
|
|
|
var overlap = this.computeRangeOverlap(rd, item.startTime, item.endTime, d, dayEnd); |
|
var startOffset = cal.difference(startTime, this.floorToDay(overlap[0], false, rd), "day"); |
|
|
|
if(startOffset >= this.columnCount){ |
|
// If the offset is greater than the column count |
|
// the item will be processed in another row. |
|
break; |
|
} |
|
|
|
if(startOffset >= 0){ |
|
var list = layoutItems[startOffset]; |
|
if(list == null){ |
|
list = []; |
|
layoutItems[startOffset] = list; |
|
} |
|
|
|
list.push(lang.mixin( |
|
{ startOffset: startOffset, |
|
range: overlap, |
|
item: item |
|
}, item)); |
|
} |
|
|
|
d = cal.add(d, "day", 1); |
|
this.floorToDay(d, true); |
|
} |
|
} |
|
return layoutItems; |
|
}, |
|
|
|
_computeLabelOffsets: function(layoutItems, offsets){ |
|
// tags: |
|
// private |
|
|
|
for(var i=0; i<this.renderData.columnCount; i++){ |
|
offsets[i] = layoutItems[i] == null ? 0 : layoutItems[i].length * (this.labelRendererHeight + this.verticalGap); |
|
} |
|
}, |
|
|
|
_computeColHasHiddenItems: function(index, hOffsets, lOffsets){ |
|
// tags: |
|
// private |
|
|
|
var res = []; |
|
var cellH = this._getRowHeight(index); |
|
var h; |
|
var maxH = 0; |
|
for(var i=0; i<this.renderData.columnCount; i++){ |
|
h = hOffsets == null || hOffsets[i] == null ? this.cellPaddingTop : hOffsets[i]; |
|
h += lOffsets == null || lOffsets[i] == null ? 0 : lOffsets[i]; |
|
if(h > maxH){ |
|
maxH = h; |
|
} |
|
res[i] = h > cellH; |
|
} |
|
|
|
this.naturalRowsHeight[index] = maxH; |
|
return res; |
|
}, |
|
|
|
_layoutHorizontalItemsImpl: function(index, layoutItems, hOverlapLayout, hasHiddenItems, hiddenItems, itemsType){ |
|
|
|
// tags: |
|
// private |
|
|
|
var rd = this.renderData; |
|
var cell = rd.cells[index]; |
|
var cellH = this._getRowHeight(index); |
|
var irHeight = this.horizontalRendererHeight; |
|
var vOverlap = this.percentOverlap / 100; |
|
|
|
for(var i=0; i<layoutItems.length; i++){ |
|
|
|
var item = layoutItems[i]; |
|
var lane = item.lane; |
|
|
|
if(itemsType === "dataItems"){ |
|
|
|
var posY = this.cellPaddingTop; |
|
|
|
if(vOverlap == 0) { |
|
//no overlap and a padding between each event |
|
posY += lane * (irHeight + this.verticalGap); |
|
} else { |
|
// an overlap |
|
posY += lane * (irHeight - vOverlap * irHeight); |
|
} |
|
|
|
var exp = false; |
|
var maxH = cellH; |
|
if(this.expandRenderer){ |
|
for(var off=item.startOffset; off<=item.endOffset; off++){ |
|
if(hasHiddenItems[off]){ |
|
exp = true; |
|
break; |
|
} |
|
} |
|
maxH = exp ? cellH - this.expandRendererHeight : cellH; |
|
} |
|
|
|
if(posY + irHeight <= maxH){ |
|
|
|
var ir = this._createRenderer(item, "horizontal", this.horizontalRenderer, "dojoxCalendarHorizontal"); |
|
|
|
var fullHeight = this.isItemBeingEdited(item) && !this.liveLayout && this._isEditing; |
|
var h = fullHeight ? cellH - this.cellPaddingTop : irHeight; |
|
var w = item.end - item.start; |
|
if (has("ie") >= 9 && item.start + w < this.itemContainer.offsetWidth) { |
|
w++; |
|
} |
|
|
|
domStyle.set(ir.container, { |
|
"top": (fullHeight ? this.cellPaddingTop : posY) + "px", |
|
"left": item.start + "px", |
|
"width": w + "px", |
|
"height": h + "px" |
|
}); |
|
|
|
this._applyRendererLayout(item, ir, cell, w, h, "horizontal"); |
|
|
|
}else{ |
|
// The items does not fit in view, fill hidden items per column |
|
for(var d=item.startOffset;d<item.endOffset;d++){ |
|
if(hiddenItems[d] == null){ |
|
hiddenItems[d] = [item.item]; |
|
}else{ |
|
hiddenItems[d].push(item.item); |
|
} |
|
} |
|
} |
|
|
|
}else{ //itemsType === "decorationItems" |
|
var ir = this.decorationRendererManager.createRenderer(item, "horizontal", this.horizontalDecorationRenderer, "dojoxCalendarDecoration"); |
|
|
|
var h = cellH; |
|
var w = item.end - item.start; |
|
if (has("ie") >= 9 && item.start + w < this.itemContainer.offsetWidth) { |
|
w++; |
|
} |
|
|
|
domStyle.set(ir.container, { |
|
"top": "0", |
|
"left": item.start + "px", |
|
"width": w + "px", |
|
"height": h + "px" |
|
}); |
|
|
|
domConstruct.place(ir.container, cell); |
|
domStyle.set(ir.container, "display", "block"); |
|
} |
|
} |
|
}, |
|
|
|
_layoutLabelItemsImpl: function(index, layoutItems, hasHiddenItems, hiddenItems, hOffsets){ |
|
// tags: |
|
// private |
|
var list, posY; |
|
var rd = this.renderData; |
|
var cell = rd.cells[index]; |
|
var cellH = this._getRowHeight(index); |
|
var irHeight = this.labelRendererHeight; |
|
var maxW = domGeometry.getMarginBox(this.itemContainer).w; |
|
|
|
for(var i=0; i<layoutItems.length; i++){ |
|
list = layoutItems[i]; |
|
|
|
if(list != null){ |
|
|
|
// sort according to start time the list of label renderers |
|
list.sort(lang.hitch(this, function(a, b){ |
|
return this.dateModule.compare(a.range[0], b.range[0]); |
|
})); |
|
|
|
var maxH = this.expandRenderer ? (hasHiddenItems[i] ? cellH - this.expandRendererHeight: cellH) : cellH; |
|
posY = hOffsets == null || hOffsets[i] == null ? this.cellPaddingTop : hOffsets[i] + this.verticalGap; |
|
var celPos = domGeometry.position(this._getCellAt(index, i)); |
|
var left = celPos.x - rd.gridTablePosX; |
|
|
|
for(var j=0; j<list.length; j++){ |
|
|
|
if(posY + irHeight + this.verticalGap <= maxH){ |
|
var item = list[j]; |
|
|
|
lang.mixin(item, { |
|
start: left, |
|
end: left + celPos.w |
|
}); |
|
|
|
var ir = this._createRenderer(item, "label", this.labelRenderer, "dojoxCalendarLabel"); |
|
|
|
var fullHeight = this.isItemBeingEdited(item) && !this.liveLayout && this._isEditing; |
|
var h = fullHeight ? this._getRowHeight(index) - this.cellPaddingTop : irHeight; |
|
|
|
if(rd.rtl){ |
|
item.start = maxW - item.end; |
|
item.end = item.start + celPos.w; |
|
} |
|
|
|
domStyle.set(ir.container, { |
|
"top": (fullHeight ? this.cellPaddingTop : posY) + "px", |
|
"left": item.start + "px", |
|
"width": celPos.w + "px", |
|
"height": h + "px" |
|
}); |
|
|
|
this._applyRendererLayout(item, ir, cell, celPos.w, h, "label"); |
|
|
|
}else{ |
|
break; |
|
} |
|
posY += irHeight + this.verticalGap; |
|
} |
|
|
|
for(var j; j<list.length; j++){ |
|
if(hiddenItems[i] == null){ |
|
hiddenItems[i] = [list[j]]; |
|
}else{ |
|
hiddenItems[i].push(list[j]); |
|
} |
|
} |
|
} |
|
} |
|
}, |
|
|
|
_applyRendererLayout: function(item, ir, cell, w, h, kind){ |
|
// tags: |
|
// private |
|
|
|
var edited = this.isItemBeingEdited(item); |
|
var selected = this.isItemSelected(item); |
|
var hovered = this.isItemHovered(item); |
|
var focused = this.isItemFocused(item); |
|
|
|
var renderer = ir.renderer; |
|
|
|
renderer.set("hovered", hovered); |
|
renderer.set("selected", selected); |
|
renderer.set("edited", edited); |
|
renderer.set("focused", this.showFocus ? focused : false); |
|
renderer.set("moveEnabled", this.isItemMoveEnabled(item._item, kind)); |
|
renderer.set("storeState", this.getItemStoreState(item)); |
|
|
|
if(kind != "label"){ |
|
renderer.set("resizeEnabled", this.isItemResizeEnabled(item, kind)); |
|
} |
|
|
|
this.applyRendererZIndex(item, ir, hovered, selected, edited, focused); |
|
|
|
if(renderer.updateRendering){ |
|
renderer.updateRendering(w, h); |
|
} |
|
|
|
domConstruct.place(ir.container, cell); |
|
domStyle.set(ir.container, "display", "block"); |
|
}, |
|
|
|
_getCellAt: function(rowIndex, columnIndex, rtl){ |
|
// tags: |
|
// private |
|
|
|
if((rtl == undefined || rtl == true) && !this.isLeftToRight()){ |
|
columnIndex = this.renderData.columnCount -1 - columnIndex; |
|
} |
|
return this.gridTable.childNodes[0].childNodes[rowIndex].childNodes[columnIndex]; |
|
}, |
|
|
|
_layoutExpandRenderers: function(index, hasHiddenItems, hiddenItems){ |
|
// tags: |
|
// private |
|
|
|
if(!this.expandRenderer){ |
|
return; |
|
} |
|
var rd = this.renderData; |
|
if(rd.expandedRow == index){ |
|
if(rd.expandedRowCol != null && rd.expandedRowCol != -1){ |
|
this._layoutExpandRendererImpl(rd.expandedRow, rd.expandedRowCol, null, true); |
|
} |
|
}else{ |
|
if(rd.expandedRow == null){ |
|
for(var i=0; i<rd.columnCount; i++){ |
|
if(hasHiddenItems[i]){ |
|
this._layoutExpandRendererImpl(index, rd.rtl ? rd.columnCount -1 -i: i, hiddenItems[i], false); |
|
} |
|
} |
|
} |
|
} |
|
}, |
|
|
|
_layoutExpandRendererImpl: function(rowIndex, colIndex, items, expanded){ |
|
// tags: |
|
// private |
|
|
|
var rd = this.renderData; |
|
var d = lang.clone(rd.dates[rowIndex][colIndex]); |
|
var ir = null; |
|
var cell = rd.cells[rowIndex]; |
|
|
|
ir = this._getExpandRenderer(d, items, rowIndex, colIndex, expanded); |
|
|
|
var dim = domGeometry.position(this._getCellAt(rowIndex, colIndex)); |
|
dim.x -= rd.gridTablePosX; |
|
this.layoutExpandRenderer(ir, d, items, dim, this.expandRendererHeight); |
|
domConstruct.place(ir.domNode, cell); |
|
domStyle.set(ir.domNode, "display", "block"); |
|
}, |
|
|
|
layoutExpandRenderer: function(renderer, date, items, cellPosition, height){ |
|
// summary: |
|
// Computes and sets the position of the expand/collapse renderers. |
|
// By default the renderer is set to take the width of the cell and is placed at the bottom of the cell. |
|
// The renderer DOM node is in a row that takes all the grid width. |
|
// renderer: Object |
|
// The renderer used in specified cell that indicates that some items cannot be displayed. |
|
// date: Date |
|
// The date displayed by the cell. |
|
// items: Object[] |
|
// The list of non visible items. |
|
// cellPosition: Object |
|
// An object that contains the position (x and y properties) and size of the cell (w and h properties). |
|
// tags: |
|
// private |
|
domStyle.set(renderer.domNode, { |
|
"left": cellPosition.x + "px", |
|
"width": cellPosition.w + "px", |
|
"height": height + "px", |
|
"top": (cellPosition.h - height -1) + "px" |
|
}); |
|
}, |
|
|
|
///////////////////////////////////////////// |
|
// |
|
// Editing |
|
// |
|
////////////////////////////////////////////// |
|
|
|
_onItemEditBeginGesture: function(e){ |
|
// tags: |
|
// private |
|
var p = this._edProps; |
|
|
|
var item = p.editedItem; |
|
var dates = e.dates; |
|
|
|
var refTime = this.newDate(p.editKind == "resizeEnd" ? item.endTime : item.startTime); |
|
|
|
if(p.rendererKind == "label"){ |
|
// noop |
|
}else if(e.editKind == "move" && (item.allDay || this.roundToDay)){ |
|
var cal = this.renderData.dateModule; |
|
p.dayOffset = cal.difference( |
|
this.floorToDay(dates[0], false, this.renderData), |
|
refTime, "day"); |
|
} // else managed in super |
|
|
|
this.inherited(arguments); |
|
}, |
|
|
|
_computeItemEditingTimes: function(item, editKind, rendererKind, times, eventSource){ |
|
// tags: |
|
// private |
|
var cal = this.renderData.dateModule; |
|
var p = this._edProps; |
|
|
|
if(rendererKind == "label"){ // noop |
|
}else if(item.allDay || this.roundToDay){ |
|
var isStartOfDay = this.isStartOfDay(times[0]); |
|
switch(editKind){ |
|
case "resizeEnd": |
|
if(!isStartOfDay && item.allDay){ |
|
times[0] = cal.add(times[0], "day", 1); // no break; |
|
} |
|
case "resizeStart": |
|
if(!isStartOfDay){ |
|
times[0] = this.floorToDay(times[0], true); |
|
} |
|
break; |
|
case "move": |
|
times[0] = cal.add(times[0], "day", p.dayOffset); |
|
break; |
|
case "resizeBoth": |
|
if(!isStartOfDay){ |
|
times[0] = this.floorToDay(times[0], true); |
|
} |
|
if(!this.isStartOfDay(times[1])){ |
|
times[1] = this.floorToDay(cal.add(times[1], "day", 1), true); |
|
} |
|
break; |
|
} |
|
|
|
}else{ |
|
times = this.inherited(arguments); |
|
} |
|
|
|
return times; |
|
}, |
|
|
|
|
|
///////////////////////////////////////////// |
|
// |
|
// Pixel to Time projection |
|
// |
|
////////////////////////////////////////////// |
|
|
|
getTime: function(e, x, y, touchIndex){ |
|
// summary: |
|
// Returns the time displayed at the specified point by this component. |
|
// e: Event |
|
// Optional mouse event. |
|
// x: Number |
|
// Position along the x-axis with respect to the sheet container used if event is not defined. |
|
// y: Number |
|
// Position along the y-axis with respect to the sheet container (scroll included) used if event is not defined. |
|
// touchIndex: Integer |
|
// If parameter 'e' is not null and a touch event, the index of the touch to use. |
|
// returns: Date |
|
|
|
var rd = this.renderData; |
|
|
|
if(e != null){ |
|
var refPos = domGeometry.position(this.itemContainer, true); |
|
|
|
if(e.touches){ |
|
|
|
touchIndex = touchIndex==undefined ? 0 : touchIndex; |
|
|
|
x = e.touches[touchIndex].pageX - refPos.x; |
|
y = e.touches[touchIndex].pageY - refPos.y; |
|
|
|
}else{ |
|
|
|
x = e.pageX - refPos.x; |
|
y = e.pageY - refPos.y; |
|
} |
|
} |
|
|
|
var r = domGeometry.getContentBox(this.itemContainer); |
|
|
|
if(x < 0){ |
|
x = 0; |
|
}else if(x > r.w){ |
|
x = r.w-1; |
|
} |
|
|
|
if(y < 0){ |
|
y = 0; |
|
}else if(y > r.h){ |
|
y = r.h-1; |
|
} |
|
|
|
// compute the date from column the time in day instead of time from start date of row to prevent DST hour offset. |
|
|
|
var w = domGeometry.getMarginBox(this.itemContainer).w; |
|
var colW = w / rd.columnCount; |
|
|
|
var row; |
|
if(rd.expandedRow == null){ |
|
row = Math.floor(y / (domGeometry.getMarginBox(this.itemContainer).h / rd.rowCount)); |
|
}else{ |
|
row = rd.expandedRow; //other rows are not usable |
|
} |
|
|
|
var r = domGeometry.getContentBox(this.itemContainer); |
|
|
|
if(rd.rtl){ |
|
x = r.w - x; |
|
} |
|
|
|
var col = Math.floor(x / colW); |
|
|
|
var tm = Math.floor((x-(col*colW)) * 1440 / colW); |
|
|
|
var date = null; |
|
if(row < rd.dates.length && col < this.renderData.dates[row].length){ |
|
date = this.newDate(this.renderData.dates[row][col]); |
|
date = this.renderData.dateModule.add(date, "minute", tm); |
|
} |
|
|
|
return date; |
|
}, |
|
|
|
///////////////////////////////////////////// |
|
// |
|
// Event management |
|
// |
|
////////////////////////////////////////////// |
|
|
|
_onGridMouseUp: function(e){ |
|
// tags: |
|
// private |
|
|
|
this.inherited(arguments); |
|
|
|
if(this._gridMouseDown){ |
|
this._gridMouseDown = false; |
|
|
|
this._onGridClick({ |
|
date: this.getTime(e), |
|
triggerEvent: e |
|
}); |
|
} |
|
}, |
|
|
|
_onGridTouchEnd: function(e){ |
|
// tags: |
|
// private |
|
this.inherited(arguments); |
|
|
|
var g = this._gridProps; |
|
|
|
if(g){ |
|
|
|
if(!this._isEditing){ |
|
|
|
// touched on grid and on touch start editing was ongoing. |
|
if(!g.fromItem && !g.editingOnStart){ |
|
this.selectFromEvent(e, null, null, true); |
|
} |
|
|
|
if(!g.fromItem){ |
|
|
|
if(this._pendingDoubleTap && this._pendingDoubleTap.grid){ |
|
|
|
this._onGridDoubleClick({ |
|
date: this.getTime(this._gridProps.event), |
|
triggerEvent: this._gridProps.event |
|
}); |
|
|
|
clearTimeout(this._pendingDoubleTap.timer); |
|
|
|
delete this._pendingDoubleTap; |
|
|
|
}else{ |
|
|
|
this._onGridClick({ |
|
date: this.getTime(this._gridProps.event), |
|
triggerEvent: this._gridProps.event |
|
}); |
|
|
|
this._pendingDoubleTap = { |
|
grid: true, |
|
timer: setTimeout(lang.hitch(this, function(){ |
|
delete this._pendingDoubleTap; |
|
}), this.doubleTapDelay) |
|
}; |
|
} |
|
} |
|
} |
|
|
|
this._gridProps = null; |
|
} |
|
}, |
|
|
|
|
|
///////////////////////////////////////////// |
|
// |
|
// Events |
|
// |
|
////////////////////////////////////////////// |
|
|
|
_onRowHeaderClick: function(e){ |
|
this._dispatchCalendarEvt(e, "onRowHeaderClick"); |
|
// tags: |
|
// private |
|
}, |
|
|
|
onRowHeaderClick: function(e){ |
|
// summary: |
|
// Event dispatched when a row header cell is clicked. |
|
// e: __HeaderClickEventArgs |
|
// Header click event. |
|
// tags: |
|
// callback |
|
}, |
|
|
|
expandRendererClickHandler: function(e, renderer){ |
|
// summary: |
|
// Default action when an expand renderer is clicked. |
|
// e: Event |
|
// The mouse event. |
|
// renderer: Object |
|
// The expand renderer. |
|
// tags: |
|
// protected |
|
|
|
event.stop(e); |
|
|
|
var ri = renderer.get("rowIndex"); |
|
var ci = renderer.get("columnIndex"); |
|
|
|
this._onExpandRendererClick(lang.mixin(this._createItemEditEvent(), { |
|
rowIndex: ri, |
|
columnIndex: ci, |
|
renderer: renderer, |
|
triggerEvent: e, |
|
date: this.renderData.dates[ri][ci] |
|
})); |
|
}, |
|
|
|
onExpandRendererClick: function(e){ |
|
// summary: |
|
// Event dispatched when an expand renderer is clicked. |
|
// e: __ExpandRendererClickEventArgs |
|
// Expand renderer click event. |
|
// tags: |
|
// callback |
|
}, |
|
|
|
_onExpandRendererClick: function(e){ |
|
|
|
this._dispatchCalendarEvt(e, "onExpandRendererClick"); |
|
|
|
if(!e.isDefaultPrevented()){ |
|
|
|
if(this.getExpandedRowIndex() != -1){ |
|
this.collapseRow(); |
|
}else{ |
|
this.expandRow(e.rowIndex, e.columnIndex); |
|
} |
|
} |
|
}, |
|
|
|
|
|
//////////////////////////////////////////// |
|
// |
|
// Editing |
|
// |
|
/////////////////////////////////////////// |
|
|
|
snapUnit: "minute", |
|
snapSteps: 15, |
|
minDurationUnit: "minute", |
|
minDurationSteps: 15, |
|
triggerExtent: 3, |
|
liveLayout: false, |
|
stayInView: true, |
|
allowStartEndSwap: true, |
|
allowResizeLessThan24H: false |
|
|
|
}); |
|
});
|
|
|