UNPKG

dojox

Version:

Dojo eXtensions, a rollup of many useful sub-projects and varying states of maturity – from very stable and robust, to alpha and experimental. See individual projects contain README files for details.

1,548 lines (1,238 loc) 40.4 kB
define([ "./ViewBase", "dijit/_TemplatedMixin", "./_ScrollBarBase", "dojo/text!./templates/MonthColumnView.html", "dojo/_base/declare", "dojo/_base/event", "dojo/_base/lang", "dojo/_base/array", "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/mouse", "dojo/query", "dojo/i18n", "dojox/html/metrics"], function( ViewBase, _TemplatedMixin, _ScrollBarBase, template, declare, event, lang, arr, has, fx, html, on, dom, domClass, domStyle, domGeometry, domConstruct, mouse, query, i18n, metrics){ /*===== var __ColumnClickEventArgs = { // summary: // A column click event. // index: Integer // The column index. // date: Date // The date displayed by the column. // triggerEvent: Event // The origin event. }; =====*/ return declare("dojox.calendar.MonthColumnView", [ViewBase, _TemplatedMixin], { // summary: // The month column view is a calendar view used to display a month per column where each cell of the column is a day. baseClass: "dojoxCalendarMonthColumnView", templateString: template, // viewKind: String // Type of the view. Used by the calendar widget to determine how to configure the view. // This view kind is "columns". viewKind: "monthColumns", // scroll container is the focusable item to enable scrolling using up and down arrows _setTabIndexAttr: "domNode", // renderData: Object // The render data is the object that contains all the properties needed to render the component. 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, // columnCount: Integer // The number of column to display (from the startDate). columnCount: 6, // daySize: Integer // The desired size in pixels of an hour on the screen. // Note that the effective size may be different as the time slot size must be an integer. daySize: 30, // showCellLabel: Boolean // Whether display or not the grid cells label (usually the day of month). showCellLabel: true, // showHiddenItems: Boolean // Whether show or not the hidden items. // By default the events that are shorter than a day are not displayed using vertical renderers by this widget. // But the grid cells that contains one or several hidden items display a decoration. showHiddenItems: true, // verticalRenderer: Class // The class use to create vertical renderers. verticalRenderer: null, // verticalDecorationRenderer: Class // The class use to create vertical decoration renderers. verticalDecorationRenderer: null, // percentOverlap: Integer // The percentage of the renderer width used to superimpose one item renderer on another // when two events are overlapping. percentOverlap: 0, // horizontalGap: Integer // The number of pixels between two item renderers. horizontalGap: 4, // columnHeaderFormatLength: String // Length of the column labels. Valid values are "wide" or "abbr". columnHeaderFormatLength: null, // gridCellDatePattern: String // The date pattern of the cell labels. By default a custom function is used to compute the label. gridCellDatePattern: null, // roundToDay: [private] Boolean roundToDay: true, // _layoutUnit: String // Unit of layout: each column is displaying a month. _layoutUnit: "month", _columnHeaderHandlers: null, constructor: function(){ this.invalidatingProperties = ["columnCount", "startDate", "daySize", "percentOverlap", "verticalRenderer", "verticalDecorationRenderer", "columnHeaderDatePattern", "horizontalGap", "scrollBarRTLPosition", "itemToRendererKindFunc", "layoutPriorityFunction", "textDir", "items", "showCellLabel", "showHiddenItems"]; this._columnHeaderHandlers = []; }, postCreate: function(){ this.inherited(arguments); this.keyboardUpDownUnit = "day"; this.keyboardUpDownSteps = 1; this.keyboardLeftRightUnit = "month"; this.keyboardLeftRightSteps = 1; this.allDayKeyboardUpDownUnit = "day"; this.allDayKeyboardUpDownSteps = 1; this.allDayKeyboardLeftRightUnit = "month"; this.allDayKeyboardLeftRightSteps = 1; }, destroy: function(preserveDom){ this._cleanupColumnHeader(); if(this.scrollBar){ this.scrollBar.destroy(preserveDom); } this.inherited(arguments); }, _scrollBar_onScroll: function(value){ // tags: // private this.scrollContainer.scrollTop = value; }, buildRendering: function(){ // tags: // private this.inherited(arguments); if(this.vScrollBar){ this.scrollBar = new _ScrollBarBase( {content: this.vScrollBarContent}, this.vScrollBar); this.scrollBar.on("scroll", lang.hitch(this, this._scrollBar_onScroll)); this._viewHandles.push( on(this.scrollContainer, mouse.wheel, dojo.hitch(this, this._mouseWheelScrollHander))); } }, postscript: function(){ this.inherited(arguments); this._initialized = true; if(!this.invalidRendering){ this.refreshRendering(); } }, _setVerticalRendererAttr: function(value){ this._destroyRenderersByKind("vertical"); this._set("verticalRenderer", value); }, _createRenderData: function(){ var rd = {}; rd.daySize = this.get("daySize"); rd.scrollbarWidth = metrics.getScrollbar().w + 1; rd.dateLocaleModule = this.dateLocaleModule; rd.dateClassObj = this.dateClassObj; rd.dateModule = this.dateModule; // arithmetics on Dates rd.dates = []; rd.columnCount = this.get("columnCount"); var d = this.get("startDate"); if (d == null){ d = new rd.dateClassObj(); } d = this.floorToMonth(d, false, rd); this.startDate = d; var currentMonth = d.getMonth(); var maxDayCount = 0; for(var col = 0; col < rd.columnCount ; col++){ var dates = []; rd.dates.push(dates); while(d.getMonth() == currentMonth){ dates.push(d); d = this.addAndFloor(d, "day", 1); } currentMonth = d.getMonth(); if(maxDayCount < dates.length){ maxDayCount = dates.length; } } rd.startTime = new rd.dateClassObj(rd.dates[0][0]); rd.endTime = new rd.dateClassObj(dates[dates.length-1]); rd.endTime = rd.dateModule.add(rd.endTime, "day", 1); rd.maxDayCount = maxDayCount; rd.sheetHeight = rd.daySize * maxDayCount; if(this.displayedItemsInvalidated && !this._isEditing){ this.displayedItemsInvalidated = false; this._computeVisibleItems(rd); }else if (this.renderData){ rd.items = this.renderData.items; } if(this.displayedDecorationItemsInvalidated){ // while editing in no live layout we must not to recompute items (duplicate renderers) rd.decorationItems = this.decorationStoreManager._computeVisibleItems(rd); }else if (this.renderData){ rd.decorationItems = this.renderData.decorationItems; } return rd; }, _validateProperties: function() { this.inherited(arguments); if (this.columnCount<1 || isNaN(this.columnCount)){ this.columnCount = 1; } if(this.daySize<5 || isNaN(this.daySize)){ this.daySize = 5; } }, _setStartDateAttr: function(value){ this.displayedItemsInvalidated = true; this._set("startDate", value); }, _setColumnCountAttr: function(value){ this.displayedItemsInvalidated = true; this._set("columnCount", value); }, __fixEvt:function(e){ e.sheet = "primary"; e.source = this; return e; }, ////////////////////////////////////////// // // Formatting functions // ////////////////////////////////////////// _formatColumnHeaderLabel: function(/*Date*/d){ // summary: // Computes the column header label for the specified date. // d: Date // The date to format // tags: // protected var len = "wide"; if(this.columnHeaderFormatLength){ len = this.columnHeaderFormatLength; } var months = this.renderData.dateLocaleModule.getNames("months", len, "standAlone"); return months[d.getMonth()]; }, // gridCellDatePattern: String // Custom date/time pattern for cell labels to override default one coming from the CLDR. // See dojo/date/locale documentation for format string. gridCellDatePattern: 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>gridCellDatePattern</code> // property 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 format, rb; if(d == null){ return ""; } if(this.gridCellPattern){ return this.renderData.dateLocaleModule.format(d, { selector: 'date', datePattern: this.gridCellDatePattern }); }else{ rb = i18n.getLocalization("dojo.cldr", this._calendar); format = rb["dateFormatItem-d"]; var days = this.renderData.dateLocaleModule.getNames("days", "abbr", "standAlone"); return days[d.getDay()].substring(0, 1) + " " + this.renderData.dateLocaleModule.format(d, { selector: 'date', datePattern: format }); } }, ////////////////////////////////////////// // // Time of day management // ////////////////////////////////////////// // scrollPosition: Integer // The scroll position of the view. scrollPosition: null, // scrollBarRTLPosition: String // Position of the scroll bar in right-to-left display. // Valid values are "left" and "right", default value is "left". scrollBarRTLPosition: "left", _setScrollPositionAttr: function(value){ this._setScrollPosition(value.date, value.duration, value.easing); }, _getScrollPositionAttr: function(){ return {date: (this.scrollContainer.scrollTop / this.daySize) + 1}; }, _setScrollPosition: function(date, maxDuration, easing){ // tags: // private if(date < 1){ date = 1; }else if(date>31){ date = 31; } var position = (date-1) * this.daySize; if(maxDuration) { if(this._scrollAnimation){ this._scrollAnimation.stop(); } var duration = Math.abs(((position - this.scrollContainer.scrollTop) * maxDuration) / this.renderData.sheetHeight); this._scrollAnimation = new fx.Animation({ curve: [this.scrollContainer.scrollTop, position], duration: duration, easing: easing, onAnimate: lang.hitch(this, function(position) { this._setScrollImpl(position); }) }); this._scrollAnimation.play(); }else{ this._setScrollImpl(position); } }, _setScrollImpl: function(v){ // tags: // private this.scrollContainer.scrollTop = v; if(this.scrollBar){ this.scrollBar.set("value", v); } }, ensureVisibility: function(start, end, visibilityTarget, margin, duration){ // summary: // Scrolls the view if the [start, end] time range is not visible or only partially visible. // start: Date // Start time of the range of interest. // end: Date // End time of the range of interest. // margin: Integer // Margin in minutes around the time range. // visibilityTarget: String // The end(s) of the time range to make visible. // Valid values are: "start", "end", "both". // duration: Number // Optional, the maximum duration of the scroll animation. margin = margin == undefined ? 1 : margin; if(this.scrollable && this.autoScroll){ var s = start.getDate() - margin; // -1 because day of months starts at 1 and not 0 if(this.isStartOfDay(end)){ end = this._waDojoxAddIssue(end, "day", -1); } var e = end.getDate() + margin; var viewStart = this.get("scrollPosition").date; var r = domGeometry.getContentBox(this.scrollContainer); var viewEnd = (this.get("scrollPosition").date + (r.h/this.daySize)); var visible = false; var target = null; switch(visibilityTarget){ case "start": visible = s >= viewStart && s <= viewEnd; target = s ; break; case "end": visible = e >= viewStart && e <= viewEnd; target = e - (viewEnd - viewStart); break; case "both": visible = s >= viewStart && e <= viewEnd; target = s; break; } if(!visible){ this._setScrollPosition(target, duration); } } }, scrollView: function(dir){ // summary: // Scrolls the view to the specified direction of one time slot duration. // dir: Integer // Direction of the scroll. Valid values are -1 and 1. // var pos = this.get("scrollPosition").date + dir; this._setScrollPosition(pos); }, _mouseWheelScrollHander: function(e){ // summary: // Mouse wheel handler. // tags: // protected this.scrollView(e.wheelDelta > 0 ? -1 : 1); }, ////////////////////////////////////////// // // HTML structure management // ////////////////////////////////////////// refreshRendering: function(){ if(!this._initialized){ 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(/*Object*/renderData, /*Object*/oldRenderData){ // tags: // private domStyle.set(this.sheetContainer, "height", renderData.sheetHeight + "px"); // padding for the scroll bar. this._configureScrollBar(renderData); this._buildColumnHeader(renderData, oldRenderData); this._buildGrid(renderData, oldRenderData); this._buildItemContainer(renderData, oldRenderData); }, _configureScrollBar: function(renderData){ // summary: // Sets the scroll bar size and position. // renderData: Object // The render data. // tags: // protected if(has("ie") && this.scrollBar){ domStyle.set(this.scrollBar.domNode, "width", (renderData.scrollbarWidth + 1) + "px"); } var atRight = this.isLeftToRight() ? true : this.scrollBarRTLPosition == "right"; var rPos = atRight ? "right" : "left"; var lPos = atRight? "left" : "right"; if(this.scrollBar){ this.scrollBar.set("maximum", renderData.sheetHeight); domStyle.set(this.scrollBar.domNode, rPos, 0); domStyle.set(this.scrollBar.domNode, lPos, "auto"); } domStyle.set(this.scrollContainer, rPos, renderData.scrollbarWidth + "px"); domStyle.set(this.scrollContainer, lPos, "0"); domStyle.set(this.columnHeader, rPos, renderData.scrollbarWidth + "px"); domStyle.set(this.columnHeader, lPos, "0"); if(this.buttonContainer && this.owner != null && this.owner.currentView == this){ domStyle.set(this.buttonContainer, rPos, renderData.scrollbarWidth + "px"); domStyle.set(this.buttonContainer, lPos, "0"); } }, _columnHeaderClick: function(e){ // tags: // private event.stop(e); var index = query("td", this.columnHeaderTable).indexOf(e.currentTarget); this._onColumnHeaderClick({ index: index, date: this.renderData.dates[index][0], triggerEvent: e }); }, _buildColumnHeader: function(renderData, 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._cleanupColumnHeader(); 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); var h = []; h.push(on(td, "click", lang.hitch(this, this._columnHeaderClick))); if(has("touch-events")){ h.push(on(td, "touchstart", function(e){ event.stop(e); domClass.add(e.currentTarget, "Active"); })); h.push(on(td, "touchend", function(e){ event.stop(e); domClass.remove(e.currentTarget, "Active"); })); }else{ h.push(on(td, "mousedown", function(e){ event.stop(e); domClass.add(e.currentTarget, "Active"); })); h.push(on(td, "mouseup", function(e){ event.stop(e); domClass.remove(e.currentTarget, "Active"); })); h.push(on(td, "mouseover", function(e){ event.stop(e); domClass.add(e.currentTarget, "Hover"); })); h.push(on(td, "mouseout", function(e){ event.stop(e); domClass.remove(e.currentTarget, "Hover"); })); } this._columnHeaderHandlers.push(h); } }else{ // deletion count = -count; for(var i=0; i < count; i++){ td = tr.lastChild; tr.removeChild(td); domConstruct.destroy(td); var list = this._columnHeaderHandlers.pop(); while(list.length>0){ list.pop().remove(); } } } // fill & configure query("td", table).forEach(function(td, i){ td.className = ""; if(i == 0){ domClass.add(td, "first-child"); }else if(i == this.renderData.columnCount-1){ domClass.add(td, "last-child"); } var d = renderData.dates[i][0]; this._setText(td, this._formatColumnHeaderLabel(d)); this.styleColumnHeaderCell(td, d, renderData); }, this); }, _cleanupColumnHeader: function(){ // tags: // private while(this._columnHeaderHandlers.length > 0){ var list = this._columnHeaderHandlers.pop(); while(list.length > 0){ list.pop().remove(); } } }, styleColumnHeaderCell: function(node, date, renderData){ // summary: // Styles the CSS classes to the node that displays a column header cell. // By default this method is does nothing and is designed to be overridden. // 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 }, _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; } domStyle.set(table, "height", renderData.sheetHeight + "px"); var rowDiff = renderData.maxDayCount - (oldRenderData ? oldRenderData.maxDayCount : 0); var addRows = rowDiff > 0; var colDiff = 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._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.maxDayCount; 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.maxDayCount - 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){ 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.maxDayCount-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 = null; if(row < renderData.dates[col].length) { d = renderData.dates[col][row]; } var span = query("span", td)[0]; this._setText(span, this.showCellLabel ? this._formatGridCellLabel(d, row, col): null); this.styleGridCell(td, d, col, row, 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, col, row, renderData){ // summary: // Styles the CSS classes to the node that displays a column. // 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, // - 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. // col: Integer // The column index of this cell. // row: Integer // The row index of this cell. // renderData: Object // The render data. // tags: // protected if(date == null){ return; } domClass.add(node, this._cssDays[date.getDay()]); if(this.isToday(date)){ domClass.add(node, "dojoxCalendarToday"); }else if(this.isWeekEnd(date)){ domClass.add(node, "dojoxCalendarWeekend"); } }, styleGridCell: function(node, date, col, row, renderData){ // summary: // Styles the CSS classes to the node that displays a column. // 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. // col: Integer // The column index of this cell. // row: Integer // The row index of this cell. // renderData: Object // The render data. // tags: // protected if(this.styleGridCellFunc){ this.styleGridCellFunc(node, date, col, row, renderData); }else{ this.defaultStyleGridCell(node, date, col, row, 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 bgCols = []; domStyle.set(table, "height", renderData.sheetHeight + "px"); 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._itemTableSave == null){ this._itemTableSave = lang.clone(table); }else if(count < 0){ this.itemContainer.removeChild(table); this._recycleItemRenderers(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 trs = query("tr", table); var tbody, tr, td; if (tbodies.length == 1){ tbody = tbodies[0]; }else{ tbody = domConstruct.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); domConstruct.create("div", {"className": "dojoxCalendarContainerColumn"}, td); } }else{ // deletion count = -count; for(var i=0; i < count; i++){ tr.removeChild(tr.lastChild); } } query("td>div", table).forEach(function(div, i){ domStyle.set(div, { "height": renderData.sheetHeight + "px" }); bgCols.push(div); }, this); renderData.cells = bgCols; }, /////////////////////////////////////////////////////////////// // // Layout // /////////////////////////////////////////////////////////////// _overlapLayoutPass2: function(lanes){ // summary: // Second pass of the overlap layout (optional). Compute the extent of each layout item. // lanes: // The array of lanes. // tags: // private var i,j,lane, layoutItem; // last lane, no extent possible lane = lanes[lanes.length-1]; for(j = 0; j < lane.length; j++){ lane[j].extent = 1; } for(i=0; i<lanes.length-1; i++){ lane = lanes[i]; for(var j=0; j<lane.length; j++){ layoutItem = lane[j]; // if item was already overlapping another one there is no extent possible. if(layoutItem.extent == -1){ layoutItem.extent = 1; var space = 0; var stop = false; for(var k = i + 1; k < lanes.length && !stop; k++){ var ccol = lanes[k]; for(var l = 0; l < ccol.length && !stop; l++){ var layoutItem2 = ccol[l]; if(layoutItem.start < layoutItem2.end && layoutItem2.start < layoutItem.end){ stop = true; } } if(!stop){ //no hit in the entire lane space++; } } layoutItem.extent += space; } } } }, _defaultItemToRendererKindFunc: function(item){ // tags: // private if(item.allDay){ return "vertical"; } var dur = Math.abs(this.renderData.dateModule.difference(item.startTime, item.endTime, "minute")); return dur >= 1440 ? "vertical" : null; }, _layoutRenderers: function(renderData){ this.hiddenEvents = {}; this.inherited(arguments); }, _layoutInterval: function(/*Object*/renderData, /*Integer*/index, /*Date*/start, /*Date*/end, /*Object[]*/items, /*String*/itemsType){ // tags: // private var verticalItems = []; var hiddenItems = []; renderData.colW = this.itemContainer.offsetWidth / renderData.columnCount; if(itemsType === "dataItems"){ for(var i=0; i<items.length; i++){ var item = items[i]; if(this._itemToRendererKind(item) == "vertical"){ verticalItems.push(item); }else if(this.showHiddenItems){ hiddenItems.push(item); } } if(verticalItems.length > 0){ this._layoutVerticalItems(renderData, index, start, end, verticalItems, itemsType); } if(hiddenItems.length > 0){ this._layoutBgItems(renderData, index, start, end, hiddenItems); } }else{ // itemsType === "decorationItems" this._layoutVerticalItems(renderData, index, start, end, items, itemsType); } }, _dateToYCoordinate: function(renderData, d, start){ // tags: // private var pos = 0; if(start || d.getHours() != 0 || d.getMinutes() != 0){ pos = (d.getDate()-1) * this.renderData.daySize; }else{ var d2 = this._waDojoxAddIssue(d, "day", -1); pos = this.renderData.daySize + ((d2.getDate()-1) * this.renderData.daySize); } pos += (d.getHours()*60+d.getMinutes())*this.renderData.daySize/1440; return pos; }, _layoutVerticalItems: function(/*Object*/renderData, /*Integer*/index, /*Date*/startTime, /*Date*/endTime, /*Object[]*/items, /*String*/itemsType){ // tags: // private if(this.verticalRenderer == null){ return; } var cell = renderData.cells[index]; var layoutItems = []; // step 1 compute projected position and size for(var i = 0; i < items.length; i++){ var item = items[i]; var overlap = this.computeRangeOverlap(renderData, item.startTime, item.endTime, startTime, endTime); var top = this._dateToYCoordinate(renderData, overlap[0], true); var bottom = this._dateToYCoordinate(renderData, overlap[1], false); if (bottom > top){ var litem = lang.mixin({ start: top, end: bottom, range: overlap, item: item }, item); layoutItems.push(litem); } } // step 2: compute overlapping layout var numLanes = itemsType === "dataItems" ? this.computeOverlapping(layoutItems, this._overlapLayoutPass2).numLanes : 1; var hOverlap = this.percentOverlap / 100; // step 3: create renderers and apply layout for(i=0; i<layoutItems.length; i++){ item = layoutItems[i]; var lane = item.lane; var extent = item.extent; var ir = null; if(itemsType === "dataItems"){ var w; var posX; if(hOverlap == 0) { //no overlap and a padding between each event w = numLanes == 1 ? renderData.colW : ((renderData.colW - (numLanes - 1) * this.horizontalGap)/ numLanes); posX = lane * (w + this.horizontalGap); w = extent == 1 ? w : w * extent + (extent-1) * this.horizontalGap; w = 100 * w / renderData.colW; posX = 100 * posX / renderData.colW; } else { // an overlap w = numLanes == 1 ? 100 : (100 / (numLanes - (numLanes - 1) * hOverlap)); posX = lane * (w - hOverlap*w); w = extent == 1 ? w : w * ( extent - (extent-1) * hOverlap); } ir = this._createRenderer(item, "vertical", this.verticalRenderer, "dojoxCalendarVertical"); domStyle.set(ir.container, { "top": item.start + "px", "left": posX + "%", "width": w + "%", "height": (item.end-item.start+1) + "px" }); 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("storeState", this.getItemStoreState(item)); renderer.set("moveEnabled", this.isItemMoveEnabled(item._item, "vertical")); renderer.set("resizeEnabled", this.isItemResizeEnabled(item._item, "vertical")); this.applyRendererZIndex(item, ir, hovered, selected, edited, focused); if(renderer.updateRendering){ renderer.updateRendering(w, item.end-item.start+1); } }else{ //itemsType === "decorationItems" ir = this.decorationRendererManager.createRenderer(item, "vertical", this.verticalDecorationRenderer, "dojoxCalendarDecoration"); domStyle.set(ir.container, { "top": item.start + "px", "left": "0", "width": "100%", "height": (item.end-item.start+1) + "px" }); } 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]; }, invalidateLayout: function(){ //make sure to clear hiddens object state query("td", this.gridTable).forEach(function(td){ domClass.remove(td, "dojoxCalendarHiddenEvents"); }); this.inherited(arguments); }, _layoutBgItems: function(/*Object*/renderData, /*Integer*/col, /*Date*/startTime, /*Date*/endTime, /*Object[]*/items){ // tags: // private var bgItems = {}; for(var i = 0; i < items.length; i++){ var item = items[i]; var overlap = this.computeRangeOverlap(renderData, item.startTime, item.endTime, startTime, endTime); var start = overlap[0].getDate()-1; // handle use case where end time is first day of next month. var end; if(this.isStartOfDay(overlap[1])){ end = this._waDojoxAddIssue(overlap[1], "day", -1); end = end.getDate()-1; }else{ end = overlap[1].getDate()-1; } for (var d=start; d<=end; d++){ bgItems[d] = true; } } for(var row in bgItems) { if(bgItems[row]){ var node = this._getCellAt(row, col, false); domClass.add(node, "dojoxCalendarHiddenEvents"); } } }, _sortItemsFunction: function(a, b){ // tags: // private var res = this.dateModule.compare(a.startTime, b.startTime); if(res == 0){ res = -1 * this.dateModule.compare(a.endTime, b.endTime); } return this.isLeftToRight() ? res : -res; }, /////////////////////////////////////////////////////////////// // // View 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 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(!this.isLeftToRight()){ x = r.w - x; } 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; } var col = Math.floor(x / (r.w / this.renderData.columnCount)); var row = Math.floor(y / (r.h / this.renderData.maxDayCount)); var date = null; if(col < this.renderData.dates.length && row < this.renderData.dates[col].length){ date = this.newDate(this.renderData.dates[col][row]); } return date; }, /////////////////////////////////////////////////////////////// // // Events // /////////////////////////////////////////////////////////////// _onGridMouseUp: function(e){ // tags: // private this.inherited(arguments); if (this._gridMouseDown) { this._gridMouseDown = false; this._onGridClick({ date: this.getTime(e), triggerEvent: e }); } }, _onGridTouchStart: function(e){ // tags: // private this.inherited(arguments); var g = this._gridProps; g.moved= false; g.start= e.touches[0].screenY; g.scrollTop= this.scrollContainer.scrollTop; }, _onGridTouchMove: function(e){ // tags: // private this.inherited(arguments); if (e.touches.length > 1 && !this._isEditing){ event.stop(e); return; } if(this._gridProps && !this._isEditing){ var touch = {x: e.touches[0].screenX, y: e.touches[0].screenY}; var p = this._edProps; if (!p || p && (Math.abs(touch.x - p.start.x) > 25 || Math.abs(touch.y - p.start.y) > 25)) { this._gridProps.moved = true; var d = e.touches[0].screenY - this._gridProps.start; var value = this._gridProps.scrollTop - d; var max = this.itemContainer.offsetHeight - this.scrollContainer.offsetHeight; if (value < 0){ this._gridProps.start = e.touches[0].screenY; this._setScrollImpl(0); this._gridProps.scrollTop = 0; }else if(value > max){ this._gridProps.start = e.touches[0].screenY; this._setScrollImpl(max); this._gridProps.scrollTop = max; }else{ this._setScrollImpl(value); } } } }, _onGridTouchEnd: function(e){ // tags: // private //event.stop(e); this.inherited(arguments); var g = this._gridProps; if(g){ if(!this._isEditing){ if(!g.moved){ // 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; } }, _onColumnHeaderClick: function(e){ // tags: // private this._dispatchCalendarEvt(e, "onColumnHeaderClick"); }, onColumnHeaderClick: function(e){ // summary: // Event dispatched when a column header cell is dispatched. // e: __ColumnClickEventArgs // tags: // callback }, /////////////////////////////////////////////////////////////// // // View limits // /////////////////////////////////////////////////////////////// _onScrollTimer_tick: function(){ // tags: // private this._setScrollImpl(this.scrollContainer.scrollTop + this._scrollProps.scrollStep); }, //////////////////////////////////////////// // // Editing // /////////////////////////////////////////// snapUnit: "day", snapSteps: 1, minDurationUnit: "day", minDurationSteps: 1, liveLayout: false, stayInView: true, allowStartEndSwap: true, allowResizeLessThan24H: false }); });