4 * base class for bootstrap elements.
8 Roo.bootstrap = Roo.bootstrap || {};
10 * @class Roo.bootstrap.Component
11 * @extends Roo.Component
12 * Bootstrap Component base class
13 * @cfg {String} cls css class
14 * @cfg {String} style any extra css
15 * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16 * @cfg {Boolean} can_build_overlaid True if element can be rebuild from a HTML page
17 * @cfg {string} dataId cutomer id
18 * @cfg {string} name Specifies name attribute
21 * Do not use directly - it does not do anything..
22 * @param {Object} config The config object
27 Roo.bootstrap.Component = function(config){
28 Roo.bootstrap.Component.superclass.constructor.call(this, config);
31 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
34 allowDomMove : false, // to stop relocations in parent onRender...
42 initEvents : function() { },
48 can_build_overlaid : true,
55 // returns the parent component..
56 return Roo.ComponentMgr.get(this.parentId)
62 onRender : function(ct, position)
64 // Roo.log("Call onRender: " + this.xtype);
66 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
69 if (this.el.attr('xtype')) {
70 this.el.attr('xtypex', this.el.attr('xtype'));
71 this.el.dom.removeAttribute('xtype');
81 var cfg = Roo.apply({}, this.getAutoCreate());
84 // fill in the extra attributes
85 if (this.xattr && typeof(this.xattr) =='object') {
86 for (var i in this.xattr) {
87 cfg[i] = this.xattr[i];
92 cfg.dataId = this.dataId;
96 cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
99 if (this.style) { // fixme needs to support more complex style data.
100 cfg.style = this.style;
104 cfg.name = this.name;
107 this.el = ct.createChild(cfg, position);
109 if(this.tabIndex !== undefined){
110 this.el.dom.setAttribute('tabIndex', this.tabIndex);
117 getChildContainer : function()
123 addxtype : function(tree,cntr)
127 cn = Roo.factory(tree);
129 cn.parentType = this.xtype; //??
130 cn.parentId = this.id;
132 cntr = typeof(cntr == 'undefined' ) ? 'getChildContainer' : cntr;
134 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
136 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
138 var build_from_html = Roo.XComponent.build_from_html;
140 var is_body = (tree.xtype == 'Body') ;
142 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
144 var self_cntr_el = Roo.get(this[cntr]());
146 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
147 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
148 return this.addxtypeChild(tree,cntr);
151 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
154 return this.addxtypeChild(Roo.apply({}, tree),cntr);
157 Roo.log('skipping render');
165 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
171 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
175 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
180 addxtypeChild : function (tree, cntr)
183 cntr = typeof(cntr == 'undefined' ) ? 'getChildContainer' : cntr;
186 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
187 (typeof(tree['flexy:foreach']) != 'undefined');
192 // render the element if it's not BODY.
193 if (tree.xtype != 'Body') {
194 var test = Roo.factory(tree);
195 cn = Roo.factory(tree);
197 cn.parentType = this.xtype; //??
198 cn.parentId = this.id;
200 var build_from_html = Roo.XComponent.build_from_html;
203 // does the container contain child eleemnts with 'xtype' attributes.
204 // that match this xtype..
205 // note - when we render we create these as well..
206 // so we should check to see if body has xtype set.
207 if (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
209 var self_cntr_el = Roo.get(this[cntr]());
210 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
212 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
213 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
219 //echild.dom.removeAttribute('xtype');
221 Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
228 // if object has flexy:if - then it may or may not be rendered.
229 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
230 // skip a flexy if element.
231 Roo.log('skipping render');
234 // actually if flexy:foreach is found, we really want to create
235 // multiple copies here...
237 cn.render(this[cntr]());
239 // then add the element..
246 if (typeof (tree.menu) != 'undefined') {
247 tree.menu.parentType = cn.xtype;
248 tree.menu.triggerEl = cn.el;
249 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
253 if (!tree.items || !tree.items.length) {
257 var items = tree.items;
260 //Roo.log(items.length);
262 for(var i =0;i < items.length;i++) {
263 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
279 * based on jquery fullcalendar
283 Roo.bootstrap = Roo.bootstrap || {};
285 * @class Roo.bootstrap.Calendar
286 * @extends Roo.bootstrap.Component
287 * Bootstrap Calendar class
290 * Create a new Container
291 * @param {Object} config The config object
296 Roo.bootstrap.Calendar = function(config){
297 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
301 * Fires when a date is selected
302 * @param {DatePicker} this
303 * @param {Date} date The selected date
308 * Fires when the displayed month changes
309 * @param {DatePicker} this
310 * @param {Date} date The selected month
315 * Fires when mouse over an event
316 * @param {Calendar} this
317 * @param {event} Event
322 * Fires when the mouse leaves an
323 * @param {Calendar} this
329 * Fires when the mouse click an
330 * @param {Calendar} this
339 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
342 * @cfg {Number} startDay
343 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
347 getAutoCreate : function(){
350 fc_button = function(name, corner, style, content ) {
351 return Roo.apply({},{
353 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
355 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
358 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
366 style : 'width:100%',
373 cls : 'fc-header-left',
375 fc_button('prev', 'left', 'arrow', '‹' ),
376 fc_button('next', 'right', 'arrow', '›' ),
377 { tag: 'span', cls: 'fc-header-space' },
378 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
386 cls : 'fc-header-center',
390 cls: 'fc-header-title',
393 html : 'month / year'
401 cls : 'fc-header-right',
403 /* fc_button('month', 'left', '', 'month' ),
404 fc_button('week', '', '', 'week' ),
405 fc_button('day', 'right', '', 'day' )
417 var cal_heads = function() {
419 // fixme - handle this.
421 for (var i =0; i < Date.dayNames.length; i++) {
422 var d = Date.dayNames[i];
425 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
426 html : d.substring(0,3)
430 ret[0].cls += ' fc-first';
431 ret[6].cls += ' fc-last';
434 var cal_cell = function(n) {
437 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
442 cls: 'fc-day-number',
446 cls: 'fc-day-content',
450 style: 'position: relative;' // height: 17px;
462 var cal_rows = function() {
465 for (var r = 0; r < 6; r++) {
472 for (var i =0; i < Date.dayNames.length; i++) {
473 var d = Date.dayNames[i];
474 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
477 row.cn[0].cls+=' fc-first';
478 row.cn[0].cn[0].style = 'min-height:90px';
479 row.cn[6].cls+=' fc-last';
483 ret[0].cls += ' fc-first';
484 ret[4].cls += ' fc-prev-last';
485 ret[5].cls += ' fc-last';
492 cls: 'fc-border-separate',
493 style : 'width:100%',
501 cls : 'fc-first fc-last',
520 style : "position: relative;",
523 cls : 'fc-view fc-view-month fc-grid',
524 style : 'position: relative',
528 cls : 'fc-event-container',
529 style : 'position:absolute;z-index:8;top:0;left:0;'
547 initEvents : function()
550 throw "can not find store for calendar";
553 this.store = Roo.factory(this.store, Roo.data);
554 this.store.on('load', this.onLoad, this);
557 this.cells = this.el.select('.fc-day',true);
559 this.textNodes = this.el.query('.fc-day-number');
560 this.cells.addClassOnOver('fc-state-hover');
562 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
563 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
564 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
565 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
567 this.on('monthchange', this.onMonthChange, this);
569 this.update(new Date().clearTime());
572 resize : function() {
573 var sz = this.el.getSize();
575 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
576 this.el.select('.fc-day-content div',true).setHeight(34);
581 showPrevMonth : function(e){
582 this.update(this.activeDate.add("mo", -1));
584 showToday : function(e){
585 this.update(new Date().clearTime());
588 showNextMonth : function(e){
589 this.update(this.activeDate.add("mo", 1));
593 showPrevYear : function(){
594 this.update(this.activeDate.add("y", -1));
598 showNextYear : function(){
599 this.update(this.activeDate.add("y", 1));
604 update : function(date)
606 var vd = this.activeDate;
607 this.activeDate = date;
608 // if(vd && this.el){
609 // var t = date.getTime();
610 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
611 // Roo.log('using add remove');
613 // this.fireEvent('monthchange', this, date);
615 // this.cells.removeClass("fc-state-highlight");
616 // this.cells.each(function(c){
617 // if(c.dateValue == t){
618 // c.addClass("fc-state-highlight");
619 // setTimeout(function(){
620 // try{c.dom.firstChild.focus();}catch(e){}
630 var days = date.getDaysInMonth();
632 var firstOfMonth = date.getFirstDateOfMonth();
633 var startingPos = firstOfMonth.getDay()-this.startDay;
635 if(startingPos < this.startDay){
639 var pm = date.add("mo", -1);
640 var prevStart = pm.getDaysInMonth()-startingPos;
642 this.cells = this.el.select('.fc-day',true);
643 this.textNodes = this.el.query('.fc-day-number');
644 this.cells.addClassOnOver('fc-state-hover');
646 var cells = this.cells.elements;
647 var textEls = this.textNodes;
649 Roo.each(cells, function(cell){
650 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
655 // convert everything to numbers so it's fast
657 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
658 var today = new Date().clearTime().getTime();
659 var sel = date.clearTime().getTime();
660 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
661 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
662 var ddMatch = this.disabledDatesRE;
663 var ddText = this.disabledDatesText;
664 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
665 var ddaysText = this.disabledDaysText;
666 var format = this.format;
668 var setCellClass = function(cal, cell){
674 cell.className += " fc-today";
675 cell.className += " fc-state-highlight";
676 cell.title = cal.todayText;
679 // disable highlight in other month..
680 //cell.className += " fc-state-highlight";
685 cell.className = " fc-state-disabled";
686 cell.title = cal.minText;
690 cell.className = " fc-state-disabled";
691 cell.title = cal.maxText;
695 if(ddays.indexOf(d.getDay()) != -1){
696 cell.title = ddaysText;
697 cell.className = " fc-state-disabled";
700 if(ddMatch && format){
701 var fvalue = d.dateFormat(format);
702 if(ddMatch.test(fvalue)){
703 cell.title = ddText.replace("%0", fvalue);
704 cell.className = " fc-state-disabled";
708 if (!cell.initialClassName) {
709 cell.initialClassName = cell.dom.className;
712 cell.dom.className = cell.initialClassName + ' ' + cell.className;
717 for(; i < startingPos; i++) {
718 textEls[i].innerHTML = (++prevStart);
719 d.setDate(d.getDate()+1);
721 cells[i].className = "fc-past fc-other-month";
722 setCellClass(this, cells[i]);
727 for(; i < days; i++){
728 intDay = i - startingPos + 1;
729 textEls[i].innerHTML = (intDay);
730 d.setDate(d.getDate()+1);
732 cells[i].className = ''; // "x-date-active";
733 setCellClass(this, cells[i]);
738 textEls[i].innerHTML = (++extraDays);
739 d.setDate(d.getDate()+1);
741 cells[i].className = "fc-future fc-other-month";
742 setCellClass(this, cells[i]);
745 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
747 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
749 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
750 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
753 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
754 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
757 this.fireEvent('monthchange', this, date);
761 if(!this.internalRender){
762 var main = this.el.dom.firstChild;
763 var w = main.offsetWidth;
764 this.el.setWidth(w + this.el.getBorderWidth("lr"));
765 Roo.fly(main).setWidth(w);
766 this.internalRender = true;
767 // opera does not respect the auto grow header center column
768 // then, after it gets a width opera refuses to recalculate
769 // without a second pass
770 if(Roo.isOpera && !this.secondPass){
771 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
772 this.secondPass = true;
773 this.update.defer(10, this, [date]);
780 findCell : function(dt) {
781 dt = dt.clearTime().getTime();
783 this.cells.each(function(c){
784 //Roo.log("check " +c.dateValue + '?=' + dt);
785 if(c.dateValue == dt){
795 findCells : function(ev) {
796 var s = ev.start.clone().clearTime().getTime();
797 var e= ev.end.clone().clearTime().getTime();
799 this.cells.each(function(c){
800 //Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
814 findBestRow: function(cells)
818 for (var i =0 ; i < cells.length;i++) {
819 ret = Math.max(cells[i].rows || 0,ret);
826 addItem : function(ev)
828 // look for vertical location slot in
829 var cells = this.findCells(ev);
831 ev.row = this.findBestRow(cells);
833 // work out the location.
837 for(var i =0; i < cells.length; i++) {
845 if (crow.start.getY() == cells[i].getY()) {
863 for (var i = 0; i < cells.length;i++) {
864 cells[i].rows = Math.max(cells[i].rows || 0 , ev.row + 1 );
868 this.calevents.push(ev);
871 clearEvents: function() {
877 Roo.each(this.cells.elements, function(c){
881 Roo.each(this.calevents, function(e) {
882 Roo.each(e.els, function(el) {
883 el.un('mouseenter' ,this.onEventEnter, this);
884 el.un('mouseleave' ,this.onEventLeave, this);
891 renderEvents: function()
893 // first make sure there is enough space..
895 this.cells.each(function(c) {
897 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.rows * 20));
900 for (var e = 0; e < this.calevents.length; e++) {
901 var ev = this.calevents[e];
902 var cells = ev.cells;
905 for(var i =0; i < rows.length; i++) {
908 // how many rows should it span..
911 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
912 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
917 cls: 'fc-event-inner',
921 cls: 'fc-event-time',
922 html : cells.length > 1 ? '' : ev.time
926 cls: 'fc-event-title',
927 html : String.format('{0}', ev.title)
934 cls: 'ui-resizable-handle ui-resizable-e',
935 html : '  '
941 cfg.cls += ' fc-event-start';
943 if ((i+1) == rows.length) {
944 cfg.cls += ' fc-event-end';
947 var ctr = this.el.select('.fc-event-container',true).first();
948 var cg = ctr.createChild(cfg);
950 cg.on('mouseenter' ,this.onEventEnter, this, ev);
951 cg.on('mouseleave' ,this.onEventLeave, this, ev);
952 cg.on('click', this.onEventClick, this, ev);
956 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
957 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
959 cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);
960 cg.setWidth(ebox.right - sbox.x -2);
968 onEventEnter: function (e, el,event,d) {
969 this.fireEvent('evententer', this, el, event);
972 onEventLeave: function (e, el,event,d) {
973 this.fireEvent('eventleave', this, el, event);
976 onEventClick: function (e, el,event,d) {
977 this.fireEvent('eventclick', this, el, event);
980 onMonthChange: function () {
984 onLoad: function () {
987 Roo.log('calendar onload');
991 if(this.store.getCount() > 0){
992 this.store.data.each(function(d){
995 start: new Date(d.data.start_dt),
996 end : new Date(d.data.end_dt),
997 time : d.data.start_time,
998 title : d.data.title,
999 description : d.data.description,
1000 venue : d.data.venue
1005 this.renderEvents();
1013 * based on jquery fullcalendar
1019 * @class Roo.Calendar
1020 * @extends Roo.Component
1021 * Bootstrap Calendar class
1024 * Create a new Container
1025 * @param {Object} config The config object
1028 Roo.CalendarPanel = function(config){
1030 Roo.log("cal panel ctr");
1032 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
1033 {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
1035 //this.wrapper.dom.appendChild(grid.getGridEl().dom);
1037 Roo.CalendarPanel.superclass.constructor.call(this, this.wrapper, config);
1042 this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
1044 // xtype created footer. - not sure if will work as we normally have to render first..
1045 if (this.footer && !this.footer.el && this.footer.xtype) {
1047 //this.footer.container = this.grid.getView().getFooterPanel(true);
1048 //this.footer.dataSource = this.grid.dataSource;
1049 //this.footer = Roo.factory(this.footer, Roo);
1053 this.on('activate', function()
1055 Roo.log('activate');
1057 //console.log('render tree');
1064 * Fires when a date is selected
1065 * @param {DatePicker} this
1066 * @param {Date} date The selected date
1070 * @event monthchange
1071 * Fires when the displayed month changes
1072 * @param {DatePicker} this
1073 * @param {Date} date The selected month
1075 'monthchange': true,
1078 * Fires when mouse over an event
1079 * @param {Calendar} this
1080 * @param {event} Event
1085 * Fires when the mouse leaves an
1086 * @param {Calendar} this
1092 * Fires when the mouse click an
1093 * @param {Calendar} this
1101 //this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
1105 Roo.extend(Roo.CalendarPanel, Roo.ContentPanel, {
1110 setSize : function(width, height){
1111 if(!this.ignoreResize(width, height)){
1112 var grid = this.grid;
1113 var size = this.adjustForComponents(width, height);
1114 grid.getGridEl().setSize(size.width, size.height);
1119 beforeSlide : function(){
1120 this.grid.getView().scroller.clip();
1123 afterSlide : function(){
1124 this.grid.getView().scroller.unclip();
1127 destroy : function(){
1128 // this.grid.destroy();
1129 // delete this.grid;
1130 Roo.GridPanel.superclass.destroy.call(this);
1135 this.onRender(this.el, false)
1138 onRender : function(ct, position)
1140 if (this.rendered) {
1143 this.rendered = true;
1145 Roo.log("render calendar");
1147 //Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
1150 var cfg = Roo.apply({}, this.getAutoCreate());
1153 // fill in the extra attributes
1154 if (this.xattr && typeof(this.xattr) =='object') {
1155 for (var i in this.xattr) {
1156 cfg[i] = this.xattr[i];
1161 cfg.dataId = this.dataId;
1165 cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
1168 if (this.style) { // fixme needs to support more complex style data.
1169 cfg.style = this.style;
1173 cfg.name = this.name;
1176 this.el = ct.createChild(cfg, position);
1178 if(this.tabIndex !== undefined){
1179 this.el.dom.setAttribute('tabIndex', this.tabIndex);
1185 getAutoCreate : Roo.bootstrap.Calendar.prototype.getAutoCreate,
1187 initEvents : Roo.bootstrap.Calendar.prototype.initEvents
1219 Roo.CalendarPanel.prototype[p] = Roo.bootstrap.Calendar.prototype[p];