1. #1
    Ext User aungii's Avatar
    Join Date
    Jul 2007
    Location
    Singapore
    Posts
    4
    Vote Rating
    0
    aungii is on a distinguished road

      0  

    Smile Multi-Month Calendar

    Multi-Month Calendar


    Description
    -----------

    Multi-month calendar
    1. Configurable no. of month
    2. Define the event dates which will show as selected


    Code
    PHP Code:
    /*
     * Ext JS Library 2.0 RC 1
     * Copyright(c) 2006-2007, Ext JS, LLC.
     * licensing@extjs.com
     * 
     * http://extjs.com/license
     */

    /**
     * @class Ext.MultiMonthCalendar
     * @extends Ext.Component
     * Multi-month Calendar
     * @constructor
      * @param {Object} config The config object
     */
    Ext.ux.MultiMonthCalendar Ext.extend(Ext.Component, {
        
    /**
         * @cfg {Date} minDate
         * Minimum allowable date (JavaScript date object, defaults to null)
         */
        
    minDate null,
        
    /**
         * @cfg {Date} maxDate
         * Maximum allowable date (JavaScript date object, defaults to null)
         */
        
    maxDate null,
        
    /**
         * @cfg {String} minText
         * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
         */
        
    minText "This date is before the minimum date",
        
    /**
         * @cfg {String} maxText
         * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
         */
        
    maxText "This date is after the maximum date",
        
    /**
         * @cfg {String} format
         * The default date format string which can be overriden for localization support.  The format must be
         * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
         */
        
    format "m/d/y",
        
    /**
         * @cfg {Array} disabledDays
         * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
         */
        
    disabledDays null,
        
    /**
         * @cfg {String} disabledDaysText
         * The tooltip to display when the date falls on a disabled day (defaults to "")
         */
        
    disabledDaysText "",
        
    /**
         * @cfg {RegExp} disabledDatesRE
         * JavaScript regular expression used to disable a pattern of dates (defaults to null)
         */
        
    disabledDatesRE null,
        
    /**
         * @cfg {String} disabledDatesText
         * The tooltip text to display when the date falls on a disabled date (defaults to "")
         */
        
    disabledDatesText "",
        
    /**
         * @cfg {Boolean} constrainToViewport
         * True to constrain the date picker to the viewport (defaults to true)
         */
        
    constrainToViewport true,
        
    /**
         * @cfg {Array} monthNames
         * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
         */
        
    monthNames Date.monthNames,
        
    /**
         * @cfg {Array} dayNames
         * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
         */
        
    dayNames Date.dayNames,
        
    /**
         * @cfg {String} nextText
         * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
         */
        
    nextText'Next Month (Control+Right)',
        
    /**
         * @cfg {String} prevText
         * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
         */
        
    prevText'Previous Month (Control+Left)',    
        
    /**
         * @cfg {Number} startDay
         * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
         */
        
    startDay 0,
        
    /**
        * @cfg {Number} noOfMonth
        * No of Month to be displayed
        */
        
    noOfMonth 2,
        
    /**
        * @cfg {Array} eventDates
        * List of Dates which have an event (show as selected in UI)
        */    
        
    eventDates null,
        
        
    initComponent : function(){
            
    Ext.ux.MultiMonthCalendar.superclass.initComponent.call(this);
            
    this.value this.value ?
                     
    this.value.clearTime() : new Date().clearTime();
            
    this.initDisabledDays();
        },

        
    // private
        
    initDisabledDays : function(){
            if(!
    this.disabledDatesRE && this.disabledDates){
                var 
    dd this.disabledDates;
                var 
    re "(?:";
                for(var 
    0dd.lengthi++){
                    
    re += dd[i];
                    if(
    != dd.length-1re += "|";
                }
                
    this.disabledDatesRE = new RegExp(re ")");
            }
        },

        
    /**
         * Sets the value of the date field
         * @param {Date} value The date to set
         */

        
    setValue : function(value){
            var 
    old this.value;
            
    this.value value.clearTime(true);
            if(
    this.el){
                
    this.update(this.value);
            }
        },

        
    /**
         * Gets the current selected value of the date field
         * @return {Date} The selected date
         */
        
    getValue : function(){
            return 
    this.value;
        },


        
    // private
        
    focus : function(){
            if(
    this.el){
                
    this.update(this.activeDate);
            }
        },

        
    // private
        
    onRender : function(containerposition){
            var 
    = ["<table cellspacing='0'><tr>"];
            for(var 
    x=0x<this.noOfMonthx++) {
                
    m.push("<td><table cellspacing='0'><tr>");
                if(
    x==0) {
                    
    m.push("<td class='x-date-left'><a href='#' title='"this.prevText ,"'> </a></td>");
                } else {
                    
    m.push("<td class='x-date-left'>&nbsp;</td>");
                }
                
    m.push("<td class='x-date-middle' align='center'><span id='monthLabel" "'></span></td>");
                if(
    == this.noOfMonth-1) {
                    
    m.push("<td class='x-date-right'><a href='#' title='"this.nextText ,"'> </a></td>");
                } else {
                    
    m.push("<td class='x-date-right'>&nbsp;</td>");
                }
                
    m.push("</tr><tr><td colspan='3'><table class='x-date-inner' id='inner-date"+x+"' cellspacing='0'><thead><tr>");        
                var 
    dn this.dayNames;
                for(var 
    07i++){
                   var 
    this.startDay+i;
                   if(
    6){
                       
    d-7;
                   }
                    
    m.push("<th><span>"dn[d].substr(0,1), "</span></th>");
                }
                
    m[m.length] = "</tr></thead><tbody><tr>";
                for(var 
    042i++) {
                    if(
    == && != 0){
                        
    m[m.length] = "</tr><tr>";
                    }
                    
    m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
                }
                
    m[m.length] = '</tr></tbody></table></td></tr></table></td>';
                if(
    != this.noOfMonth-1) {
                    
    m[m.length] = "<td width='3'></td>";
                }
            }
            
    m[m.length] = "</tr></table>";            
            var 
    el document.createElement("div");
            
    el.className "x-date-picker";
            
    el.innerHTML m.join("");

            
    container.dom.insertBefore(elposition);

            
    this.el Ext.get(el);        
            
    this.eventEl Ext.get(el.firstChild);

            new 
    Ext.util.ClickRepeater(this.el.child("td.x-date-left a"), {
                
    handlerthis.showPrevMonth,
                
    scopethis,
                
    preventDefault:true,
                
    stopDefault:true
            
    });

            new 
    Ext.util.ClickRepeater(this.el.child("td.x-date-right a"), {
                
    handlerthis.showNextMonth,
                
    scopethis,
                
    preventDefault:true,
                
    stopDefault:true
            
    });

            var 
    kn = new Ext.KeyNav(this.eventEl, {
                
    "left" : function(e){
                    
    e.ctrlKey ?
                        
    this.showPrevMonth() :
                        
    this.update(this.activeDate.add("d", -1));
                },

                
    "right" : function(e){
                    
    e.ctrlKey ?
                        
    this.showNextMonth() :
                        
    this.update(this.activeDate.add("d"1));
                },

                
    "pageUp" : function(e){
                    
    this.showNextMonth();
                },

                
    "pageDown" : function(e){
                    
    this.showPrevMonth();
                },

                
    "enter" : function(e){
                    
    e.stopPropagation();
                    return 
    true;
                }, 
                
    scope this 
            
    });
            
            
    this.cellsArray = new Array();
            
    this.textNodesArray = new Array(); 
            for(var 
    x=0xthis.noOfMonthx++) {
                var 
    cells Ext.get('inner-date'+x).select("tbody td");
                var 
    textNodes Ext.get('inner-date'+x).query("tbody span");
                
    this.cellsArray[x] = cells;
                
    this.textNodesArray[x] = textNodes;
            }

            if(
    Ext.isIE){
                
    this.el.repaint();
            }
            
    this.update(this.value);
        },

        
    // private
        
    showPrevMonth : function(e){
            
    this.update(this.activeDate.add("mo", -1));
        },

        
    // private
        
    showNextMonth : function(e){
            
    this.update(this.activeDate.add("mo"1));
        },

        
    // private
        
    update : function(date){
            
    this.activeDate date;
            for(var 
    x=0;x<this.noOfMonth;x++) {
                var 
    days date.getDaysInMonth();
                var 
    firstOfMonth date.getFirstDateOfMonth();
                var 
    startingPos firstOfMonth.getDay()-this.startDay;
        
                if(
    startingPos <= this.startDay){
                    
    startingPos += 7;
                }
        
                var 
    pm date.add("mo", -1);
                var 
    prevStart pm.getDaysInMonth()-startingPos;
        
                var 
    cells this.cellsArray[x].elements;
                var 
    textEls this.textNodesArray[x];
                
    days += startingPos;
        
                
    // convert everything to numbers so it's fast
                
    var day 86400000;
                var 
    = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
                var 
    min this.minDate this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
                var 
    max this.maxDate this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
                var 
    ddMatch this.disabledDatesRE;
                var 
    ddText this.disabledDatesText;
                var 
    ddays this.disabledDays this.disabledDays.join("") : false;
                var 
    ddaysText this.disabledDaysText;
                var 
    format this.format;
        
                var 
    setCellClass = function(calcell){
                    
    cell.title "";
                    var 
    d.getTime();
                    
    cell.firstChild.dateValue t;

                    
    // disabling
                    
    if(min) {
                        
    cell.className " x-date-disabled";
                        
    cell.title cal.minText;
                        return;
                    }
                    if(
    max) {
                        
    cell.className " x-date-disabled";
                        
    cell.title cal.maxText;
                        return;
                    }
                    if(
    ddays){
                        if(
    ddays.indexOf(d.getDay()) != -1){
                            
    cell.title ddaysText;
                            
    cell.className " x-date-disabled";
                        }
                    }
                    if(
    ddMatch && format){
                        var 
    fvalue d.dateFormat(format);
                        if(
    ddMatch.test(fvalue)){
                            
    cell.title ddText.replace("%0"fvalue);
                            
    cell.className " x-date-disabled";
                        }
                    }
                    
    //Only active days need to be selected
                    
    if(cal.eventDates && (cell.className.indexOf('x-date-active') != -1)) {
                        for(var 
    y=0cal.eventDates.lengthy++) {
                            var 
    evtDate cal.eventDates[y].clearTime().getTime();
                            if(
    == evtDate) {
                                
    cell.className += " x-date-selected";
                                break;
                            }
                        }
                    }
                };
        
                var 
    0;
                for(; 
    startingPosi++) {
                    
    textEls[i].innerHTML = (++prevStart);
                    
    d.setDate(d.getDate()+1);
                    
    cells[i].className "x-date-prevday";
                    
    setCellClass(thiscells[i]);
                }
                for(; 
    daysi++){
                    
    intDay startingPos 1;
                    
    textEls[i].innerHTML = (intDay);
                    
    d.setDate(d.getDate()+1);
                    
    cells[i].className "x-date-active";
                    
    setCellClass(thiscells[i]);
                }
                var 
    extraDays 0;
                for(; 
    42i++) {
                     
    textEls[i].innerHTML = (++extraDays);
                     
    d.setDate(d.getDate()+1);
                     
    cells[i].className "x-date-nextday";
                     
    setCellClass(thiscells[i]);
                }
                var 
    monthLabel Ext.get('monthLabel' x);                    
                
    monthLabel.update(this.monthNames[date.getMonth()] + " " date.getFullYear());

                if(!
    this.internalRender){
                    var 
    main this.el.dom.firstChild;
                    var 
    main.offsetWidth;
                    
    this.el.setWidth(this.el.getBorderWidth("lr"));
                    
    Ext.fly(main).setWidth(w);
                    
    this.internalRender true;
                    
    // opera does not respect the auto grow header center column
                    // then, after it gets a width opera refuses to recalculate
                    // without a second pass
                    
    if(Ext.isOpera && !this.secondPass){
                        
    main.rows[0].cells[1].style.width = (- (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
                        
    this.secondPass true;
                        
    this.update.defer(10this, [date]);
                    }
                }
                
    date date.add('mo',1);
            }    
        }
    });
    Ext.reg('mmcalendar'Ext.ux.MultiMonthCalendar); 

    CSS
    PHP Code:
    .x-date-right, .x-date-left {
            
    height:16px;

    Usage
    PHP Code:
    Ext.onReady(function() {

        var 
    evtDate = new Array();
        for(var 
    x=010x++) {
            
    evtDate[x] = new Date(200791).add('d',x*5);
        }
        new 
    Ext.FormPanel({
            
    id'multi-cal-panel',
            
    title"Multi Calendars",
            
    layout'table',
            
    width600,
            
    layoutConfig: {
                
    columns3
            
    },
            
    defaults: {
                
    style: {
                    
    margin'10px'
                
    }
            },
            
    items: [{
                
    xtype'mmcalendar',
                
    value: new Date(200791),
                
    noOfMonth 3,
                
    eventDates evtDate
            
    }],
            
    renderToExt.getBody()
        });
        
    Ext.QuickTips.init();
    }); 
    Attached Images

  2. #2
    Sencha User
    Join Date
    Apr 2012
    Location
    Austin, Texas
    Posts
    4
    Vote Rating
    0
    brian.moeskau is an unknown quantity at this point

      0  

    Default


    Nice! I assume that this should be in the 2.0 Extensions forum, correct? Also, you should add a page to the wiki if you get a chance: http://extjs.com/learn/Ext_Extensions

  3. #3
    franckxx
    Guest

    Default


    wouahh ! nice improvement !

    thx, it's look great !

  4. #4
    Ext User DigitalSkyline's Avatar
    Join Date
    Apr 2007
    Location
    Rochester, MI
    Posts
    461
    Vote Rating
    1
    DigitalSkyline is on a distinguished road

      0  

    Default


    Cool. questions... do the calendars always show in a single row? Would it then be possible to show 12 months, say in a 3x4/4x3 grid? See where I'm going w/this interrogatory?

  5. #5
    Ext User aungii's Avatar
    Join Date
    Jul 2007
    Location
    Singapore
    Posts
    4
    Vote Rating
    0
    aungii is on a distinguished road

      0  

    Default Re:

    Re:


    Brian : Thanks for the moving to the correct forum and sorry for my mistake. It should be under 2.0 extension. I will add the extension page on wiki as soon as possible.

    DigitalSkyline : You got a good point. Yes, This version show all the months in one row.
    For 4x3 or 3x4 grid, we may need to consider for navigation. Right now, "prev month" button is on first month, and "next month" button is on
    last month. We can move the navigation button out of the calendar with seperate row on the top. I will try to modify it in coming version.

    Update : http://extjs.com/learn/Extension:Multi_Month_Calendar

  6. #6
    Ext User
    Join Date
    Apr 2007
    Posts
    122
    Vote Rating
    0
    cocorossello is on a distinguished road

      0  

    Default


    Hi,

    Thx for the extension.

    It would be interesting to configure a "layout" for the calendars, so you can do a 4x3 calendar, show 3 calendars in a column, etc....

    It will be a limited TableLayout, where you can configure the number of rows and columns and 1 calendar will be in each cell of the table.

    I will look how i can do that with your extension

  7. #7
    Ext User
    Join Date
    Nov 2007
    Posts
    2
    Vote Rating
    0
    wdvee is on a distinguished road

      0  

    Default Event handling?

    Event handling?


    Thanks for this extension, I was looking for something like this!

    One question: how would you register handlers for the select or click event? I would like to register a "selection" handler that gets the selected date and adds it to an array of selected dates for multiple selection support.

    Cheers

  8. #8
    Ext User
    Join Date
    Nov 2007
    Posts
    2
    Vote Rating
    0
    wdvee is on a distinguished road

      0  

    Default Found it

    Found it


    Quote Originally Posted by wdvee View Post
    Thanks for this extension, I was looking for something like this!

    One question: how would you register handlers for the select or click event? I would like to register a "selection" handler that gets the selected date and adds it to an array of selected dates for multiple selection support.

    Cheers
    Found it! I found the relevant code in DatePicker.js, and got the 'select' event to bubble up out of the MultiMonthCalendar. I'm working on the multiple selections now, and functions to get/set those.

  9. #9
    Ext User aungii's Avatar
    Join Date
    Jul 2007
    Location
    Singapore
    Posts
    4
    Vote Rating
    0
    aungii is on a distinguished road

      0  

    Default New Version

    New Version


    Can define no. of month to be displayed in a row.

    Changes :
    1. Adding one more attribute named "noOfMonthPerRow"
    2. Modify initComponent() and onRender()
    3. Adding more CSS.

    Updated Code :
    PHP Code:
    ..... 
    /**
        * @cfg {Array} eventDates
        * List of Dates which have an event (show as selected in UI)
        */    
        
    eventDates null,
        
        
    /**
        * @cfg {Array} noOfMonthPerRow
        * No. Of Month to be displayed in a row
        */    
        
    noOfMonthPerRow 3,
        
        
    initComponent : function(){
            
    Ext.ux.MultiMonthCalendar.superclass.initComponent.call(this);
            
    this.value this.value ?
                     
    this.value.clearTime() : new Date().clearTime();
            
    this.initDisabledDays();   
            
    this.noOfMonthPerRow this.noOfMonthPerRow this.noOfMonth ?this.noOfMonth this.noOfMonthPerRow    
        
    }, 
    ...

    ...
    onRender : function(containerposition){
            var 
    = ["<table cellspacing='0'>"];
            if(
    this.noOfMonthPerRow 1) {
                
    m.push("<tr><td class='x-date-left'><a href='#' title='"this.prevText ,"'> </a></td>");
                   
    m.push("<td class='x-date-left' colspan='"+ eval(this.noOfMonthPerRow *-3) +"'></td>");
                   
    m.push("<td class='x-date-right'><a href='#' title='"this.nextText ,"'> </a></td></tr><tr>");
           } else {
                   
    //Special case of only one month
                   
    m.push("<tr><td><table cellspacing='0' width='100%'><tr><td class='x-date-left'><a href='#' title='"this.prevText ,"'> </a></td>");
                   
    m.push("<td class='x-date-right'><a href='#' title='"this.nextText ,"'> </a></td></tr></table></td></tr><tr>");
           }       
            for(var 
    x=0x<this.noOfMonthx++) {            
                
    m.push("<td><table border='1' cellspacing='0'><tr>");
                
    m.push("<td class='x-date-middle' align='center'><span id='monthLabel" "'></span></td>");
                
    m.push("</tr><tr><td><table class='x-date-inner' id='inner-date"+x+"' cellspacing='0'><thead><tr>");        
                var 
    dn this.dayNames;
                for(var 
    07i++){
                   var 
    this.startDay+i;
                   if(
    6){
                       
    d-7;
                   }
                    
    m.push("<th><span>"dn[d].substr(0,1), "</span></th>");
                }
                
    m[m.length] = "</tr></thead><tbody><tr>";
                for(var 
    042i++) {
                    if(
    == && != 0){
                        
    m[m.length] = "</tr><tr>";
                    }
                    
    m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
                }
                
    m[m.length] = '</tr></tbody></table></td></tr></table></td>';
                if(
    != this.noOfMonth-1) {
                    
    m[m.length] = "<td width='3'></td>";
                }
                if( (
    x+1) % this.noOfMonthPerRow == && x!= 0) {
                    
    m[m.length] = "</tr><tr>"
                }            
            }
            
    m[m.length] = "</tr></table>";            
            var 
    el document.createElement("div");
            
    el.className "x-date-picker";
            
    el.innerHTML m.join(""); 
    .... 
    CSS :
    PHP Code:
    .x-date-right, .x-date-left {
        
    height:16px;
    }
    .
    x-date-right a {
        
    float:right;

    Usage :
    PHP Code:
    ...
    items: [{
                
    xtype'mmcalendar',
                
    value: new Date(200791),
                
    noOfMonth 6,
                
    noOfMonthPerRow 3,
                
    eventDates evtDate
            
    }], 
    ... 
    Attached Images
    Aung II

  10. #10
    Ext User DigitalSkyline's Avatar
    Join Date
    Apr 2007
    Location
    Rochester, MI
    Posts
    461
    Vote Rating
    1
    DigitalSkyline is on a distinguished road

      0  

    Default Cool

    Cool


    Nice work, can't wait to give it a try.