8 * @class Roo.grid.Calendar
9 * @extends Roo.util.Grid
10 * This class extends the Grid to provide a calendar widget
11 * <br><br>Usage:<pre><code>
12 var grid = new Roo.grid.Calendar("my-container-id", {
15 selModel: mySelectionModel,
16 autoSizeColumns: true,
17 monitorWindowResize: false,
19 eventstore : real data store..
25 * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
26 * The container MUST have some type of size defined for the grid to fill. The container will be
27 * automatically set to position relative if it isn't already.
28 * @param {Object} config A config object that sets properties on this grid.
30 Roo.grid.Calendar = function(container, config){
31 // initialize the container
32 this.container = Roo.get(container);
33 this.container.update("");
34 this.container.setStyle("overflow", "hidden");
35 this.container.addClass('x-grid-container');
37 this.id = this.container.id;
39 Roo.apply(this, config);
40 // check and correct shorthanded configs
44 for (var r = 0;r < 6;r++) {
47 for (var c =0;c < 7;c++) {
51 if (this.eventStore) {
52 this.eventStore= Roo.factory(this.eventStore, Roo.data);
53 this.eventStore.on('load',this.onLoad, this);
57 this.dataSource = new Roo.data.Store({
58 proxy: new Roo.data.MemoryProxy(rows),
59 reader: new Roo.data.ArrayReader({}, [
60 'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
63 this.dataSource.load();
64 this.ds = this.dataSource;
65 this.ds.xmodule = this.xmodule || false;
67 this.colModel = new Roo.grid.ColumnModel( [
71 dataIndex : 'weekday0',
73 //renderer : function(v,x,r) { return _this.grid.cellrenderer(v,x,r); }
78 dataIndex : 'weekday1',
80 //renderer : function(v,x,r) { return _this.grid.cellrenderer(v,x,r); }
85 dataIndex : 'weekday2',
87 //renderer : function(v,x,r) { return _this.grid.cellrenderer(v,x,r); }
92 dataIndex : 'weekday3',
94 //renderer : function(v,x,r) { return _this.grid.cellrenderer(v,x,r); }
99 dataIndex : 'weekday4',
101 //renderer : function(v,x,r) { return _this.grid.cellrenderer(v,x,r); }
104 xtype: 'ColumnModel',
106 dataIndex : 'weekday5',
108 //renderer : function(v,x,r) { return _this.grid.cellrenderer(v,x,r); }
111 xtype: 'ColumnModel',
113 dataIndex : 'weekday6',
115 //renderer : function(v,x,r) { return _this.grid.cellrenderer(v,x,r); }
118 this.cm = this.colModel;
119 this.cm.xmodule = this.xmodule || false;
123 //this.selModel = new Roo.grid.CellSelectionModel();
124 //this.sm = this.selModel;
125 //this.selModel.init(this);
129 this.container.setWidth(this.width);
133 this.container.setHeight(this.height);
140 * The raw click event for the entire grid.
141 * @param {Roo.EventObject} e
146 * The raw dblclick event for the entire grid.
147 * @param {Roo.EventObject} e
152 * The raw contextmenu event for the entire grid.
153 * @param {Roo.EventObject} e
155 "contextmenu" : true,
158 * The raw mousedown event for the entire grid.
159 * @param {Roo.EventObject} e
164 * The raw mouseup event for the entire grid.
165 * @param {Roo.EventObject} e
170 * The raw mouseover event for the entire grid.
171 * @param {Roo.EventObject} e
176 * The raw mouseout event for the entire grid.
177 * @param {Roo.EventObject} e
182 * The raw keypress event for the entire grid.
183 * @param {Roo.EventObject} e
188 * The raw keydown event for the entire grid.
189 * @param {Roo.EventObject} e
197 * Fires when a cell is clicked
199 * @param {Number} rowIndex
200 * @param {Number} columnIndex
201 * @param {Roo.EventObject} e
205 * @event celldblclick
206 * Fires when a cell is double clicked
208 * @param {Number} rowIndex
209 * @param {Number} columnIndex
210 * @param {Roo.EventObject} e
212 "celldblclick" : true,
215 * Fires when a row is clicked
217 * @param {Number} rowIndex
218 * @param {Roo.EventObject} e
223 * Fires when a row is double clicked
225 * @param {Number} rowIndex
226 * @param {Roo.EventObject} e
228 "rowdblclick" : true,
231 * Fires when a header is clicked
233 * @param {Number} columnIndex
234 * @param {Roo.EventObject} e
236 "headerclick" : true,
238 * @event headerdblclick
239 * Fires when a header cell is double clicked
241 * @param {Number} columnIndex
242 * @param {Roo.EventObject} e
244 "headerdblclick" : true,
246 * @event rowcontextmenu
247 * Fires when a row is right clicked
249 * @param {Number} rowIndex
250 * @param {Roo.EventObject} e
252 "rowcontextmenu" : true,
254 * @event cellcontextmenu
255 * Fires when a cell is right clicked
257 * @param {Number} rowIndex
258 * @param {Number} cellIndex
259 * @param {Roo.EventObject} e
261 "cellcontextmenu" : true,
263 * @event headercontextmenu
264 * Fires when a header is right clicked
266 * @param {Number} columnIndex
267 * @param {Roo.EventObject} e
269 "headercontextmenu" : true,
272 * Fires when the body element is scrolled
273 * @param {Number} scrollLeft
274 * @param {Number} scrollTop
278 * @event columnresize
279 * Fires when the user resizes a column
280 * @param {Number} columnIndex
281 * @param {Number} newSize
283 "columnresize" : true,
286 * Fires when the user moves a column
287 * @param {Number} oldIndex
288 * @param {Number} newIndex
293 * Fires when row(s) start being dragged
295 * @param {Roo.GridDD} dd The drag drop object
296 * @param {event} e The raw browser event
301 * Fires when a drag operation is complete
303 * @param {Roo.GridDD} dd The drag drop object
304 * @param {event} e The raw browser event
309 * Fires when dragged row(s) are dropped on a valid DD target
311 * @param {Roo.GridDD} dd The drag drop object
312 * @param {String} targetId The target drag drop object
313 * @param {event} e The raw browser event
318 * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
320 * @param {Roo.GridDD} dd The drag drop object
321 * @param {String} targetId The target drag drop object
322 * @param {event} e The raw browser event
327 * Fires when the dragged row(s) first cross another DD target while being dragged
329 * @param {Roo.GridDD} dd The drag drop object
330 * @param {String} targetId The target drag drop object
331 * @param {event} e The raw browser event
336 * Fires when the dragged row(s) leave another DD target while being dragged
338 * @param {Roo.GridDD} dd The drag drop object
339 * @param {String} targetId The target drag drop object
340 * @param {event} e The raw browser event
345 * Fires when a row is rendered, so you can change add a style to it.
346 * @param {GridView} gridview The grid view
347 * @param {Object} rowcfg contains record rowIndex and rowClass - set rowClass to add a style.
353 * Fires when the grid is rendered
359 * Fires when a date is selected
360 * @param {DatePicker} this
361 * @param {Date} date The selected date
366 * Fires when the displayed month changes
367 * @param {DatePicker} this
368 * @param {Date} date The selected month
373 * Fires when mouse over an event
374 * @param {Calendar} this
375 * @param {event} Event
380 * Fires when the mouse leaves an
381 * @param {Calendar} this
387 * Fires when the mouse click an
388 * @param {Calendar} this
394 Roo.grid.Grid.superclass.constructor.call(this);
395 this.on('render', function() {
396 (function() { this.setDate(new Date()); }).defer(100,this); //default today..
401 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
403 * @cfg {Store} eventStore The store that loads events.
411 monitorWindowResize : false,
414 resizeColumns : function() {
415 var col = (this.view.el.getWidth() / 7) - 3;
416 // loop through cols, and setWidth
417 for(var i =0 ; i < 7 ; i++){
418 this.cm.setColumnWidth(i, col);
421 setDate :function(date) {
422 this.resizeColumns();
423 var vd = this.activeDate;
424 this.activeDate = date;
425 // if(vd && this.el){
426 // var t = date.getTime();
427 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
428 // Roo.log('using add remove');
430 // this.fireEvent('monthchange', this, date);
432 // this.cells.removeClass("fc-state-highlight");
433 // this.cells.each(function(c){
434 // if(c.dateValue == t){
435 // c.addClass("fc-state-highlight");
436 // setTimeout(function(){
437 // try{c.dom.firstChild.focus();}catch(e){}
447 var days = date.getDaysInMonth();
449 var firstOfMonth = date.getFirstDateOfMonth();
450 var startingPos = firstOfMonth.getDay()-this.startDay;
452 if(startingPos < this.startDay){
456 var pm = date.add(Date.MONTH, -1);
457 var prevStart = pm.getDaysInMonth()-startingPos;
461 this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
463 this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
464 //this.cells.addClassOnOver('fc-state-hover');
466 var cells = this.cells.elements;
467 var textEls = this.textNodes;
469 //Roo.each(cells, function(cell){
470 // cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
475 // convert everything to numbers so it's fast
477 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
480 //Roo.log(prevStart);
482 var today = new Date().clearTime().getTime();
483 var sel = date.clearTime().getTime();
484 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
485 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
486 var ddMatch = this.disabledDatesRE;
487 var ddText = this.disabledDatesText;
488 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
489 var ddaysText = this.disabledDaysText;
490 var format = this.format;
492 var setCellClass = function(cal, cell){
494 //Roo.log('set Cell Class');
502 // cell.className += " fc-today";
503 // cell.className += " fc-state-highlight";
504 cell.title = cal.todayText;
507 // disable highlight in other month..
508 //cell.className += " fc-state-highlight";
513 //cell.className = " fc-state-disabled";
514 cell.title = cal.minText;
518 //cell.className = " fc-state-disabled";
519 cell.title = cal.maxText;
523 if(ddays.indexOf(d.getDay()) != -1){
524 // cell.title = ddaysText;
525 // cell.className = " fc-state-disabled";
528 if(ddMatch && format){
529 var fvalue = d.dateFormat(format);
530 if(ddMatch.test(fvalue)){
531 cell.title = ddText.replace("%0", fvalue);
532 // cell.className = " fc-state-disabled";
536 if (!cell.initialClassName) {
537 //cell.initialClassName = cell.dom.className;
540 cell.dom.className = cell.initialClassName + ' ' + cell.className;
545 for(; i < startingPos; i++) {
546 cells[i].dayName = (++prevStart);
548 d.setDate(d.getDate()+1);
550 //cells[i].className = "fc-past fc-other-month";
551 setCellClass(this, cells[i]);
556 for(; i < days; i++){
557 intDay = i - startingPos + 1;
558 cells[i].dayName = (intDay);
559 d.setDate(d.getDate()+1);
561 cells[i].className = ''; // "x-date-active";
562 setCellClass(this, cells[i]);
567 //textEls[i].innerHTML = (++extraDays);
569 d.setDate(d.getDate()+1);
570 cells[i].dayName = (++extraDays);
571 cells[i].className = "fc-future fc-other-month";
572 setCellClass(this, cells[i]);
575 //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
577 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
581 for (var r = 0;r < 6;r++) {
582 for (var c =0;c < 7;c++) {
583 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
589 //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
590 //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
592 ////if(totalRows != 6){
593 //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
594 // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
597 this.fireEvent('monthchange', this, date);
602 * Returns the grid's SelectionModel.
603 * @return {SelectionModel}
605 getSelectionModel : function(){
607 this.selModel = new Roo.grid.CellSelectionModel();
609 return this.selModel;
613 this.eventStore.load()
619 findCell : function(dt) {
620 dt = dt.clearTime().getTime();
622 this.cells.each(function(c){
623 //Roo.log("check " +c.dateValue + '?=' + dt);
624 if(c.dateValue == dt){
634 findCells : function(ev) {
635 var s = ev.start_dt.clone().clearTime().getTime();
637 var e= ev.end_dt.clone().clearTime().getTime();
640 this.cells.each(function(c){
641 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
655 findBestRow: function(cells)
659 for (var i =0 ; i < cells.length;i++) {
660 ret = Math.max(cells[i].rows || 0,ret);
667 addItem : function(ev)
669 // look for vertical location slot in
670 var cells = this.findCells(ev);
672 ev.row = this.findBestRow(cells);
674 // work out the location.
678 for(var i =0; i < cells.length; i++) {
686 if (crow.start.getY() == cells[i].getY()) {
704 for (var i = 0; i < cells.length;i++) {
705 cells[i].rows = Math.max(cells[i].rows || 0 , ev.row + 1 );
709 this.calevents.push(ev);
712 clearEvents: function() {
718 Roo.each(this.cells.elements, function(c){
722 Roo.each(this.calevents, function(e) {
723 Roo.each(e.els, function(el) {
724 el.un('mouseenter' ,this.onEventEnter, this);
725 el.un('mouseleave' ,this.onEventLeave, this);
732 renderEvents: function()
734 // first make sure there is enough space..
736 this.cells.each(function(c) {
738 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.rows * 20));
741 for (var e = 0; e < this.calevents.length; e++) {
742 var ev = this.calevents[e];
743 var cells = ev.cells;
746 for(var i =0; i < rows.length; i++) {
749 // how many rows should it span..
752 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
753 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
758 cls: 'fc-event-inner',
762 cls: 'fc-event-time',
763 html : cells.length > 1 ? '' : ev.start_dt.format('h:ia')
767 cls: 'fc-event-title',
768 html : String.format('{0}', ev.title)
775 cls: 'ui-resizable-handle ui-resizable-e',
776 html : '  '
782 cfg.cls += ' fc-event-start';
784 if ((i+1) == rows.length) {
785 cfg.cls += ' fc-event-end';
788 var ctr = this.el.select('.fc-event-container',true).first();
789 var cg = ctr.createChild(cfg);
791 cg.on('mouseenter' ,this.onEventEnter, this, ev);
792 cg.on('mouseleave' ,this.onEventLeave, this, ev);
793 cg.on('click', this.onEventClick, this, ev);
797 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
798 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
800 cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);
801 cg.setWidth(ebox.right - sbox.x -2);
809 onEventEnter: function (e, el,event,d) {
810 this.fireEvent('evententer', this, el, event);
813 onEventLeave: function (e, el,event,d) {
814 this.fireEvent('eventleave', this, el, event);
817 onEventClick: function (e, el,event,d) {
818 this.fireEvent('eventclick', this, el, event);
821 onMonthChange: function () {
825 onLoad: function () {
828 //Roo.log('calendar onload');
832 if(this.store.getCount() > 0){
833 this.store.data.each(function(d){
837 var add = Roo.apply({}, d.data);
838 if (typeof(add.end_dt) == 'undefined') {
839 Roo.log("Missing End time in calendar data: ");
843 if (typeof(add.start_dt) == 'undefined') {
844 Roo.log("Missing Start time in calendar data: ");
848 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
849 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
850 add.id = add.id || d.id;
851 add.title = add.title || '??';
854 // other than the 'required' coluns, we should just pass the data from the store.
859 start: new Date(d.data.start_dt),
860 end : new Date(d.data.end_dt),
861 time : d.data.start_time,
862 title : d.data.title,
863 description : d.data.description,
883 if (!this.view.el.hasClass('course-timesheet')) {
884 this.view.el.addClass('course-timesheet');
891 Roo.log(_this.grid.view.el.getWidth());
894 this.tsStyle = Roo.util.CSS.createStyleSheet({
895 '.course-timesheet .x-grid-row' : {
901 '.course-edit-link' : {
903 'text-overflow' : 'ellipsis',
904 'overflow' : 'hidden',
905 'white-space' : 'nowrap',
911 '.de-act-sup-link' : {
913 'text-decoration' : 'line-through'
917 'text-decoration' : 'line-through'
919 '.course-timesheet .course-highlight' : {
920 'border-top-style': 'dashed !important',
921 'border-bottom-bottom': 'dashed !important'
923 '.course-timesheet .course-item' : {
924 'font-family' : 'tahoma, arial, helvetica',
925 'font-size' : '11px',
926 'overflow' : 'hidden',
927 'padding-left' : '10px',
928 'padding-right' : '10px',
929 'padding-top' : '10px'
937 monitorWindowResize : false,
938 cellrenderer : function(v,x,r)
943 xtype: 'CellSelectionModel',
950 beforeload : function (_self, options)
952 options.params = options.params || {};
953 options.params._month = _this.monthField.getValue();
954 options.params.limit = 9999;
955 options.params['sort'] = 'when_dt';
956 options.params['dir'] = 'ASC';
957 this.proxy.loadResponse = this.loadResponse;
961 load : function (_self, records, options)
963 _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
964 // if you click on the translation.. you can edit it...
965 var el = Roo.get(this);
966 var id = el.dom.getAttribute('data-id');
967 var d = el.dom.getAttribute('data-date');
968 var t = el.dom.getAttribute('data-time');
969 //var id = this.child('span').dom.textContent;
972 Pman.Dialog.CourseCalendar.show({
976 productitem_active : id ? 1 : 0
978 _this.grid.ds.load({});
983 _this.panel.fireEvent('resize', [ '', '' ]);
986 loadResponse : function(o, success, response){
987 // this is overridden on before load..
989 Roo.log("our code?");
992 delete this.activeRequest;
994 this.fireEvent("loadexception", this, o, response);
995 o.request.callback.call(o.request.scope, null, o.request.arg, false);
1000 result = o.reader.read(response);
1002 Roo.log("load exception?");
1003 this.fireEvent("loadexception", this, o, response, e);
1004 o.request.callback.call(o.request.scope, null, o.request.arg, false);
1007 Roo.log("ready...");
1008 // loop through result.records;
1009 // and set this.tdate[date] = [] << array of records..
1011 Roo.each(result.records, function(r){
1013 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
1014 _this.tdata[r.data.when_dt.format('j')] = [];
1016 _this.tdata[r.data.when_dt.format('j')].push(r.data);
1019 //Roo.log(_this.tdata);
1021 result.records = [];
1022 result.totalRecords = 6;
1024 // let's generate some duumy records for the rows.
1025 //var st = _this.dateField.getValue();
1027 // work out monday..
1028 //st = st.add(Date.DAY, -1 * st.format('w'));
1030 var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
1032 var firstOfMonth = date.getFirstDayOfMonth();
1033 var days = date.getDaysInMonth();
1035 var firstAdded = false;
1036 for (var i = 0; i < result.totalRecords ; i++) {
1037 //var d= st.add(Date.DAY, i);
1040 for(var w = 0 ; w < 7 ; w++){
1041 if(!firstAdded && firstOfMonth != w){
1048 var dd = (d > 0 && d < 10) ? "0"+d : d;
1049 row['weekday'+w] = String.format(
1050 '<span style="font-size: 16px;"><b>{0}</b></span>'+
1051 '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
1053 date.format('Y-m-')+dd
1056 if(typeof(_this.tdata[d]) != 'undefined'){
1057 Roo.each(_this.tdata[d], function(r){
1061 var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
1062 if(r.parent_id*1>0){
1063 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
1066 if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
1067 deactive = 'de-act-link';
1070 row['weekday'+w] += String.format(
1071 '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
1073 r.product_id_name, //1
1074 r.when_dt.format('h:ia'), //2
1084 // only do this if something added..
1086 result.records.push(_this.grid.dataSource.reader.newRow(row));
1090 // push it twice. (second one with an hour..
1094 this.fireEvent("load", this, o, o.request.arg);
1095 o.request.callback.call(o.request.scope, result, o.request.arg, true);
1097 sortInfo : {field: 'when_dt', direction : 'ASC' },
1102 url : baseURL + '/Roo/Shop_course.php'
1105 xtype: 'JsonReader',
1122 'name': 'parent_id',
1126 'name': 'product_id',
1130 'name': 'productitem_id',
1148 click : function (_self, e)
1150 var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
1151 sd.setMonth(sd.getMonth()-1);
1152 _this.monthField.setValue(sd.format('Y-m-d'));
1153 _this.grid.ds.load({});
1163 xtype: 'MonthField',
1166 render : function (_self)
1168 _this.monthField = _self;
1169 // _this.monthField.set today
1171 select : function (combo, date)
1173 _this.grid.ds.load({});
1176 value : (function() { return new Date(); })()
1185 text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
1195 click : function (_self, e)
1197 var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
1198 sd.setMonth(sd.getMonth()+1);
1199 _this.monthField.setValue(sd.format('Y-m-d'));
1200 _this.grid.ds.load({});