← Back to team overview

openerp-dev-web team mailing list archive

lp:~openerp-dev/openobject-client-web/trunk-proto61-dhtmlx-scheduler-vda into lp:~openerp-dev/openobject-client-web/trunk-proto61

 

vda(Open ERP) has proposed merging lp:~openerp-dev/openobject-client-web/trunk-proto61-dhtmlx-scheduler-vda into lp:~openerp-dev/openobject-client-web/trunk-proto61.

Requested reviews:
  OpenERP R&D Team (openerp-dev)

For more details, see:
https://code.launchpad.net/~openerp-dev/openobject-client-web/trunk-proto61-dhtmlx-scheduler-vda/+merge/57859
-- 
https://code.launchpad.net/~openerp-dev/openobject-client-web/trunk-proto61-dhtmlx-scheduler-vda/+merge/57859
Your team OpenERP R&D Team is requested to review the proposed merge of lp:~openerp-dev/openobject-client-web/trunk-proto61-dhtmlx-scheduler-vda into lp:~openerp-dev/openobject-client-web/trunk-proto61.
=== modified file 'addons/base/controllers/main.py'
--- addons/base/controllers/main.py	2011-04-13 12:50:42 +0000
+++ addons/base/controllers/main.py	2011-04-15 12:10:15 +0000
@@ -96,6 +96,7 @@
         return {
             "session_id": req.session_id,
             "uid": req.session._uid,
+            'locale_code': req.session._lang and req.session._lang.get('iso_code', False) or False
         }
 
     @openerpweb.jsonrequest
@@ -377,7 +378,6 @@
         """
         Model = request.session.model(model)
         records = Model.read(ids, fields)
-
         record_map = dict((record['id'], record) for record in records)
 
         return [record_map[id] for id in ids if record_map.get(id)]

=== modified file 'addons/base/static/src/js/chrome.js'
--- addons/base/static/src/js/chrome.js	2011-04-13 14:20:01 +0000
+++ addons/base/static/src/js/chrome.js	2011-04-15 12:10:15 +0000
@@ -275,6 +275,7 @@
         this.module_list = [];
         this.module_loaded = {"base": true};
         this.context = {};
+        this.locale_code = false;
     },
     start: function() {
         this.session_restore();
@@ -384,6 +385,7 @@
         this.rpc("/base/session/login", params, function(result) {
             self.session_id = result.session_id;
             self.uid = result.uid;
+            self.locale_code = result.locale_code;
             self.session_save();
             self.on_session_valid();
             if (success_callback)

=== modified file 'addons/base_calendar/__openerp__.py'
--- addons/base_calendar/__openerp__.py	2011-04-13 14:20:01 +0000
+++ addons/base_calendar/__openerp__.py	2011-04-15 12:10:15 +0000
@@ -4,7 +4,9 @@
     "depends": ['base'],
     "js": [
         'static/lib/dhtmlxScheduler/codebase/dhtmlxscheduler.js',
-        'static/src/js/calendar.js'
+        'static/lib/jquery/jquery.glob.js',
+        'static/lib/jquery/date.js',
+        'static/src/js/calendar.js',
     ],
     "css": ['static/lib/dhtmlxScheduler/codebase/dhtmlxscheduler.css'],
     'active': True

=== modified file 'addons/base_calendar/controllers/main.py'
--- addons/base_calendar/controllers/main.py	2011-04-11 10:31:18 +0000
+++ addons/base_calendar/controllers/main.py	2011-04-15 12:10:15 +0000
@@ -1,356 +1,10 @@
 from base.controllers.main import View
-import openerpweb, time, math, re, datetime as DT, pytz
-
-COLOR_PALETTE = ['#f57900', '#cc0000', '#d400a8', '#75507b', '#3465a4', '#73d216', '#c17d11', '#edd400',
-                 '#fcaf3e', '#ef2929', '#ff00c9', '#ad7fa8', '#729fcf', '#8ae234', '#e9b96e', '#fce94f',
-                 '#ff8e00', '#ff0000', '#b0008c', '#9000ff', '#0078ff', '#00ff00', '#e6ff00', '#ffff00',
-                 '#905000', '#9b0000', '#840067', '#510090', '#0000c9', '#009b00', '#9abe00', '#ffc900', ]
-
-_colorline = ['#%02x%02x%02x' % (25 + ((r + 10) % 11) * 23, 5 + ((g + 1) % 11) * 20, 25 + ((b + 4) % 11) * 23) for r in range(11) for g in range(11) for b in range(11) ]
-
-DT_SERVER_FORMATS = {
-  'datetime' : '%Y-%m-%d %H:%M:%S',
-  'date' : '%Y-%m-%d',
-  'time' : '%H:%M:%S'
-}
-
-DT_FORMAT_INFO = {'datetime' : ('%Y-%m-%d %H:%M:%S', DT.datetime, 0, 6),
-                  'date': ('%Y-%m-%d', DT.date, 0, 3),
-                  'time': ('%H:%M:%S', DT.time, 3, 6)}
-
-def choice_colors(n):
-        if n > len(COLOR_PALETTE):
-            return _colorline[0:-1:len(_colorline) / (n + 1)]
-        elif n:
-            return COLOR_PALETTE[:n]
-        return []
+import openerpweb
 
 class CalendarView(View):
     _cp_path = "/base_calendar/calendarview"
     
-    mode = 'month'
-    date_start = None
-    date_delay = None
-    date_stop = None
-    color_field = None
-    day_length = 8
-    use_search = False
-    selected_day = None
-    date_format = '%Y-%m-%d'
-    info_fields = []
-    fields = {}
-    events = []
-
-    colors = {}
-    color_values = []
-
-    remote_timezone = 'utc'
-    client_timezone = False
-    
-    calendar_fields = {}
-    concurrency_info = None
-
-    ids = []
-    model = ''
-    domain = []
-    context = {}
-    
     @openerpweb.jsonrequest
     def load(self, req, model, view_id):
         fields_view = self.fields_view_get(req, model, view_id, 'calendar')
-        return {'fields_view':fields_view}
-    
-    def convert(self, event):
-        fields = [self.date_start]
-        if self.date_stop:
-            fields.append(self.date_stop)
-            
-        for fld in fields:
-            fld_type = self.fields[fld]['type']
-            fmt = DT_SERVER_FORMATS[fld_type]
-            if event[fld] and fmt:
-                event[fld] = time.strptime(event[fld], fmt)
-            
-            # default start/stop time is 9:00 AM / 5:00 PM
-            if fld_type == 'date' and event[fld]:
-                ds = list(event[fld])
-                if fld == self.date_start:
-                    ds[3] = 9
-                elif fld == self.date_stop:
-                    ds[3] = 17
-                event[fld] = tuple(ds)
-             
-    
-    @openerpweb.jsonrequest
-    def schedule_events(self, req, **kw):
-        self.model = kw['model']
-        self.mode = kw.get('mode') or self.mode or 'month'
-        self.fields = kw['fields']
-        self.color_field = kw.get('color_field') or self.color_field or None
-        self.colors = kw.get('colors') or {}
-        self.calendar_fields = kw['calendar_fields']
-        self.info_fields = kw['info_fields']
-        self.date_start = self.calendar_fields['date_start']['name']
-        self.domain = kw.get('domain') or []
-        
-        self.remote_timezone = req.session.remote_timezone
-        self.client_timezone = req.session.client_timezone
-        
-        if self.calendar_fields.get('date_stop'):
-            self.date_stop = self.calendar_fields['date_stop']['name']
-        
-        if self.calendar_fields.get('date_delay'):
-            self.date_delay = self.calendar_fields['date_delay']['name']
-            
-        model = req.session.model(self.model)
-        event_ids = model.search(self.domain)
-        
-        self.events = model.read(event_ids, self.fields.keys())
-        result = []
-        self.date_format = req.session._lang and req.session._lang['date_format']
-        
-        if self.color_field:
-            for evt in self.events:
-                key = evt[self.color_field]
-                name = key
-                value = key
-                if isinstance(key, list): # M2O, XMLRPC returns List instead of Tuple
-                    name = key[0]
-                    value = key[-1]
-                    evt[self.color_field] = key = key[-1]
-                if isinstance(key, tuple): # M2O
-                    value, name = key
-                self.colors[key] = (name, value, None)
-            
-            colors = choice_colors(len(self.colors))
-            for i, (key, value) in enumerate(self.colors.items()):
-                self.colors[key] = [value[0], value[1], colors[i]]
-        
-        for evt in self.events:
-            self.convert(evt)
-            a = self.get_event_widget(evt)
-            result.append(a)
-            
-        return {'result':result,'sidebar':self.colors}
-    
-    def parsedatetime(self, string):
-        
-        kind = 'datetime'
-
-        if '-' in string and ':' in string:
-            kind = 'datetime'
-        elif '-' in string:
-            kind = 'date'
-        elif ':' in string:
-            kind = 'time'
-        
-        fmt, obj, i, j = DT_FORMAT_INFO[kind]
-        return obj(*time.strptime(string, fmt)[i:j])
-    
-    def parse_datetime(self, value, kind="datetime", as_timetuple=False):
-        server_format = DT_SERVER_FORMATS[kind]
-        local_format = self.date_format
-        if not value:
-            return False
-
-        if isinstance(value, (time.struct_time, tuple)):
-            value = time.strftime(local_format, value)
-
-        try:
-            value = time.strptime(value, local_format)
-        except ValueError:
-            try:
-                # might be in server format already (e.g. filter domain)
-                value = time.strptime(value, server_format)
-            except ValueError:
-                try:
-                    dt = list(time.localtime())
-                    dt[2] = int(value)
-                    value = tuple(dt)
-                except:
-                    return False
-    
-        if kind == "datetime":
-            try:
-                value = self.tz_convert(value, 'parse')
-            except Exception,e:
-                print "*******************Error in timezone parsing *********",e
-    
-        if as_timetuple:
-            return value
-        
-        return time.strftime(server_format, value)
-
-    
-    @openerpweb.jsonrequest
-    def edit_events(self, req,**kw):
-        data = {}
-        ds = self.parsedatetime(kw['start_date'])
-        de = self.parsedatetime(kw['end_date'])
-        data[kw['calendar_fields']['date_start']['name']] = self.parse_datetime(ds.timetuple())
-        
-        if 'date_stop' in kw['calendar_fields']:
-            data[kw['calendar_fields']['date_stop']['name']] = self.parse_datetime(de.timetuple())
-        elif 'date_delay' in kw['calendar_fields']:
-            day_length = kw['calendar_fields']['day_length']
-            
-            tds = time.mktime(ds.timetuple())
-            tde = time.mktime(de.timetuple())
-            
-            n = (tde - tds) / (60 * 60)
-
-            if n > day_length:
-                d = math.floor(n / 24)
-                h = n % 24
-
-                n = d * day_length + h
-
-            data[kw['calendar_fields']['date_delay']['name']] = n
-        error = None
-        try:
-            req.session.model(kw['model']).write([int(kw['id'])], data)
-        except Exception, e:
-            error = e
-        return error
-    
-    def tz_convert(self, struct_time, action):
-        # if no client timezone is configured, consider the client is in the same
-        # timezone as the server
-        lzone = pytz.timezone(self.client_timezone or self.remote_timezone)
-        szone = pytz.timezone(self.remote_timezone)
-        dt = DT.datetime.fromtimestamp(time.mktime(struct_time))
-    
-        if action == 'parse':
-            fromzone = lzone
-            tozone = szone
-        elif action == 'format':
-            fromzone = szone
-            tozone = lzone
-        else:
-            raise Exception("_tz_convert action should be 'parse' or 'format'. Not '%s'" % (action, ))
-    
-        localized_original_datetime = fromzone.localize(dt, is_dst=True)
-        destination_datetime = localized_original_datetime.astimezone(tozone)
-        return destination_datetime.timetuple()
-    
-    def format_datetime(self, value, kind="datetime", as_timetuple=False):
-        """Convert date value to the local datetime considering timezone info.
-    
-        @param value: the date value
-        @param kind: type of the date value (date, time or datetime)
-        @param as_timetuple: return timetuple
-    
-        @type value: basestring or time.time_tuple)
-    
-        @return: string or timetuple
-        """
-    
-        server_format = DT_SERVER_FORMATS[kind]
-        local_format = self.date_format
-    
-        if not value:
-            return ''
-    
-        if isinstance(value, (time.struct_time, tuple)):
-            value = time.strftime(server_format, value)
-    
-        if isinstance(value, DT.datetime):
-            value = value
-            try:
-                value = DT.datetime.strptime(value[:10], server_format)
-                return value.strftime(local_format)
-            except:
-                return ''
-    
-        value = value.strip()
-    
-        # remove trailing miliseconds
-        value = re.sub("(.*?)(\s+\d{2}:\d{2}:\d{2})(\.\d+)?$", "\g<1>\g<2>", value)
-    
-        # add time part in value if missing
-        if kind == 'datetime' and not re.search('\s+\d{2}:\d{2}:\d{2}?$', value):
-            value += ' 00:00:00'
-    
-        # remove time part from value
-        elif kind == 'date':
-            value = re.sub('\s+\d{2}:\d{2}:\d{2}(\.\d+)?$', '', value)
-    
-        value = time.strptime(value, server_format)
-    
-        if kind == "datetime":
-            try:
-                value = self.tz_convert(value, 'format')
-            except Exception, e:
-                print "\n\n\n************ Error in timezone formatting", e
-    
-        if as_timetuple:
-            return value
-    
-        return time.strftime(local_format, value)
-    
-    def get_event_widget(self, event):
-        title = ''       # the title
-        description = [] # the description
-        
-        if self.info_fields:
-
-            f = self.info_fields[0]
-            s = event[f]
-            
-            if isinstance(s, (tuple, list)): s = s[-1]
-            
-            title = s
-            for f in self.info_fields[1:]:
-                s = event[f]
-                if isinstance(s, (tuple, list)):
-                    s = s[-1]
-                if s:
-                    description.append(str(s))
-        
-        starts = event.get(self.date_start)
-        ends = event.get(self.date_delay) or 1.0
-        span = 0
-        
-        if starts and ends:
-
-            n = 0
-            h = ends
-
-            if ends == self.day_length:
-                span = 1
-
-            elif ends > self.day_length:
-                n = ends / self.day_length
-                h = ends % self.day_length
-
-                n = int(math.floor(n))
-                
-                if h > 0:
-                    span = n + 1
-                else:
-                    span = n
-            ends = time.localtime(time.mktime(starts) + (h * 60 * 60) + (n * 24 * 60 * 60))
-            
-        if starts and self.date_stop:
-
-            ends = event.get(self.date_stop)
-            if not ends:
-                ends = time.localtime(time.mktime(starts) + 60 * 60)
-
-            tds = time.mktime(starts)
-            tde = time.mktime(ends)
-
-            if tds >= tde:
-                tde = tds + 60 * 60
-                ends = time.localtime(tde)
-
-            n = (tde - tds) / (60 * 60)
-
-            if n >= self.day_length:
-                span = math.ceil(n / 24)
-        
-        starts = self.format_datetime(starts, "datetime", True)
-        ends = self.format_datetime(ends, "datetime", True)
-        title = title.strip()
-        description = ', '.join(description).strip()
-        return {'id': event['id'], 'start_date': str(DT.datetime(*starts[:6])), 'end_date': str(DT.datetime(*ends[:6])), 'text': title, 'title': description, 'color': self.colors[event[self.color_field]][-1]}
+        return {'fields_view': fields_view}

=== added directory 'addons/base_calendar/static/lib/jquery'
=== added file 'addons/base_calendar/static/lib/jquery/date.js'
--- addons/base_calendar/static/lib/jquery/date.js	1970-01-01 00:00:00 +0000
+++ addons/base_calendar/static/lib/jquery/date.js	2011-04-15 12:10:15 +0000
@@ -0,0 +1,135 @@
+/*
+ * jQuery Date
+ *
+ * Copyright 2010 Marc Grabanski
+ * Licensed under the MIT license
+ *
+ *
+ * Depends:
+ *	jquery.glob.js
+ */
+(function( $, undefined ) {
+
+if ( typeof( $.global.culture ) == "undefined" ) {
+	$.global.culture = $.global.cultures[ "default" ];
+}
+
+$.date = function ( datestring, formatstring ) {
+	var calendar = $.global.culture.calendar,
+		format = formatstring ? formatstring : calendar.patterns.d,
+		date = datestring ? $.global.parseDate(datestring, format) : new Date();
+	return {
+		refresh: function() {
+			calendar = $.global.culture.calendar;
+			format = formatstring || calendar.patterns.d;
+			return this;
+		},
+		setFormat: function( formatstring ) {
+			if ( formatstring ) {
+				format = formatstring;
+			}	
+			return this;
+		},
+		setDay: function( day ) {
+			date = new Date( date.getFullYear(), date.getMonth(), day );
+			return this;
+		},
+		adjust: function( period, offset ) {
+			var day = period == "D" ? date.getDate() + offset : date.getDate(), 
+				month = period == "M" ? date.getMonth() + offset : date.getMonth(), 
+				year = period == "Y" ? date.getFullYear() + offset : date.getFullYear();
+			date = new Date( year, month, day );
+			return this;
+		},
+		daysInMonth: function( year, month ) {
+			year = year || date.getFullYear();
+			month = month || date.getMonth();
+			return 32 - new Date( year, month, 32 ).getDate();
+		},
+		monthname: function() {
+			return calendar.months.names[ date.getMonth() ];
+		},
+		year: function() {
+			return date.getFullYear();
+		},
+		weekdays: function() {
+			// TODO take firstDay into account
+			var result = [];
+			for ( var dow = 0; dow < 7; dow++ ) {
+				var day = ( dow + calendar.firstDay ) % 7;
+				result.push( {
+					shortname: calendar.days.namesShort[ day ],
+					fullname: calendar.days.names[ day ],
+				});
+			}
+			return result;
+		},
+		days: function() {
+			var result = [],
+				firstDayOfMonth = new Date( this.year(), date.getMonth(), 1 ).getDay(),
+				leadDays = ( firstDayOfMonth - calendar.firstDay + 7 ) % 7,
+				rows = Math.ceil( ( leadDays + this.daysInMonth() ) / 7),
+				printDate = new Date( this.year(), date.getMonth(), 1 - leadDays );
+			for ( var row = 0; row < rows; row++ ) {
+				var week = result[ result.length ] = {
+					number: this.iso8601Week( printDate ),
+					days: []
+				};
+				for ( var dayx = 0; dayx < 7; dayx++ ) {
+					var day = week.days[ week.days.length ] = {
+						lead: printDate.getMonth() != date.getMonth(),
+						date: printDate.getDate(),
+						current: this.selected && this.selected.equal( printDate ),
+						today: today.equal( printDate )
+					};
+					day.render = day.selectable = !day.lead;
+					this.eachDay( day );
+					// TODO use adjust("D", 1)?
+					printDate.setDate( printDate.getDate() + 1 );
+				}
+			}
+			return result;
+		},
+		iso8601Week: function( date ) {
+			var checkDate = new Date( date.getTime() );
+			// Find Thursday of this week starting on Monday
+			checkDate.setDate( checkDate.getDate() + 4 - ( checkDate.getDay() || 7 ) );
+			var time = checkDate.getTime();
+			checkDate.setMonth( 0 ); // Compare with Jan 1
+			checkDate.setDate( 1 );
+			return Math.floor( Math.round( ( time - checkDate ) / 86400000) / 7 ) + 1;
+		},
+		select: function() {
+			this.selected = this.clone();
+			return this;
+		},
+		// TODO create new Date with year, month, day instead
+		clone: function() {
+			return $.date( this.format(), format );
+		},
+		// TODO compare year, month, day each for better performance
+		equal: function( other ) {
+			function format( date ) {
+				return $.global.format( date, "d" );
+			}
+			return format( date ) == format( other );
+		},
+		date: function() {
+			return date;
+		},
+		format: function( formatstring ) {
+			return $.global.format( date, formatstring ? formatstring : format );
+		},
+		calendar: function( newcalendar ) {
+			if ( newcalendar ) {
+				calendar = newcalendar;
+				return this;
+			}
+			return calendar;
+		}
+	}
+}
+
+var today = $.date();
+
+}( jQuery ));
\ No newline at end of file

=== added file 'addons/base_calendar/static/lib/jquery/jquery.glob.js'
--- addons/base_calendar/static/lib/jquery/jquery.glob.js	1970-01-01 00:00:00 +0000
+++ addons/base_calendar/static/lib/jquery/jquery.glob.js	2011-04-15 12:10:15 +0000
@@ -0,0 +1,1342 @@
+/*!
+ * jQuery Globalization Plugin
+ * http://github.com/jquery/jquery-global
+ *
+ * Copyright Software Freedom Conservancy, Inc.
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ */
+(function() {
+
+var Globalization = {}, localized = { en: {} };
+localized["default"] = localized.en;
+
+Globalization.extend = function( deep ) {
+    var target = arguments[ 1 ] || {};
+    for ( var i = 2, l = arguments.length; i < l; i++ ) {
+        var source = arguments[ i ];
+        if ( source ) {
+            for ( var field in source ) {
+                var sourceVal = source[ field ];
+                if ( typeof sourceVal !== "undefined" ) {
+                    if ( deep && (isObject( sourceVal ) || isArray( sourceVal )) ) {
+                        var targetVal = target[ field ];
+                        // extend onto the existing value, or create a new one
+                        targetVal = targetVal && (isObject( targetVal ) || isArray( targetVal ))
+                            ? targetVal
+                            : (isArray( sourceVal ) ? [] : {});
+                        target[ field ] = this.extend( true, targetVal, sourceVal );
+                    }
+                    else {
+                        target[ field ] = sourceVal;
+                    }
+                }
+            }
+        }
+    }
+    return target;
+}
+
+Globalization.findClosestCulture = function(name) {
+    var match;
+    if ( !name ) {
+        return this.culture || this.cultures["default"];
+    }
+    if ( isString( name ) ) {
+        name = name.split( ',' );
+    }
+    if ( isArray( name ) ) {
+        var lang,
+            cultures = this.cultures,
+            list = name,
+            i, l = list.length,
+            prioritized = [];
+        for ( i = 0; i < l; i++ ) {
+            name = trim( list[ i ] );
+            var pri, parts = name.split( ';' );
+            lang = trim( parts[ 0 ] );
+            if ( parts.length === 1 ) {
+                pri = 1;
+            }
+            else {
+                name = trim( parts[ 1 ] );
+                if ( name.indexOf("q=") === 0 ) {
+                    name = name.substr( 2 );
+                    pri = parseFloat( name, 10 );
+                    pri = isNaN( pri ) ? 0 : pri;
+                }
+                else {
+                    pri = 1;
+                }
+            }
+            prioritized.push( { lang: lang, pri: pri } );
+        }
+        prioritized.sort(function(a, b) {
+            return a.pri < b.pri ? 1 : -1;
+        });
+        for ( i = 0; i < l; i++ ) {
+            lang = prioritized[ i ].lang;
+            match = cultures[ lang ];
+            // exact match?
+            if ( match ) {
+                return match;
+            }
+        }
+        for ( i = 0; i < l; i++ ) {
+            lang = prioritized[ i ].lang;
+            // for each entry try its neutral language
+            do {
+                var index = lang.lastIndexOf( "-" );
+                if ( index === -1 ) {
+                    break;
+                }
+                // strip off the last part. e.g. en-US => en
+                lang = lang.substr( 0, index );
+                match = cultures[ lang ];
+                if ( match ) {
+                    return match;
+                }
+            }
+            while ( 1 );
+        }
+    }
+    else if ( typeof name === 'object' ) {
+        return name;
+    }
+    return match || null;
+}
+Globalization.preferCulture = function(name) {
+    this.culture = this.findClosestCulture( name ) || this.cultures["default"];
+}
+Globalization.localize = function(key, culture, value) {
+    // usign default culture in case culture is not provided
+    if (typeof culture !== 'string') {
+        culture = this.culture.name || this.culture || "default";
+    }
+    culture = this.cultures[ culture ] || { name: culture };
+
+    var local = localized[ culture.name ];
+    if ( arguments.length === 3 ) {
+        if ( !local) {
+            local = localized[ culture.name ] = {};
+        }
+        local[ key ] = value;
+    }
+    else {
+        if ( local ) {
+            value = local[ key ];
+        }
+        if ( typeof value === 'undefined' ) {
+            var language = localized[ culture.language ];
+            if ( language ) {
+                value = language[ key ];
+            }
+            if ( typeof value === 'undefined' ) {
+                value = localized["default"][ key ];
+            }
+        }
+    }
+    return typeof value === "undefined" ? null : value;
+}
+Globalization.format = function(value, format, culture) {
+    culture = this.findClosestCulture( culture );
+    if ( typeof value === "number" ) {
+        value = formatNumber( value, format, culture );
+    }
+    else if ( value instanceof Date ) {
+        value = formatDate( value, format, culture );
+    }
+    return value;
+}
+Globalization.parseInt = function(value, radix, culture) {
+    return Math.floor( this.parseFloat( value, radix, culture ) );
+}
+Globalization.parseFloat = function(value, radix, culture) {
+	// make radix optional
+	if (typeof radix === "string") {
+		culture = radix;
+		radix = 10;
+	}
+	
+    culture = this.findClosestCulture( culture );
+    var ret = NaN,
+        nf = culture.numberFormat;
+
+	if (value.indexOf(culture.numberFormat.currency.symbol) > -1) {
+		// remove currency symbol
+		value = value.replace(culture.numberFormat.currency.symbol, "");
+		// replace decimal seperator
+		value = value.replace(culture.numberFormat.currency["."], culture.numberFormat["."]);
+	}
+
+    // trim leading and trailing whitespace
+    value = trim( value );
+
+    // allow infinity or hexidecimal
+    if (regexInfinity.test(value)) {
+        ret = parseFloat(value, radix);
+    }
+    else if (!radix && regexHex.test(value)) {
+        ret = parseInt(value, 16);
+    }
+    else {
+        var signInfo = parseNegativePattern( value, nf, nf.pattern[0] ),
+            sign = signInfo[0],
+            num = signInfo[1];
+        // determine sign and number
+        if ( sign === "" && nf.pattern[0] !== "-n" ) {
+            signInfo = parseNegativePattern( value, nf, "-n" );
+            sign = signInfo[0];
+            num = signInfo[1];
+        }
+        sign = sign || "+";
+        // determine exponent and number
+        var exponent,
+            intAndFraction,
+            exponentPos = num.indexOf( 'e' );
+        if ( exponentPos < 0 ) exponentPos = num.indexOf( 'E' );
+        if ( exponentPos < 0 ) {
+            intAndFraction = num;
+            exponent = null;
+        }
+        else {
+            intAndFraction = num.substr( 0, exponentPos );
+            exponent = num.substr( exponentPos + 1 );
+        }
+        // determine decimal position
+        var integer,
+            fraction,
+            decSep = nf['.'],
+            decimalPos = intAndFraction.indexOf( decSep );
+        if ( decimalPos < 0 ) {
+            integer = intAndFraction;
+            fraction = null;
+        }
+        else {
+            integer = intAndFraction.substr( 0, decimalPos );
+            fraction = intAndFraction.substr( decimalPos + decSep.length );
+        }
+        // handle groups (e.g. 1,000,000)
+        var groupSep = nf[","];
+        integer = integer.split(groupSep).join('');
+        var altGroupSep = groupSep.replace(/\u00A0/g, " ");
+        if ( groupSep !== altGroupSep ) {
+            integer = integer.split(altGroupSep).join('');
+        }
+        // build a natively parsable number string
+        var p = sign + integer;
+        if ( fraction !== null ) {
+            p += '.' + fraction;
+        }
+        if ( exponent !== null ) {
+            // exponent itself may have a number patternd
+            var expSignInfo = parseNegativePattern( exponent, nf, "-n" );
+            p += 'e' + (expSignInfo[0] || "+") + expSignInfo[1];
+        }
+        if ( regexParseFloat.test( p ) ) {
+            ret = parseFloat( p );
+        }
+    }
+    return ret;
+}
+Globalization.parseDate = function(value, formats, culture) {
+    culture = this.findClosestCulture( culture );
+
+    var date, prop, patterns;
+    if ( formats ) {
+        if ( typeof formats === "string" ) {
+            formats = [ formats ];
+        }
+        if ( formats.length ) {
+            for ( var i = 0, l = formats.length; i < l; i++ ) {
+                var format = formats[ i ];
+                if ( format ) {
+                    date = parseExact( value, format, culture );
+                    if ( date ) {
+                        break;
+                    }
+                }
+            }
+        }
+    }
+    else {
+        patterns = culture.calendar.patterns;
+        for ( prop in patterns ) {
+            date = parseExact( value, patterns[prop], culture );
+            if ( date ) {
+                break;
+            }
+        }
+    }
+    return date || null;
+}
+
+// 1.    When defining a culture, all fields are required except the ones stated as optional.
+// 2.    You can use Globalization.extend to copy an existing culture and provide only the differing values,
+//       a good practice since most cultures do not differ too much from the 'default' culture.
+//       DO use the 'default' culture if you do this, as it is the only one that definitely
+//       exists.
+// 3.    Other plugins may add to the culture information provided by extending it. However,
+//       that plugin may extend it prior to the culture being defined, or after. Therefore,
+//       do not overwrite values that already exist when defining the baseline for a culture,
+//       by extending your culture object with the existing one.
+// 4.    Each culture should have a ".calendars" object with at least one calendar named "standard"
+//       which serves as the default calendar in use by that culture.
+// 5.    Each culture should have a ".calendar" object which is the current calendar being used,
+//       it may be dynamically changed at any time to one of the calendars in ".calendars".
+
+// To define a culture, use the following pattern, which handles defining the culture based
+// on the 'default culture, extending it with the existing culture if it exists, and defining
+// it if it does not exist.
+// Globalization.cultures.foo = Globalization.extend(true, Globalization.extend(true, {}, Globalization.cultures['default'], fooCulture), Globalization.cultures.foo)
+
+var cultures = Globalization.cultures = Globalization.cultures || {};
+var en = cultures["default"] = cultures.en = Globalization.extend(true, {
+    // A unique name for the culture in the form <language code>-<country/region code>
+    name: "en",
+    // the name of the culture in the english language
+    englishName: "English",
+    // the name of the culture in its own language
+    nativeName: "English",
+    // whether the culture uses right-to-left text
+    isRTL: false,
+    // 'language' is used for so-called "specific" cultures.
+    // For example, the culture "es-CL" means "Spanish, in Chili".
+    // It represents the Spanish-speaking culture as it is in Chili,
+    // which might have different formatting rules or even translations
+    // than Spanish in Spain. A "neutral" culture is one that is not
+    // specific to a region. For example, the culture "es" is the generic
+    // Spanish culture, which may be a more generalized version of the language
+    // that may or may not be what a specific culture expects.
+    // For a specific culture like "es-CL", the 'language' field refers to the
+    // neutral, generic culture information for the language it is using.
+    // This is not always a simple matter of the string before the dash.
+    // For example, the "zh-Hans" culture is netural (Simplified Chinese).
+    // And the 'zh-SG' culture is Simplified Chinese in Singapore, whose lanugage
+    // field is "zh-CHS", not "zh".
+    // This field should be used to navigate from a specific culture to it's
+    // more general, neutral culture. If a culture is already as general as it
+    // can get, the language may refer to itself.
+    language: "en",
+    // numberFormat defines general number formatting rules, like the digits in
+    // each grouping, the group separator, and how negative numbers are displayed.
+    numberFormat: {
+        // [negativePattern]
+        // Note, numberFormat.pattern has no 'positivePattern' unlike percent and currency,
+        // but is still defined as an array for consistency with them.
+        //  negativePattern: one of "(n)|-n|- n|n-|n -"
+        pattern: ["-n"],
+        // number of decimal places normally shown
+        decimals: 2,
+        // string that separates number groups, as in 1,000,000
+        ',': ",",
+        // string that separates a number from the fractional portion, as in 1.99
+        '.': ".",
+        // array of numbers indicating the size of each number group.
+        // TODO: more detailed description and example
+        groupSizes: [3],
+        // symbol used for positive numbers
+        '+': "+",
+        // symbol used for negative numbers
+        '-': "-",
+        percent: {
+            // [negativePattern, positivePattern]
+            //     negativePattern: one of "-n %|-n%|-%n|%-n|%n-|n-%|n%-|-% n|n %-|% n-|% -n|n- %"
+            //     positivePattern: one of "n %|n%|%n|% n"
+            pattern: ["-n %","n %"],
+            // number of decimal places normally shown
+            decimals: 2,
+            // array of numbers indicating the size of each number group.
+            // TODO: more detailed description and example
+            groupSizes: [3],
+            // string that separates number groups, as in 1,000,000
+            ',': ",",
+            // string that separates a number from the fractional portion, as in 1.99
+            '.': ".",
+            // symbol used to represent a percentage
+            symbol: "%"
+        },
+        currency: {
+            // [negativePattern, positivePattern]
+            //     negativePattern: one of "($n)|-$n|$-n|$n-|(n$)|-n$|n-$|n$-|-n $|-$ n|n $-|$ n-|$ -n|n- $|($ n)|(n $)"
+            //     positivePattern: one of "$n|n$|$ n|n $"
+            pattern: ["($n)","$n"],
+            // number of decimal places normally shown
+            decimals: 2,
+            // array of numbers indicating the size of each number group.
+            // TODO: more detailed description and example
+            groupSizes: [3],
+            // string that separates number groups, as in 1,000,000
+            ',': ",",
+            // string that separates a number from the fractional portion, as in 1.99
+            '.': ".",
+            // symbol used to represent currency
+            symbol: "$"
+        }
+    },
+    // calendars defines all the possible calendars used by this culture.
+    // There should be at least one defined with name 'standard', and is the default
+    // calendar used by the culture.
+    // A calendar contains information about how dates are formatted, information about
+    // the calendar's eras, a standard set of the date formats,
+    // translations for day and month names, and if the calendar is not based on the Gregorian
+    // calendar, conversion functions to and from the Gregorian calendar.
+    calendars: {
+        standard: {
+            // name that identifies the type of calendar this is
+            name: "Gregorian_USEnglish",
+            // separator of parts of a date (e.g. '/' in 11/05/1955)
+            '/': "/",
+            // separator of parts of a time (e.g. ':' in 05:44 PM)
+            ':': ":",
+            // the first day of the week (0 = Sunday, 1 = Monday, etc)
+            firstDay: 0,
+            days: {
+                // full day names
+                names: ["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],
+                // abbreviated day names
+                namesAbbr: ["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],
+                // shortest day names
+                namesShort: ["Su","Mo","Tu","We","Th","Fr","Sa"]
+            },
+            months: {
+                // full month names (13 months for lunar calendards -- 13th month should be "" if not lunar)
+                names: ["January","February","March","April","May","June","July","August","September","October","November","December",""],
+                // abbreviated month names
+                namesAbbr: ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec",""]
+            },
+            // AM and PM designators in one of these forms:
+            // The usual view, and the upper and lower case versions
+            //      [standard,lowercase,uppercase]
+            // The culture does not use AM or PM (likely all standard date formats use 24 hour time)
+            //      null
+            AM: ["AM", "am", "AM"],
+            PM: ["PM", "pm", "PM"],
+            eras: [
+                // eras in reverse chronological order.
+                // name: the name of the era in this culture (e.g. A.D., C.E.)
+                // start: when the era starts in ticks (gregorian, gmt), null if it is the earliest supported era.
+                // offset: offset in years from gregorian calendar
+                { "name": "A.D.", "start": null, "offset": 0 }
+            ],
+            // when a two digit year is given, it will never be parsed as a four digit
+            // year greater than this year (in the appropriate era for the culture)
+            // Set it as a full year (e.g. 2029) or use an offset format starting from
+            // the current year: "+19" would correspond to 2029 if the current year 2010.
+            twoDigitYearMax: 2029,
+            // set of predefined date and time patterns used by the culture
+            // these represent the format someone in this culture would expect
+            // to see given the portions of the date that are shown.
+            patterns: {
+                // short date pattern
+                d: "M/d/yyyy",
+                // long date pattern
+                D: "dddd, MMMM dd, yyyy",
+                // short time pattern
+                t: "h:mm tt",
+                // long time pattern
+                T: "h:mm:ss tt",
+                // long date, short time pattern
+                f: "dddd, MMMM dd, yyyy h:mm tt",
+                // long date, long time pattern
+                F: "dddd, MMMM dd, yyyy h:mm:ss tt",
+                // month/day pattern
+                M: "MMMM dd",
+                // month/year pattern
+                Y: "yyyy MMMM",
+                // S is a sortable format that does not vary by culture
+                S: "yyyy\u0027-\u0027MM\u0027-\u0027dd\u0027T\u0027HH\u0027:\u0027mm\u0027:\u0027ss"
+            }
+            // optional fields for each calendar:
+            /*
+            monthsGenitive:
+                Same as months but used when the day preceeds the month.
+                Omit if the culture has no genitive distinction in month names.
+                For an explaination of genitive months, see http://blogs.msdn.com/michkap/archive/2004/12/25/332259.aspx
+            convert:
+                Allows for the support of non-gregorian based calendars. This convert object is used to
+                to convert a date to and from a gregorian calendar date to handle parsing and formatting.
+                The two functions:
+                    fromGregorian(date)
+                        Given the date as a parameter, return an array with parts [year, month, day]
+                        corresponding to the non-gregorian based year, month, and day for the calendar.
+                    toGregorian(year, month, day)
+                        Given the non-gregorian year, month, and day, return a new Date() object
+                        set to the corresponding date in the gregorian calendar.
+            */
+        }
+    }
+}, cultures.en);
+en.calendar = en.calendar || en.calendars.standard;
+
+var regexTrim = /^\s+|\s+$/g,
+    regexInfinity = /^[+-]?infinity$/i,
+    regexHex = /^0x[a-f0-9]+$/i,
+    regexParseFloat = /^[+-]?\d*\.?\d*(e[+-]?\d+)?$/,
+    toString = Object.prototype.toString;
+
+function startsWith(value, pattern) {
+    return value.indexOf( pattern ) === 0;
+}
+
+function endsWith(value, pattern) {
+    return value.substr( value.length - pattern.length ) === pattern;
+}
+
+function trim(value) {
+    return (value+"").replace( regexTrim, "" );
+}
+
+function zeroPad(str, count, left) {
+    for (var l=str.length; l < count; l++) {
+        str = (left ? ('0' + str) : (str + '0'));
+    }
+    return str;
+}
+
+function isArray(obj) {
+    return toString.call(obj) === "[object Array]";
+}
+
+function isString(obj) {
+    return toString.call(obj) === "[object String]";
+}
+
+function isObject(obj) {
+    return toString.call(obj) === "[object Object]";
+}
+
+function arrayIndexOf( array, item ) {
+    if ( array.indexOf ) {
+        return array.indexOf( item );
+    }
+    for ( var i = 0, length = array.length; i < length; i++ ) {
+        if ( array[ i ] === item ) {
+            return i;
+        }
+    }
+    return -1;
+}
+
+// *************************************** Numbers ***************************************
+
+function expandNumber(number, precision, formatInfo) {
+    var groupSizes = formatInfo.groupSizes,
+        curSize = groupSizes[ 0 ],
+        curGroupIndex = 1,
+        factor = Math.pow( 10, precision ),
+        rounded = Math.round( number * factor ) / factor;
+    if ( !isFinite(rounded) ) {
+        rounded = number;
+    }
+    number = rounded;
+
+    var numberString = number+"",
+        right = "",
+        split = numberString.split(/e/i),
+        exponent = split.length > 1 ? parseInt( split[ 1 ], 10 ) : 0;
+    numberString = split[ 0 ];
+    split = numberString.split( "." );
+    numberString = split[ 0 ];
+    right = split.length > 1 ? split[ 1 ] : "";
+
+    var l;
+    if ( exponent > 0 ) {
+        right = zeroPad( right, exponent, false );
+        numberString += right.slice( 0, exponent );
+        right = right.substr( exponent );
+    }
+    else if ( exponent < 0 ) {
+        exponent = -exponent;
+        numberString = zeroPad( numberString, exponent + 1 );
+        right = numberString.slice( -exponent, numberString.length ) + right;
+        numberString = numberString.slice( 0, -exponent );
+    }
+
+    if ( precision > 0 ) {
+        right = formatInfo['.'] +
+            ((right.length > precision) ? right.slice( 0, precision ) : zeroPad( right, precision ));
+    }
+    else {
+        right = "";
+    }
+
+    var stringIndex = numberString.length - 1,
+        sep = formatInfo[","],
+        ret = "";
+
+    while ( stringIndex >= 0 ) {
+        if ( curSize === 0 || curSize > stringIndex ) {
+            return numberString.slice( 0, stringIndex + 1 ) + ( ret.length ? ( sep + ret + right ) : right );
+        }
+        ret = numberString.slice( stringIndex - curSize + 1, stringIndex + 1 ) + ( ret.length ? ( sep + ret ) : "" );
+
+        stringIndex -= curSize;
+
+        if ( curGroupIndex < groupSizes.length ) {
+            curSize = groupSizes[ curGroupIndex ];
+            curGroupIndex++;
+        }
+    }
+    return numberString.slice( 0, stringIndex + 1 ) + sep + ret + right;
+}
+
+
+function parseNegativePattern(value, nf, negativePattern) {
+    var neg = nf["-"],
+        pos = nf["+"],
+        ret;
+    switch (negativePattern) {
+        case "n -":
+            neg = ' ' + neg;
+            pos = ' ' + pos;
+            // fall through
+        case "n-":
+            if ( endsWith( value, neg ) ) {
+                ret = [ '-', value.substr( 0, value.length - neg.length ) ];
+            }
+            else if ( endsWith( value, pos ) ) {
+                ret = [ '+', value.substr( 0, value.length - pos.length ) ];
+            }
+            break;
+        case "- n":
+            neg += ' ';
+            pos += ' ';
+            // fall through
+        case "-n":
+            if ( startsWith( value, neg ) ) {
+                ret = [ '-', value.substr( neg.length ) ];
+            }
+            else if ( startsWith(value, pos) ) {
+                ret = [ '+', value.substr( pos.length ) ];
+            }
+            break;
+        case "(n)":
+            if ( startsWith( value, '(' ) && endsWith( value, ')' ) ) {
+                ret = [ '-', value.substr( 1, value.length - 2 ) ];
+            }
+            break;
+    }
+    return ret || [ '', value ];
+}
+
+function formatNumber(value, format, culture) {
+    if ( !format || format === 'i' ) {
+        return culture.name.length ? value.toLocaleString() : value.toString();
+    }
+    format = format || "D";
+
+    var nf = culture.numberFormat,
+        number = Math.abs(value),
+        precision = -1,
+        pattern;
+    if (format.length > 1) precision = parseInt( format.slice( 1 ), 10 );
+
+    var current = format.charAt( 0 ).toUpperCase(),
+        formatInfo;
+
+    switch (current) {
+        case "D":
+            pattern = 'n';
+            if (precision !== -1) {
+                number = zeroPad( ""+number, precision, true );
+            }
+            if (value < 0) number = -number;
+            break;
+        case "N":
+            formatInfo = nf;
+            // fall through
+        case "C":
+            formatInfo = formatInfo || nf.currency;
+            // fall through
+        case "P":
+            formatInfo = formatInfo || nf.percent;
+            pattern = value < 0 ? formatInfo.pattern[0] : (formatInfo.pattern[1] || "n");
+            if (precision === -1) precision = formatInfo.decimals;
+            number = expandNumber( number * (current === "P" ? 100 : 1), precision, formatInfo );
+            break;
+        default:
+            throw "Bad number format specifier: " + current;
+    }
+
+    var patternParts = /n|\$|-|%/g,
+        ret = "";
+    for (;;) {
+        var index = patternParts.lastIndex,
+            ar = patternParts.exec(pattern);
+
+        ret += pattern.slice( index, ar ? ar.index : pattern.length );
+
+        if (!ar) {
+            break;
+        }
+
+        switch (ar[0]) {
+            case "n":
+                ret += number;
+                break;
+            case "$":
+                ret += nf.currency.symbol;
+                break;
+            case "-":
+                // don't make 0 negative
+                if ( /[1-9]/.test( number ) ) {
+                    ret += nf["-"];
+                }
+                break;
+            case "%":
+                ret += nf.percent.symbol;
+                break;
+        }
+    }
+
+    return ret;
+}
+
+// *************************************** Dates ***************************************
+
+function outOfRange(value, low, high) {
+    return value < low || value > high;
+}
+
+function expandYear(cal, year) {
+    // expands 2-digit year into 4 digits.
+    var now = new Date(),
+        era = getEra(now);
+    if ( year < 100 ) {
+        var twoDigitYearMax = cal.twoDigitYearMax;
+        twoDigitYearMax = typeof twoDigitYearMax === 'string' ? new Date().getFullYear() % 100 + parseInt( twoDigitYearMax, 10 ) : twoDigitYearMax;
+        var curr = getEraYear( now, cal, era );
+        year += curr - ( curr % 100 );
+        if ( year > twoDigitYearMax ) {
+            year -= 100;
+        }
+    }
+    return year;
+}
+
+function getEra(date, eras) {
+    if ( !eras ) return 0;
+    var start, ticks = date.getTime();
+    for ( var i = 0, l = eras.length; i < l; i++ ) {
+        start = eras[ i ].start;
+        if ( start === null || ticks >= start ) {
+            return i;
+        }
+    }
+    return 0;
+}
+
+function toUpper(value) {
+    // 'he-IL' has non-breaking space in weekday names.
+    return value.split( "\u00A0" ).join(' ').toUpperCase();
+}
+
+function toUpperArray(arr) {
+    var results = [];
+    for ( var i = 0, l = arr.length; i < l; i++ ) {
+        results[i] = toUpper(arr[i]);
+    }
+    return results;
+}
+
+function getEraYear(date, cal, era, sortable) {
+    var year = date.getFullYear();
+    if ( !sortable && cal.eras ) {
+        // convert normal gregorian year to era-shifted gregorian
+        // year by subtracting the era offset
+        year -= cal.eras[ era ].offset;
+    }
+    return year;
+}
+
+function getDayIndex(cal, value, abbr) {
+    var ret,
+        days = cal.days,
+        upperDays = cal._upperDays;
+    if ( !upperDays ) {
+        cal._upperDays = upperDays = [
+            toUpperArray( days.names ),
+            toUpperArray( days.namesAbbr ),
+            toUpperArray( days.namesShort )
+        ];
+    }
+    value = toUpper( value );
+    if ( abbr ) {
+        ret = arrayIndexOf( upperDays[ 1 ], value );
+        if ( ret === -1 ) {
+            ret = arrayIndexOf( upperDays[ 2 ], value );
+        }
+    }
+    else {
+        ret = arrayIndexOf( upperDays[ 0 ], value );
+    }
+    return ret;
+}
+
+function getMonthIndex(cal, value, abbr) {
+    var months = cal.months,
+        monthsGen = cal.monthsGenitive || cal.months,
+        upperMonths = cal._upperMonths,
+        upperMonthsGen = cal._upperMonthsGen;
+    if ( !upperMonths ) {
+        cal._upperMonths = upperMonths = [
+            toUpperArray( months.names ),
+            toUpperArray( months.namesAbbr )
+        ];
+        cal._upperMonthsGen = upperMonthsGen = [
+            toUpperArray( monthsGen.names ),
+            toUpperArray( monthsGen.namesAbbr )
+        ];
+    }
+    value = toUpper( value );
+    var i = arrayIndexOf( abbr ? upperMonths[ 1 ] : upperMonths[ 0 ], value );
+    if ( i < 0 ) {
+        i = arrayIndexOf( abbr ? upperMonthsGen[ 1 ] : upperMonthsGen[ 0 ], value );
+    }
+    return i;
+}
+
+function appendPreOrPostMatch(preMatch, strings) {
+    // appends pre- and post- token match strings while removing escaped characters.
+    // Returns a single quote count which is used to determine if the token occurs
+    // in a string literal.
+    var quoteCount = 0,
+        escaped = false;
+    for ( var i = 0, il = preMatch.length; i < il; i++ ) {
+        var c = preMatch.charAt( i );
+        switch ( c ) {
+            case '\'':
+                if ( escaped ) {
+                    strings.push( "'" );
+                }
+                else {
+                    quoteCount++;
+                }
+                escaped = false;
+                break;
+            case '\\':
+                if ( escaped ) {
+                    strings.push( "\\" );
+                }
+                escaped = !escaped;
+                break;
+            default:
+                strings.push( c );
+                escaped = false;
+                break;
+        }
+    }
+    return quoteCount;
+}
+
+function expandFormat(cal, format) {
+    // expands unspecified or single character date formats into the full pattern.
+    format = format || "F";
+    var pattern,
+        patterns = cal.patterns,
+        len = format.length;
+    if ( len === 1 ) {
+        pattern = patterns[ format ];
+        if ( !pattern ) {
+            throw "Invalid date format string '" + format + "'.";
+        }
+        format = pattern;
+    }
+    else if ( len === 2  && format.charAt(0) === "%" ) {
+        // %X escape format -- intended as a custom format string that is only one character, not a built-in format.
+        format = format.charAt( 1 );
+    }
+    return format;
+}
+
+function getParseRegExp(cal, format) {
+    // converts a format string into a regular expression with groups that
+    // can be used to extract date fields from a date string.
+    // check for a cached parse regex.
+    var re = cal._parseRegExp;
+    if ( !re ) {
+        cal._parseRegExp = re = {};
+    }
+    else {
+        var reFormat = re[ format ];
+        if ( reFormat ) {
+            return reFormat;
+        }
+    }
+
+    // expand single digit formats, then escape regular expression characters.
+    var expFormat = expandFormat( cal, format ).replace( /([\^\$\.\*\+\?\|\[\]\(\)\{\}])/g, "\\\\$1" ),
+        regexp = ["^"],
+        groups = [],
+        index = 0,
+        quoteCount = 0,
+        tokenRegExp = getTokenRegExp(),
+        match;
+
+    // iterate through each date token found.
+    while ( (match = tokenRegExp.exec( expFormat )) !== null ) {
+        var preMatch = expFormat.slice( index, match.index );
+        index = tokenRegExp.lastIndex;
+
+        // don't replace any matches that occur inside a string literal.
+        quoteCount += appendPreOrPostMatch( preMatch, regexp );
+        if ( quoteCount % 2 ) {
+            regexp.push( match[ 0 ] );
+            continue;
+        }
+
+        // add a regex group for the token.
+        var m = match[ 0 ],
+            len = m.length,
+            add;
+        switch ( m ) {
+            case 'dddd': case 'ddd':
+            case 'MMMM': case 'MMM':
+            case 'gg': case 'g':
+                add = "(\\D+)";
+                break;
+            case 'tt': case 't':
+                add = "(\\D*)";
+                break;
+            case 'yyyy':
+            case 'fff':
+            case 'ff':
+            case 'f':
+                add = "(\\d{" + len + "})";
+                break;
+            case 'dd': case 'd':
+            case 'MM': case 'M':
+            case 'yy': case 'y':
+            case 'HH': case 'H':
+            case 'hh': case 'h':
+            case 'mm': case 'm':
+            case 'ss': case 's':
+                add = "(\\d\\d?)";
+                break;
+            case 'zzz':
+                add = "([+-]?\\d\\d?:\\d{2})";
+                break;
+            case 'zz': case 'z':
+                add = "([+-]?\\d\\d?)";
+                break;
+            case '/':
+                add = "(\\" + cal["/"] + ")";
+                break;
+            default:
+                throw "Invalid date format pattern '" + m + "'.";
+                break;
+        }
+        if ( add ) {
+            regexp.push( add );
+        }
+        groups.push( match[ 0 ] );
+    }
+    appendPreOrPostMatch( expFormat.slice( index ), regexp );
+    regexp.push( "$" );
+
+    // allow whitespace to differ when matching formats.
+    var regexpStr = regexp.join( '' ).replace( /\s+/g, "\\s+" ),
+        parseRegExp = {'regExp': regexpStr, 'groups': groups};
+
+    // cache the regex for this format.
+    return re[ format ] = parseRegExp;
+}
+
+function getTokenRegExp() {
+    // regular expression for matching date and time tokens in format strings.
+    return /\/|dddd|ddd|dd|d|MMMM|MMM|MM|M|yyyy|yy|y|hh|h|HH|H|mm|m|ss|s|tt|t|fff|ff|f|zzz|zz|z|gg|g/g;
+}
+
+function parseExact(value, format, culture) {
+    // try to parse the date string by matching against the format string
+    // while using the specified culture for date field names.
+    value = trim( value );
+    var cal = culture.calendar,
+        // convert date formats into regular expressions with groupings.
+        // use the regexp to determine the input format and extract the date fields.
+        parseInfo = getParseRegExp(cal, format),
+        match = new RegExp(parseInfo.regExp).exec(value);
+    if (match === null) {
+        return null;
+    }
+    // found a date format that matches the input.
+    var groups = parseInfo.groups,
+        era = null, year = null, month = null, date = null, weekDay = null,
+        hour = 0, hourOffset, min = 0, sec = 0, msec = 0, tzMinOffset = null,
+        pmHour = false;
+    // iterate the format groups to extract and set the date fields.
+    for ( var j = 0, jl = groups.length; j < jl; j++ ) {
+        var matchGroup = match[ j + 1 ];
+        if ( matchGroup ) {
+            var current = groups[ j ],
+                clength = current.length,
+                matchInt = parseInt( matchGroup, 10 );
+            switch ( current ) {
+                case 'dd': case 'd':
+                    // Day of month.
+                    date = matchInt;
+                    // check that date is generally in valid range, also checking overflow below.
+                    if ( outOfRange( date, 1, 31 ) ) return null;
+                    break;
+                case 'MMM':
+                case 'MMMM':
+                    month = getMonthIndex( cal, matchGroup, clength === 3 );
+                    if ( outOfRange( month, 0, 11 ) ) return null;
+                    break;
+                case 'M': case 'MM':
+                    // Month.
+                    month = matchInt - 1;
+                    if ( outOfRange( month, 0, 11 ) ) return null;
+                    break;
+                case 'y': case 'yy':
+                case 'yyyy':
+                    year = clength < 4 ? expandYear( cal, matchInt ) : matchInt;
+                    if ( outOfRange( year, 0, 9999 ) ) return null;
+                    break;
+                case 'h': case 'hh':
+                    // Hours (12-hour clock).
+                    hour = matchInt;
+                    if ( hour === 12 ) hour = 0;
+                    if ( outOfRange( hour, 0, 11 ) ) return null;
+                    break;
+                case 'H': case 'HH':
+                    // Hours (24-hour clock).
+                    hour = matchInt;
+                    if ( outOfRange( hour, 0, 23 ) ) return null;
+                    break;
+                case 'm': case 'mm':
+                    // Minutes.
+                    min = matchInt;
+                    if ( outOfRange( min, 0, 59 ) ) return null;
+                    break;
+                case 's': case 'ss':
+                    // Seconds.
+                    sec = matchInt;
+                    if ( outOfRange( sec, 0, 59 ) ) return null;
+                    break;
+                case 'tt': case 't':
+                    // AM/PM designator.
+                    // see if it is standard, upper, or lower case PM. If not, ensure it is at least one of
+                    // the AM tokens. If not, fail the parse for this format.
+                    pmHour = cal.PM && ( matchGroup === cal.PM[0] || matchGroup === cal.PM[1] || matchGroup === cal.PM[2] );
+                    if ( !pmHour && ( !cal.AM || (matchGroup !== cal.AM[0] && matchGroup !== cal.AM[1] && matchGroup !== cal.AM[2]) ) ) return null;
+                    break;
+                case 'f':
+                    // Deciseconds.
+                case 'ff':
+                    // Centiseconds.
+                case 'fff':
+                    // Milliseconds.
+                    msec = matchInt * Math.pow( 10, 3-clength );
+                    if ( outOfRange( msec, 0, 999 ) ) return null;
+                    break;
+                case 'ddd':
+                    // Day of week.
+                case 'dddd':
+                    // Day of week.
+                    weekDay = getDayIndex( cal, matchGroup, clength === 3 );
+                    if ( outOfRange( weekDay, 0, 6 ) ) return null;
+                    break;
+                case 'zzz':
+                    // Time zone offset in +/- hours:min.
+                    var offsets = matchGroup.split( /:/ );
+                    if ( offsets.length !== 2 ) return null;
+                    hourOffset = parseInt( offsets[ 0 ], 10 );
+                    if ( outOfRange( hourOffset, -12, 13 ) ) return null;
+                    var minOffset = parseInt( offsets[ 1 ], 10 );
+                    if ( outOfRange( minOffset, 0, 59 ) ) return null;
+                    tzMinOffset = (hourOffset * 60) + (startsWith( matchGroup, '-' ) ? -minOffset : minOffset);
+                    break;
+                case 'z': case 'zz':
+                    // Time zone offset in +/- hours.
+                    hourOffset = matchInt;
+                    if ( outOfRange( hourOffset, -12, 13 ) ) return null;
+                    tzMinOffset = hourOffset * 60;
+                    break;
+                case 'g': case 'gg':
+                    var eraName = matchGroup;
+                    if ( !eraName || !cal.eras ) return null;
+                    eraName = trim( eraName.toLowerCase() );
+                    for ( var i = 0, l = cal.eras.length; i < l; i++ ) {
+                        if ( eraName === cal.eras[ i ].name.toLowerCase() ) {
+                            era = i;
+                            break;
+                        }
+                    }
+                    // could not find an era with that name
+                    if ( era === null ) return null;
+                    break;
+            }
+        }
+    }
+    var result = new Date(), defaultYear, convert = cal.convert;
+    defaultYear = convert ? convert.fromGregorian( result )[ 0 ] : result.getFullYear();
+    if ( year === null ) {
+        year = defaultYear;
+    }
+    else if ( cal.eras ) {
+        // year must be shifted to normal gregorian year
+        // but not if year was not specified, its already normal gregorian
+        // per the main if clause above.
+        year += cal.eras[ (era || 0) ].offset;
+    }
+    // set default day and month to 1 and January, so if unspecified, these are the defaults
+    // instead of the current day/month.
+    if ( month === null ) {
+        month = 0;
+    }
+    if ( date === null ) {
+        date = 1;
+    }
+    // now have year, month, and date, but in the culture's calendar.
+    // convert to gregorian if necessary
+    if ( convert ) {
+        result = convert.toGregorian( year, month, date );
+        // conversion failed, must be an invalid match
+        if ( result === null ) return null;
+    }
+    else {
+        // have to set year, month and date together to avoid overflow based on current date.
+        result.setFullYear( year, month, date );
+        // check to see if date overflowed for specified month (only checked 1-31 above).
+        if ( result.getDate() !== date ) return null;
+        // invalid day of week.
+        if ( weekDay !== null && result.getDay() !== weekDay ) {
+            return null;
+        }
+    }
+    // if pm designator token was found make sure the hours fit the 24-hour clock.
+    if ( pmHour && hour < 12 ) {
+        hour += 12;
+    }
+    result.setHours( hour, min, sec, msec );
+    if ( tzMinOffset !== null ) {
+        // adjust timezone to utc before applying local offset.
+        var adjustedMin = result.getMinutes() - ( tzMinOffset + result.getTimezoneOffset() );
+        // Safari limits hours and minutes to the range of -127 to 127.  We need to use setHours
+        // to ensure both these fields will not exceed this range.  adjustedMin will range
+        // somewhere between -1440 and 1500, so we only need to split this into hours.
+        result.setHours( result.getHours() + parseInt( adjustedMin / 60, 10 ), adjustedMin % 60 );
+    }
+    return result;
+}
+
+function formatDate(value, format, culture) {
+    var cal = culture.calendar,
+        convert = cal.convert;
+    if ( !format || !format.length || format === 'i' ) {
+        var ret;
+        if ( culture && culture.name.length ) {
+            if ( convert ) {
+                // non-gregorian calendar, so we cannot use built-in toLocaleString()
+                ret = formatDate( value, cal.patterns.F, culture );
+            }
+            else {
+                var eraDate = new Date( value.getTime() ),
+                    era = getEra( value, cal.eras );
+                eraDate.setFullYear( getEraYear( value, cal, era ) );
+                ret = eraDate.toLocaleString();
+            }
+        }
+        else {
+            ret = value.toString();
+        }
+        return ret;
+    }
+
+    var eras = cal.eras,
+        sortable = format === "s";
+    format = expandFormat( cal, format );
+
+    // Start with an empty string
+    ret = [];
+    var hour,
+        zeros = ['0','00','000'],
+        foundDay,
+        checkedDay,
+        dayPartRegExp = /([^d]|^)(d|dd)([^d]|$)/g,
+        quoteCount = 0,
+        tokenRegExp = getTokenRegExp(),
+        converted;
+
+    function padZeros(num, c) {
+        var r, s = num+'';
+        if ( c > 1 && s.length < c ) {
+            r = ( zeros[ c - 2 ] + s);
+            return r.substr( r.length - c, c );
+        }
+        else {
+            r = s;
+        }
+        return r;
+    }
+
+    function hasDay() {
+        if ( foundDay || checkedDay ) {
+            return foundDay;
+        }
+        foundDay = dayPartRegExp.test( format );
+        checkedDay = true;
+        return foundDay;
+    }
+
+    function getPart( date, part ) {
+        if ( converted ) {
+            return converted[ part ];
+        }
+        switch ( part ) {
+            case 0: return date.getFullYear();
+            case 1: return date.getMonth();
+            case 2: return date.getDate();
+        }
+    }
+
+    if ( !sortable && convert ) {
+        converted = convert.fromGregorian( value );
+    }
+
+    for (;;) {
+        // Save the current index
+        var index = tokenRegExp.lastIndex,
+            // Look for the next pattern
+            ar = tokenRegExp.exec( format );
+
+        // Append the text before the pattern (or the end of the string if not found)
+        var preMatch = format.slice( index, ar ? ar.index : format.length );
+        quoteCount += appendPreOrPostMatch( preMatch, ret );
+
+        if ( !ar ) {
+            break;
+        }
+
+        // do not replace any matches that occur inside a string literal.
+        if ( quoteCount % 2 ) {
+            ret.push( ar[ 0 ] );
+            continue;
+        }
+
+        var current = ar[ 0 ],
+            clength = current.length;
+
+        switch ( current ) {
+            case "ddd":
+                //Day of the week, as a three-letter abbreviation
+            case "dddd":
+                // Day of the week, using the full name
+                names = (clength === 3) ? cal.days.namesAbbr : cal.days.names;
+                ret.push( names[ value.getDay() ] );
+                break;
+            case "d":
+                // Day of month, without leading zero for single-digit days
+            case "dd":
+                // Day of month, with leading zero for single-digit days
+                foundDay = true;
+                ret.push( padZeros( getPart( value, 2 ), clength ) );
+                break;
+            case "MMM":
+                // Month, as a three-letter abbreviation
+            case "MMMM":
+                // Month, using the full name
+                var part = getPart( value, 1 );
+                ret.push( (cal.monthsGenitive && hasDay())
+                    ? cal.monthsGenitive[ clength === 3 ? "namesAbbr" : "names" ][ part ]
+                    : cal.months[ clength === 3 ? "namesAbbr" : "names" ][ part ] );
+                break;
+            case "M":
+                // Month, as digits, with no leading zero for single-digit months
+            case "MM":
+                // Month, as digits, with leading zero for single-digit months
+                ret.push( padZeros( getPart( value, 1 ) + 1, clength ) );
+                break;
+            case "y":
+                // Year, as two digits, but with no leading zero for years less than 10
+            case "yy":
+                // Year, as two digits, with leading zero for years less than 10
+            case "yyyy":
+                // Year represented by four full digits
+                part = converted ? converted[ 0 ] : getEraYear( value, cal, getEra( value, eras ), sortable );
+                if ( clength < 4 ) {
+                    part = part % 100;
+                }
+                ret.push( padZeros( part, clength ) );
+                break;
+            case "h":
+                // Hours with no leading zero for single-digit hours, using 12-hour clock
+            case "hh":
+                // Hours with leading zero for single-digit hours, using 12-hour clock
+                hour = value.getHours() % 12;
+                if ( hour === 0 ) hour = 12;
+                ret.push( padZeros( hour, clength ) );
+                break;
+            case "H":
+                // Hours with no leading zero for single-digit hours, using 24-hour clock
+            case "HH":
+                // Hours with leading zero for single-digit hours, using 24-hour clock
+                ret.push( padZeros( value.getHours(), clength ) );
+                break;
+            case "m":
+                // Minutes with no leading zero  for single-digit minutes
+            case "mm":
+                // Minutes with leading zero  for single-digit minutes
+                ret.push( padZeros( value.getMinutes(), clength ) );
+                break;
+            case "s":
+                // Seconds with no leading zero for single-digit seconds
+            case "ss":
+                // Seconds with leading zero for single-digit seconds
+                ret.push( padZeros(value .getSeconds(), clength ) );
+                break;
+            case "t":
+                // One character am/pm indicator ("a" or "p")
+            case "tt":
+                // Multicharacter am/pm indicator
+                part = value.getHours() < 12 ? (cal.AM ? cal.AM[0] : " ") : (cal.PM ? cal.PM[0] : " ");
+                ret.push( clength === 1 ? part.charAt( 0 ) : part );
+                break;
+            case "f":
+                // Deciseconds
+            case "ff":
+                // Centiseconds
+            case "fff":
+                // Milliseconds
+                ret.push( padZeros( value.getMilliseconds(), 3 ).substr( 0, clength ) );
+                break;
+            case "z":
+                // Time zone offset, no leading zero
+            case "zz":
+                // Time zone offset with leading zero
+                hour = value.getTimezoneOffset() / 60;
+                ret.push( (hour <= 0 ? '+' : '-') + padZeros( Math.floor( Math.abs( hour ) ), clength ) );
+                break;
+            case "zzz":
+                // Time zone offset with leading zero
+                hour = value.getTimezoneOffset() / 60;
+                ret.push( (hour <= 0 ? '+' : '-') + padZeros( Math.floor( Math.abs( hour ) ), 2 ) +
+                    // Hard coded ":" separator, rather than using cal.TimeSeparator
+                    // Repeated here for consistency, plus ":" was already assumed in date parsing.
+                    ":" + padZeros( Math.abs( value.getTimezoneOffset() % 60 ), 2 ) );
+                break;
+            case "g":
+            case "gg":
+                if ( cal.eras ) {
+                    ret.push( cal.eras[ getEra(value, eras) ].name );
+                }
+                break;
+        case "/":
+            ret.push( cal["/"] );
+            break;
+        default:
+            throw "Invalid date format pattern '" + current + "'.";
+            break;
+        }
+    }
+    return ret.join( '' );
+}
+
+// EXPORTS
+jQuery.global = Globalization;
+
+})();
+
+

=== added file 'addons/base_calendar/static/lib/jquery/jquery.tmpl.js'
--- addons/base_calendar/static/lib/jquery/jquery.tmpl.js	1970-01-01 00:00:00 +0000
+++ addons/base_calendar/static/lib/jquery/jquery.tmpl.js	2011-04-15 12:10:15 +0000
@@ -0,0 +1,489 @@
+/*!
+ * jQuery Templates Plugin
+ * http://github.com/jquery/jquery-tmpl
+ *
+ * Copyright Software Freedom Conservancy, Inc.
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ */
+(function( jQuery, undefined ){
+	var oldManip = jQuery.fn.domManip, tmplItmAtt = "_tmplitem", htmlExpr = /^[^<]*(<[\w\W]+>)[^>]*$|\{\{\! /,
+		newTmplItems = {}, wrappedItems = {}, appendToTmplItems, topTmplItem = { key: 0, data: {} }, itemKey = 0, cloneIndex = 0, stack = [];
+
+	function newTmplItem( options, parentItem, fn, data ) {
+		// Returns a template item data structure for a new rendered instance of a template (a 'template item').
+		// The content field is a hierarchical array of strings and nested items (to be
+		// removed and replaced by nodes field of dom elements, once inserted in DOM).
+		var newItem = {
+			data: data || (parentItem ? parentItem.data : {}),
+			_wrap: parentItem ? parentItem._wrap : null,
+			tmpl: null,
+			parent: parentItem || null,
+			nodes: [],
+			calls: tiCalls,
+			nest: tiNest,
+			wrap: tiWrap,
+			html: tiHtml,
+			update: tiUpdate
+		};
+		if ( options ) {
+			jQuery.extend( newItem, options, { nodes: [], parent: parentItem } );
+		}
+		if ( fn ) {
+			// Build the hierarchical content to be used during insertion into DOM
+			newItem.tmpl = fn;
+			newItem._ctnt = newItem._ctnt || newItem.tmpl( jQuery, newItem );
+			newItem.key = ++itemKey;
+			// Keep track of new template item, until it is stored as jQuery Data on DOM element
+			(stack.length ? wrappedItems : newTmplItems)[itemKey] = newItem;
+		}
+		return newItem;
+	}
+
+	// Override appendTo etc., in order to provide support for targeting multiple elements. (This code would disappear if integrated in jquery core).
+	jQuery.each({
+		appendTo: "append",
+		prependTo: "prepend",
+		insertBefore: "before",
+		insertAfter: "after",
+		replaceAll: "replaceWith"
+	}, function( name, original ) {
+		jQuery.fn[ name ] = function( selector ) {
+			var ret = [], insert = jQuery( selector ), elems, i, l, tmplItems,
+				parent = this.length === 1 && this[0].parentNode;
+
+			appendToTmplItems = newTmplItems || {};
+			if ( parent && parent.nodeType === 11 && parent.childNodes.length === 1 && insert.length === 1 ) {
+				insert[ original ]( this[0] );
+				ret = this;
+			} else {
+				for ( i = 0, l = insert.length; i < l; i++ ) {
+					cloneIndex = i;
+					elems = (i > 0 ? this.clone(true) : this).get();
+					jQuery.fn[ original ].apply( jQuery(insert[i]), elems );
+					ret = ret.concat( elems );
+				}
+				cloneIndex = 0;
+				ret = this.pushStack( ret, name, insert.selector );
+			}
+			tmplItems = appendToTmplItems;
+			appendToTmplItems = null;
+			jQuery.tmpl.complete( tmplItems );
+			return ret;
+		};
+	});
+
+	jQuery.fn.extend({
+		// Use first wrapped element as template markup.
+		// Return wrapped set of template items, obtained by rendering template against data.
+		tmpl: function( data, options, parentItem ) {
+			return jQuery.tmpl( this[0], data, options, parentItem );
+		},
+
+		// Find which rendered template item the first wrapped DOM element belongs to
+		tmplItem: function() {
+			return jQuery.tmplItem( this[0] );
+		},
+
+		// Consider the first wrapped element as a template declaration, and get the compiled template or store it as a named template.
+		template: function( name ) {
+			return jQuery.template( name, this[0] );
+		},
+
+		domManip: function( args, table, callback, options ) {
+			// This appears to be a bug in the appendTo, etc. implementation
+			// it should be doing .call() instead of .apply(). See #6227
+			if ( args[0] && args[0].nodeType ) {
+				var dmArgs = jQuery.makeArray( arguments ), argsLength = args.length, i = 0, tmplItem;
+				while ( i < argsLength && !(tmplItem = jQuery.data( args[i++], "tmplItem" ))) {}
+				if ( argsLength > 1 ) {
+					dmArgs[0] = [jQuery.makeArray( args )];
+				}
+				if ( tmplItem && cloneIndex ) {
+					dmArgs[2] = function( fragClone ) {
+						// Handler called by oldManip when rendered template has been inserted into DOM.
+						jQuery.tmpl.afterManip( this, fragClone, callback );
+					};
+				}
+				oldManip.apply( this, dmArgs );
+			} else {
+				oldManip.apply( this, arguments );
+			}
+			cloneIndex = 0;
+			if ( !appendToTmplItems ) {
+				jQuery.tmpl.complete( newTmplItems );
+			}
+			return this;
+		}
+	});
+
+	jQuery.extend({
+		// Return wrapped set of template items, obtained by rendering template against data.
+		tmpl: function( tmpl, data, options, parentItem ) {
+			var ret, topLevel = !parentItem;
+			if ( topLevel ) {
+				// This is a top-level tmpl call (not from a nested template using {{tmpl}})
+				parentItem = topTmplItem;
+				tmpl = jQuery.template[tmpl] || jQuery.template( null, tmpl );
+				wrappedItems = {}; // Any wrapped items will be rebuilt, since this is top level
+			} else if ( !tmpl ) {
+				// The template item is already associated with DOM - this is a refresh.
+				// Re-evaluate rendered template for the parentItem
+				tmpl = parentItem.tmpl;
+				newTmplItems[parentItem.key] = parentItem;
+				parentItem.nodes = [];
+				if ( parentItem.wrapped ) {
+					updateWrapped( parentItem, parentItem.wrapped );
+				}
+				// Rebuild, without creating a new template item
+				return jQuery( build( parentItem, null, parentItem.tmpl( jQuery, parentItem ) ));
+			}
+			if ( !tmpl ) {
+				return []; // Could throw...
+			}
+			if ( typeof data === "function" ) {
+				data = data.call( parentItem || {} );
+			}
+			if ( options && options.wrapped ) {
+				updateWrapped( options, options.wrapped );
+			}
+			ret = jQuery.isArray( data ) ? 
+				jQuery.map( data, function( dataItem ) {
+					return dataItem ? newTmplItem( options, parentItem, tmpl, dataItem ) : null;
+				}) :
+				[ newTmplItem( options, parentItem, tmpl, data ) ];
+			return topLevel ? jQuery( build( parentItem, null, ret ) ) : ret;
+		},
+
+		// Return rendered template item for an element.
+		tmplItem: function( elem ) {
+			var tmplItem;
+			if ( elem instanceof jQuery ) {
+				elem = elem[0];
+			}
+			while ( elem && elem.nodeType === 1 && !(tmplItem = jQuery.data( elem, "tmplItem" )) && (elem = elem.parentNode) ) {}
+			return tmplItem || topTmplItem;
+		},
+
+		// Set:
+		// Use $.template( name, tmpl ) to cache a named template,
+		// where tmpl is a template string, a script element or a jQuery instance wrapping a script element, etc.
+		// Use $( "selector" ).template( name ) to provide access by name to a script block template declaration.
+
+		// Get:
+		// Use $.template( name ) to access a cached template.
+		// Also $( selectorToScriptBlock ).template(), or $.template( null, templateString )
+		// will return the compiled template, without adding a name reference.
+		// If templateString includes at least one HTML tag, $.template( templateString ) is equivalent
+		// to $.template( null, templateString )
+		template: function( name, tmpl ) {
+			if (tmpl) {
+				// Compile template and associate with name
+				if ( typeof tmpl === "string" ) {
+					// This is an HTML string being passed directly in.
+					tmpl = buildTmplFn( tmpl )
+				} else if ( tmpl instanceof jQuery ) {
+					tmpl = tmpl[0] || {};
+				}
+				if ( tmpl.nodeType ) {
+					// If this is a template block, use cached copy, or generate tmpl function and cache.
+					tmpl = jQuery.data( tmpl, "tmpl" ) || jQuery.data( tmpl, "tmpl", buildTmplFn( tmpl.innerHTML ));
+				}
+				return typeof name === "string" ? (jQuery.template[name] = tmpl) : tmpl;
+			}
+			// Return named compiled template
+			return name ? (typeof name !== "string" ? jQuery.template( null, name ): 
+				(jQuery.template[name] || 
+					// If not in map, treat as a selector. (If integrated with core, use quickExpr.exec) 
+					jQuery.template( null, htmlExpr.test( name ) ? name : jQuery( name )))) : null; 
+		},
+
+		encode: function( text ) {
+			// Do HTML encoding replacing < > & and ' and " by corresponding entities.
+			return ("" + text).split("<").join("&lt;").split(">").join("&gt;").split('"').join("&#34;").split("'").join("&#39;");
+		}
+	});
+
+	jQuery.extend( jQuery.tmpl, {
+		tag: {
+			"tmpl": {
+				_default: { $2: "null" },
+				open: "if($notnull_1){_=_.concat($item.nest($1,$2));}"
+				// tmpl target parameter can be of type function, so use $1, not $1a (so not auto detection of functions)
+				// This means that {{tmpl foo}} treats foo as a template (which IS a function). 
+				// Explicit parens can be used if foo is a function that returns a template: {{tmpl foo()}}.
+			},
+			"wrap": {
+				_default: { $2: "null" },
+				open: "$item.calls(_,$1,$2);_=[];",
+				close: "call=$item.calls();_=call._.concat($item.wrap(call,_));"
+			},
+			"each": {
+				_default: { $2: "$index, $value" },
+				open: "if($notnull_1){$.each($1a,function($2){with(this){",
+				close: "}});}"
+			},
+			"if": {
+				open: "if(($notnull_1) && $1a){",
+				close: "}"
+			},
+			"else": {
+				_default: { $1: "true" },
+				open: "}else if(($notnull_1) && $1a){"
+			},
+			"html": {
+				// Unecoded expression evaluation. 
+				open: "if($notnull_1){_.push($1a);}"
+			},
+			"=": {
+				// Encoded expression evaluation. Abbreviated form is ${}.
+				_default: { $1: "$data" },
+				open: "if($notnull_1){_.push($.encode($1a));}"
+			},
+			"!": {
+				// Comment tag. Skipped by parser
+				open: ""
+			}
+		},
+
+		// This stub can be overridden, e.g. in jquery.tmplPlus for providing rendered events
+		complete: function( items ) {
+			newTmplItems = {};
+		},
+
+		// Call this from code which overrides domManip, or equivalent
+		// Manage cloning/storing template items etc.
+		afterManip: function afterManip( elem, fragClone, callback ) {
+			// Provides cloned fragment ready for fixup prior to and after insertion into DOM
+			var content = fragClone.nodeType === 11 ?
+				jQuery.makeArray(fragClone.childNodes) :
+				fragClone.nodeType === 1 ? [fragClone] : [];
+
+			// Return fragment to original caller (e.g. append) for DOM insertion
+			callback.call( elem, fragClone );
+
+			// Fragment has been inserted:- Add inserted nodes to tmplItem data structure. Replace inserted element annotations by jQuery.data.
+			storeTmplItems( content );
+			cloneIndex++;
+		}
+	});
+
+	//========================== Private helper functions, used by code above ==========================
+
+	function build( tmplItem, nested, content ) {
+		// Convert hierarchical content into flat string array 
+		// and finally return array of fragments ready for DOM insertion
+		var frag, ret = content ? jQuery.map( content, function( item ) {
+			return (typeof item === "string") ? 
+				// Insert template item annotations, to be converted to jQuery.data( "tmplItem" ) when elems are inserted into DOM.
+				(tmplItem.key ? item.replace( /(<\w+)(?=[\s>])(?![^>]*_tmplitem)([^>]*)/g, "$1 " + tmplItmAtt + "=\"" + tmplItem.key + "\" $2" ) : item) :
+				// This is a child template item. Build nested template.
+				build( item, tmplItem, item._ctnt );
+		}) : 
+		// If content is not defined, insert tmplItem directly. Not a template item. May be a string, or a string array, e.g. from {{html $item.html()}}. 
+		tmplItem;
+		if ( nested ) {
+			return ret;
+		}
+
+		// top-level template
+		ret = ret.join("");
+
+		// Support templates which have initial or final text nodes, or consist only of text
+		// Also support HTML entities within the HTML markup.
+		ret.replace( /^\s*([^<\s][^<]*)?(<[\w\W]+>)([^>]*[^>\s])?\s*$/, function( all, before, middle, after) {
+			frag = jQuery( middle ).get();
+
+			storeTmplItems( frag );
+			if ( before ) {
+				frag = unencode( before ).concat(frag);
+			}
+			if ( after ) {
+				frag = frag.concat(unencode( after ));
+			}
+		});
+		return frag ? frag : unencode( ret );
+	}
+
+	function unencode( text ) {
+		// Use createElement, since createTextNode will not render HTML entities correctly
+		var el = document.createElement( "div" );
+		el.innerHTML = text;
+		return jQuery.makeArray(el.childNodes);
+	}
+
+	// Generate a reusable function that will serve to render a template against data
+	function buildTmplFn( markup ) {
+		return new Function("jQuery","$item",
+			"var $=jQuery,call,_=[],$data=$item.data;" +
+
+			// Introduce the data as local variables using with(){}
+			"with($data){_.push('" +
+
+			// Convert the template into pure JavaScript
+			jQuery.trim(markup)
+				.replace( /([\\'])/g, "\\$1" )
+				.replace( /[\r\t\n]/g, " " )
+				.replace( /\$\{([^\}]*)\}/g, "{{= $1}}" )
+				.replace( /\{\{(\/?)(\w+|.)(?:\(((?:[^\}]|\}(?!\}))*?)?\))?(?:\s+(.*?)?)?(\(((?:[^\}]|\}(?!\}))*?)\))?\s*\}\}/g,
+				function( all, slash, type, fnargs, target, parens, args ) {
+					var tag = jQuery.tmpl.tag[ type ], def, expr, exprAutoFnDetect;
+					if ( !tag ) {
+						throw "Template command not found: " + type;
+					}
+					def = tag._default || [];
+					if ( parens && !/\w$/.test(target)) {
+						target += parens;
+						parens = "";
+					}
+					if ( target ) {
+						target = unescape( target ); 
+						args = args ? ("," + unescape( args ) + ")") : (parens ? ")" : "");
+						// Support for target being things like a.toLowerCase();
+						// In that case don't call with template item as 'this' pointer. Just evaluate...
+						expr = parens ? (target.indexOf(".") > -1 ? target + parens : ("(" + target + ").call($item" + args)) : target;
+						exprAutoFnDetect = parens ? expr : "(typeof(" + target + ")==='function'?(" + target + ").call($item):(" + target + "))";
+					} else {
+						exprAutoFnDetect = expr = def.$1 || "null";
+					}
+					fnargs = unescape( fnargs );
+					return "');" + 
+						tag[ slash ? "close" : "open" ]
+							.split( "$notnull_1" ).join( target ? "typeof(" + target + ")!=='undefined' && (" + target + ")!=null" : "true" )
+							.split( "$1a" ).join( exprAutoFnDetect )
+							.split( "$1" ).join( expr )
+							.split( "$2" ).join( fnargs ?
+								fnargs.replace( /\s*([^\(]+)\s*(\((.*?)\))?/g, function( all, name, parens, params ) {
+									params = params ? ("," + params + ")") : (parens ? ")" : "");
+									return params ? ("(" + name + ").call($item" + params) : all;
+								})
+								: (def.$2||"")
+							) +
+						"_.push('";
+				}) +
+			"');}return _;"
+		);
+	}
+	function updateWrapped( options, wrapped ) {
+		// Build the wrapped content. 
+		options._wrap = build( options, true, 
+			// Suport imperative scenario in which options.wrapped can be set to a selector or an HTML string.
+			jQuery.isArray( wrapped ) ? wrapped : [htmlExpr.test( wrapped ) ? wrapped : jQuery( wrapped ).html()]
+		).join("");
+	}
+
+	function unescape( args ) {
+		return args ? args.replace( /\\'/g, "'").replace(/\\\\/g, "\\" ) : null;
+	}
+	function outerHtml( elem ) {
+		var div = document.createElement("div");
+		div.appendChild( elem.cloneNode(true) );
+		return div.innerHTML;
+	}
+
+	// Store template items in jQuery.data(), ensuring a unique tmplItem data data structure for each rendered template instance.
+	function storeTmplItems( content ) {
+		var keySuffix = "_" + cloneIndex, elem, elems, newClonedItems = {}, i, l, m;
+		for ( i = 0, l = content.length; i < l; i++ ) {
+			if ( (elem = content[i]).nodeType !== 1 ) {
+				continue;
+			}
+			elems = elem.getElementsByTagName("*");
+			for ( m = elems.length - 1; m >= 0; m-- ) {
+				processItemKey( elems[m] );
+			}
+			processItemKey( elem );
+		}
+		function processItemKey( el ) {
+			var pntKey, pntNode = el, pntItem, tmplItem, key;
+			// Ensure that each rendered template inserted into the DOM has its own template item,
+			if ( (key = el.getAttribute( tmplItmAtt ))) {
+				while ( pntNode.parentNode && (pntNode = pntNode.parentNode).nodeType === 1 && !(pntKey = pntNode.getAttribute( tmplItmAtt ))) { }
+				if ( pntKey !== key ) {
+					// The next ancestor with a _tmplitem expando is on a different key than this one.
+					// So this is a top-level element within this template item
+					// Set pntNode to the key of the parentNode, or to 0 if pntNode.parentNode is null, or pntNode is a fragment.
+					pntNode = pntNode.parentNode ? (pntNode.nodeType === 11 ? 0 : (pntNode.getAttribute( tmplItmAtt ) || 0)) : 0;
+					if ( !(tmplItem = newTmplItems[key]) ) {
+						// The item is for wrapped content, and was copied from the temporary parent wrappedItem.
+						tmplItem = wrappedItems[key];
+						tmplItem = newTmplItem( tmplItem, newTmplItems[pntNode]||wrappedItems[pntNode], null, true );
+						tmplItem.key = ++itemKey;
+						newTmplItems[itemKey] = tmplItem;
+					}
+					if ( cloneIndex ) {
+						cloneTmplItem( key );
+					}
+				}
+				el.removeAttribute( tmplItmAtt );
+			} else if ( cloneIndex && (tmplItem = jQuery.data( el, "tmplItem" )) ) {
+				// This was a rendered element, cloned during append or appendTo etc.
+				// TmplItem stored in jQuery data has already been cloned in cloneCopyEvent. We must replace it with a fresh cloned tmplItem.
+				cloneTmplItem( tmplItem.key );
+				newTmplItems[tmplItem.key] = tmplItem;
+				pntNode = jQuery.data( el.parentNode, "tmplItem" );
+				pntNode = pntNode ? pntNode.key : 0;
+			}
+			if ( tmplItem ) {
+				pntItem = tmplItem;
+				// Find the template item of the parent element. 
+				// (Using !=, not !==, since pntItem.key is number, and pntNode may be a string)
+				while ( pntItem && pntItem.key != pntNode ) { 
+					// Add this element as a top-level node for this rendered template item, as well as for any
+					// ancestor items between this item and the item of its parent element
+					pntItem.nodes.push( el );
+					pntItem = pntItem.parent;
+				}
+				// Delete content built during rendering - reduce API surface area and memory use, and avoid exposing of stale data after rendering...
+				delete tmplItem._ctnt;
+				delete tmplItem._wrap;
+				// Store template item as jQuery data on the element
+				jQuery.data( el, "tmplItem", tmplItem );
+			}
+			function cloneTmplItem( key ) {
+				key = key + keySuffix;
+				tmplItem = newClonedItems[key] = 
+					(newClonedItems[key] || newTmplItem( tmplItem, newTmplItems[tmplItem.parent.key + keySuffix] || tmplItem.parent, null, true ));
+			}
+		}
+	}
+
+	//---- Helper functions for template item ----
+
+	function tiCalls( content, tmpl, data, options ) {
+		if ( !content ) {
+			return stack.pop();
+		}
+		stack.push({ _: content, tmpl: tmpl, item:this, data: data, options: options });
+	}
+
+	function tiNest( tmpl, data, options ) {
+		// nested template, using {{tmpl}} tag
+		return jQuery.tmpl( jQuery.template( tmpl ), data, options, this );
+	}
+
+	function tiWrap( call, wrapped ) {
+		// nested template, using {{wrap}} tag
+		var options = call.options || {};
+		options.wrapped = wrapped;
+		// Apply the template, which may incorporate wrapped content, 
+		return jQuery.tmpl( jQuery.template( call.tmpl ), call.data, options, call.item );
+	}
+
+	function tiHtml( filter, textOnly ) {
+		var wrapped = this._wrap;
+		return jQuery.map(
+			jQuery( jQuery.isArray( wrapped ) ? wrapped.join("") : wrapped ).filter( filter || "*" ),
+			function(e) {
+				return textOnly ?
+					e.innerText || e.textContent :
+					e.outerHTML || outerHtml(e);
+			});
+	}
+
+	function tiUpdate() {
+		var coll = this.nodes;
+		jQuery.tmpl( null, null, null, this).insertBefore( coll[0] );
+		jQuery( coll ).remove();
+	}
+})( jQuery );
\ No newline at end of file

=== modified file 'addons/base_calendar/static/src/js/calendar.js'
--- addons/base_calendar/static/src/js/calendar.js	2011-04-13 13:32:07 +0000
+++ addons/base_calendar/static/src/js/calendar.js	2011-04-15 12:10:15 +0000
@@ -11,14 +11,13 @@
 		this._super(session, element_id);
 		this.view_manager = view_manager;
         this.dataset = dataset;
-        this.dataset_index = 0;
         this.model = dataset.model;
         this.view_id = view_id;
         this.fields_view = {};
         this.widgets = {};
         this.widgets_counter = 0;
         this.fields = this.dataset.fields ? this.dataset.fields: {};
-        this.datarecord = {};
+        this.ids = this.dataset.ids;
 		this.name = "";
 		this.date_start = "";
 		this.date_delay = "";
@@ -35,23 +34,22 @@
 	start: function() {
 		this.rpc("/base_calendar/calendarview/load", {"model": this.model, "view_id": this.view_id}, this.on_loaded);
 	},
-	on_loaded: function(result) {
-		var self = this;
-		var params = {};
-		this.fields_view = result.fields_view;
-		this.name = this.fields_view.name || this.fields_view.arch.attrs.string;
+    on_loaded: function(data) {
+        
+        this.fields_view = data.fields_view;
+        this.name = this.fields_view.name || this.fields_view.arch.attrs.string;
 		this.view_id = this.fields_view.view_id;
-		
-		this.date_start = this.fields_view.arch.attrs.date_start;
+        
+        this.date_start = this.fields_view.arch.attrs.date_start;
 		this.date_delay = this.fields_view.arch.attrs.date_delay;
 		this.date_stop = this.fields_view.arch.attrs.date_stop;
-		
-		this.colors = this.fields_view.arch.attrs.colors;
+        
+        this.colors = this.fields_view.arch.attrs.colors;
 		this.day_length = this.fields_view.arch.attrs.day_length || 8;
 		this.color_field = this.fields_view.arch.attrs.color;
 		this.fields =  this.fields_view.fields;
-		
-		//* Calendar Fields *
+        
+        //* Calendar Fields *
 		this.calendar_fields['date_start'] = {'name': this.date_start, 'kind': this.fields[this.date_start]['type']};
 		
 		if(this.date_delay)
@@ -59,121 +57,200 @@
 			
 		if(this.date_stop)
 			this.calendar_fields['date_stop'] = {'name': this.date_stop, 'kind': this.fields[this.date_stop]['type']};
-			
-		this.calendar_fields['day_length'] = this.day_length;
+		
 		//* ------- *
-		
-		for(var fld=0;fld<this.fields_view.arch.children.length;fld++) {
+        
+        for(var fld=0;fld<this.fields_view.arch.children.length;fld++) {
 			this.info_fields.push(this.fields_view.arch.children[fld].attrs.name);
 		}
-		
-		
-		this.load_scheduler();
-	},
+      
+        this.load_scheduler();
+    },
 	
-	load_scheduler:function() {
-		var self = this;
-		
-		var params = {};
-		
-		params['model'] = this.model;
-		params['calendar_fields'] = this.calendar_fields;
-		params['info_fields'] = this.info_fields;
-		params['fields'] = this.fields;
-		params['color_field'] = this.color_field;
-		params['domain'] = this.domain;
-		params['colors'] =  this.colors;
-		
-		/*
-		 * Start dhtmlx Schedular
+    load_scheduler: function() {
+        var self = this;
+        this.dataset.read_ids(
+            this.ids, 
+            this.fields,
+            function(events){
+                if (self.session.locale_code) {
+                    
+                    $LAB.setOptions({AlwaysPreserveOrder: true})
+                    .script([
+                        '/base_calendar/static/lib/dhtmlxScheduler/sources/locale_'+self.session.locale_code+'.js',
+                        '/base_calendar/static/lib/dhtmlxScheduler/sources/locale_recurring_'+self.session.locale_code+'.js'
+                    ])
+                    .wait(function() {
+                        self.schedule_events(events);        
+                    });
+                    
+                } else {
+                    self.schedule_events(events);
+                }
+            });
+    },
+    
+    convert_date_format: function(date, date_type, kind) {
+        var _date = $.date(date, 'yyyy-MM-dd').date();
+        if(kind == 'date') {
+            if(date_type == 'date_start') {
+               _date.setHours(9);
+               _date.setMinutes(00);
+            } else if(date_type == 'date_stop') {
+                _date.setHours(17);
+               _date.setMinutes(00);
+            }
+        }
+        var new_date = _date.getFullYear() + '-' + (_date.getMonth() + 1) + '-' + _date.getDate() + ' '+ _date.getHours() + ':' + _date.getMinutes() + ':'+ _date.getSeconds();
+        return new_date;
+    },
+    
+    schedule_events: function(events) {
+        this.$element.html(QWeb.render("CalendarView", {"fields_view": this.fields_view}));
+        
+        /*
+		 * Initialize dhtmlx Schedular
 		 */
-		scheduler.clearAll();
-		scheduler.config.xml_date="%Y-%m-%d %H:%i";
-		scheduler.config.multi_day = true; //Multi day events are not rendered in daily and weekly views
-		
-		this.rpc(
-			'/base_calendar/calendarview/schedule_events',
-			params,
-			function(result) {
-				self.schedule_events(result);
-			}
-		)
-	},
-	
-	schedule_events: function(result) {
-		var self = this;
-		var res = result.result;
-		var sidebar = result.sidebar;
-		this.$element.html(QWeb.render("CalendarView", {"view": this, "fields_view": this.fields_view, "sidebar":sidebar, "calendar":this}));
-		
-		// Initialize Sceduler
+        
+        scheduler.clearAll();
+        
+        if(this.fields[this.date_start]['type'] == 'time') {
+            scheduler.config.xml_date="%H:%M:%S";
+        } else {
+            scheduler.config.xml_date="%Y-%m-%d %H:%M:%S";
+        }
+        
+        scheduler.config.multi_day = true; //Multi day events are not rendered in daily and weekly views
+        
+        // Initialize Sceduler
 		scheduler.init('openerp_scheduler',null,"month");
-		scheduler.parse(res,"json");
-		jQuery('#dhx_minical_icon').bind('click', this.mini_calendar);
-		
-		
-		// To Change Event
-		scheduler.attachEvent(
-					'onEventChanged'
-					,function(event_id, event_object) {
-						self.edit_event(event_id, event_object)
-					});
-		
-		/*
-		 * Create Sidebar
-		 */
-		jQuery('#calendar-sidebar').append(
-			jQuery('<table>',{'width':'100%','cellspacing': 0, 'cellpadding': 0, 'id':'cal-sidebar-option'})
-		)
-		for(s in sidebar) {
-			jQuery('#cal-sidebar-option').append(
-				jQuery('<tr>').append(
-					jQuery('<td>').append(
-						jQuery('<div>')
-						.append(
-							jQuery('<input>',
-							{
-								'type': 'checkbox', 
-								'id':sidebar[s][0],
-								'value':sidebar[s][0]
-							}).bind('click',function(){
-								self.reload_scheduler()
-							}),
-							sidebar[s][1]
-						)
-						.css('background-color',sidebar[s][sidebar[s].length-1])
-					)
-				)
-			)
-		}
-	},
-	
-	convert_date_format: function(start_date, end_date) {
-		var params = {};
-		params['start_date'] = start_date.getFullYear() +'-' + start_date.getMonth()+'-' + start_date.getDate()+' '+start_date.getHours()+':'+start_date.getMinutes()+':'+start_date.getSeconds();
-		if(end_date) {
-			params['end_date'] = end_date.getFullYear() +'-' + end_date.getMonth()+'-' + end_date.getDate()+' '+end_date.getHours()+':'+end_date.getMinutes()+':'+end_date.getSeconds();
-		}
-		return params; 
-	},
-	
-	edit_event: function(evt_id, evt_object) {
-		var dates = this.convert_date_format(evt_object.start_date, evt_object.end_date);
-		this.rpc(
-			'/base_calendar/calendarview/edit_events',
-			{
-				'start_date': dates.start_date,
-				'end_date': dates.end_date,
-				'id': evt_id,
-				'model': this.model,
-				'info_fields': this.info_fields,
-				'fields': this.fields,
-				'calendar_fields': this.calendar_fields
-			}
-		);
-	},
-	
-	mini_calendar: function() {
+        
+        //To parse Events we have to convert date Format
+        
+        var res_events = [];
+        for(evt in events) {
+            if(!events[evt][this.date_start]) {
+                this.notification.warn("Start date is not defined for event :", events[evt]['id']);
+                break;
+            }
+            if (this.fields[this.date_start]['type'] == 'date') {
+                events[evt][this.date_start] = this.convert_date_format(events[evt][this.date_start], 'date_start', this.fields[this.date_start]['type'])
+                if (this.date_stop) {
+                    events[evt][this.date_stop] = this.convert_date_format(events[evt][this.date_stop], 'date_stop', this.fields[this.date_stop]['type'])
+                }
+            }
+            res_events.push(this.convert_event(events[evt]))
+        }
+        scheduler.parse(res_events,"json");
+        jQuery('#dhx_minical_icon').bind('click', this.mini_calendar);
+        
+        // Event Options Click,edit
+        var self = this;
+        scheduler.attachEvent(
+            "onDblClick",
+            function(event_id, event_object) {
+                self.popup_event(event_id);
+            }
+        );
+        
+        scheduler.attachEvent(
+            "onEventCreated",
+            function(event_id, event_object) {
+                //Replace default Lightbox with Popup Form of new Event
+                
+                scheduler.showLightbox = function(){
+                    //Delete Newly created Event,Later we reload Scheduler
+                    scheduler.deleteEvent(event_id)
+                    self.popup_event();
+                }
+                
+            }
+        );
+    },
+    
+    convert_event: function(event) {
+        var res_text = '';
+        var res_description = [];
+        var start = event[this.date_start];
+        var end = event[this.date_delay] || 1;
+        var span = 0;
+        if(this.info_fields) {
+            var fld = event[this.info_fields[0]];
+            if(typeof fld == 'object') {
+                
+                res_text = fld[fld.length -1];
+            } else {
+                res_text = fld
+            }
+            var sliced_info_fields = this.info_fields.slice(1);
+            for(sl_fld in sliced_info_fields) {
+                var slc_fld = event[sliced_info_fields[sl_fld]];
+                if(typeof slc_fld == 'object') {
+                  res_description.push(slc_fld[slc_fld.length - 1])  
+                } else {
+                    if(slc_fld) {
+                        res_description.push(slc_fld);
+                    }
+                }
+            }
+        }
+        if(start && end){
+            var n = 0;
+            var h = end;
+            if (end == this.day_length) {
+                span = 1
+            } else if(end > this.day_length) {
+                n = end / this.day_length;
+                h = end % this.day_length;
+                n = parseInt(Math.floor(n));
+                
+                if(h > 0)
+                    span = n + 1
+                else
+                    span = n
+            }
+            var n_date = $.date(start, 'yyyy-MM-dd HH:mm:ss').date();
+            end = n_date.getFullYear() + '-' + (n_date.getMonth() + 1) + '-' + n_date.getDate() + ' '+ (n_date.getHours() + 1) + ':' + n_date.getMinutes() + ':'+ n_date.getSeconds();   
+        }
+        
+        if(start && this.date_stop) {
+            var tds = $.date(start, 'yyyy-MM-dd HH:mm:ss').date();
+            var tde = ends = $.date(event[this.date_stop], 'yyyy-MM-dd HH:mm:ss').date();
+            if(event[this.date_stop] == undefined) {
+                if(tds) {
+//                    end = (tds.getTime() + 60 * 60)
+                }
+            }
+            
+            if(tds && tde) {
+//                tds = tds.getTime();
+//                tde = tde.getTime();
+                //time.mktime equivalent
+                tds = (tds.getTime() / 1e3 >> 0) - (tds.getTime() < 0);
+                tde = (tde.getTime() / 1e3 >> 0) - (tde.getTime() < 0);
+                
+            }
+            if(tds >= tde) {
+                 tde = tds + 60 * 60;
+            }
+            
+            n = (tde - tds) / (60 * 60);
+            if (n >= this.day_length) {
+                span = Math.ceil(n / 24);
+            }
+            end = ends.getFullYear() + '-' + (ends.getMonth() + 1) + '-' + ends.getDate() + ' '+ (ends.getHours() + 1) + ':' + ends.getMinutes() + ':'+ ends.getSeconds();
+        }
+        
+        return {
+            'start_date': start,
+            'end_date': end,
+            'text': res_text,
+            'id': event['id'],
+            'title': res_description.join()
+            }
+    },
+    
+    mini_calendar: function() {
 		
 		if(scheduler.isCalendarVisible()) {
 			scheduler.destroyCalendar();
@@ -189,11 +266,22 @@
 			});
 		}
 	},
-	
-	reload_scheduler: function() {
-//		self.color_field
-		console.log('Reload Scheduler>>>')
-	},
+    
+    do_search: function(domains, contexts, groupbys) {
+        var self = this;
+        this.rpc('/base/session/eval_domain_and_context', {
+            domains: domains,
+            contexts: contexts,
+            group_by_seq: groupbys
+        }, function (results) {
+            // TODO: handle non-empty results.group_by with read_group
+            self.dataset.context = self.context = results.context;
+            self.dataset.domain = self.domain = results.domain;
+            self.dataset.read_slice(self.fields, 0, self.limit,function(events){
+                self.schedule_events(events)
+            });
+        });
+    },
 	
 	do_show: function () {
         this.$element.show();
@@ -201,23 +289,33 @@
 	
     do_hide: function () {
         this.$element.hide();
+    },
+    
+    popup_event: function(event_id) {
+        if (event_id) {
+            event_id = parseInt(event_id, 10);
+            var dataset_event_index = jQuery.inArray(event_id, this.ids);
+        } else  {
+            var dataset_event_index = null;
+        }
+        this.dataset.index = dataset_event_index;
+        var element_id = _.uniqueId("act_window_dialog");
+        var dialog = jQuery('<div>', 
+                        {'id': element_id
+                    }).dialog({
+                        title: 'Scheduler',
+                        modal: true,
+                        buttons: {
+                            Cancel: function() {
+                                $(this).dialog("close");
+                            }
+                        }
+                    });
+        var event_form = new openerp.base.FormView(this.view_manager, this.session, element_id, this.dataset, false);
+        event_form.start();
     }
 });
 
-//openerp.base.Action = openerp.base.Action.extend({
-//    do_action_window: function(action) {
-//        this._super.apply(this,arguments);
-//        for(var i = 0; i < action.views.length; i++)  {
-//            if(action.views[i][1] == "calendar") {
-//                this.calendar_id = action.views[i][0];
-//                break;
-//            }
-//        }
-//        // IF there is a view calender
-//        // if(this.calendar_id
-//    },
-//});
-
 };
 
 // DEBUG_RPC:rpc.request:('execute', 'addons-dsh-l10n_us', 1, '*', ('ir.filters', 'get_filters', u'res.partner'))

=== modified file 'addons/base_calendar/static/src/xml/base_calendar.xml'
--- addons/base_calendar/static/src/xml/base_calendar.xml	2011-04-13 12:50:42 +0000
+++ addons/base_calendar/static/src/xml/base_calendar.xml	2011-04-15 12:10:15 +0000
@@ -1,15 +1,15 @@
 <template>
   <t t-name="CalendarView">
-    <h3 class="title"><t t-esc="view.fields_view.arch.attrs.string"/></h3>
+    <h3 class="title"><t t-esc="fields_view.arch.attrs.string"/></h3>
     <table class="calendar-view" width="100%" height="100%" cellspacing="0" cellpadding="0">
       <tr>
-        <td>
+        <!--<td>
           <div style="height: 1000px;width: 100%;">
             <div id="calendar-sidebar">
             </div>
           </div>
-        </td>
-        <td style="width:85%;" align="left">
+        </td>-->
+        <td style="width:100%;" align="left">
           <div id="openerp_scheduler" class="dhx_cal_container" style="height: 1000px;width: 100%;">
             <div class="dhx_cal_navline">
               <div class="dhx_cal_prev_button"/>


Follow ups