can3p (can3p) wrote in changelog,
can3p
can3p
changelog

[livejournal] r17960: LJSUP-7240: Controlstrip. Calendar now u...

Committer: dpetrov
LJSUP-7240: Controlstrip. Calendar now uses plugin from jquery_fn.js and shows only months with entries.h
U   trunk/htdocs/js/controlstrip.js
U   trunk/htdocs/js/jquery_fn.js
Modified: trunk/htdocs/js/controlstrip.js
===================================================================
--- trunk/htdocs/js/controlstrip.js	2010-12-17 07:46:57 UTC (rev 17959)
+++ trunk/htdocs/js/controlstrip.js	2010-12-17 08:25:51 UTC (rev 17960)
@@ -64,17 +64,8 @@
 };
 
 ControlStrip.Calendar = function( o ) {
+	o = o || {};
 
-	var options = {
-		monthNames: [ "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" ],
-		dayRef: "/%Y/%M/%D",
-		currentDate: new Date(),
-		startAtSunday: true
-	},
-	$ = jQuery;
-
-	o = $.extend( {}, options, o);
-
 	if( ControlStrip.Calendar.MonthNames ) {
 		o.monthNames = ControlStrip.Calendar.MonthNames;
 	}
@@ -83,321 +74,17 @@
 		o.startAtSunday = ControlStrip.Calendar.StartAtSunday;
 	}
 
-	function getDaysInMonth( date ) {
-		var monthArr = [ 31, (isLeapYear(date.getFullYear())?29:28), 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 ];
-		return monthArr[ date.getMonth() ];
+	var onFetch = function( onload ) {
+		jQuery.getJSON( LiveJournal.getAjaxUrl('get_posting_days'),
+			{ journal: Site.currentJournal }, onload );
 	}
 
-	function isLeapYear(year){
-		return ( year % 4 === 0 && (year % 100 != 0 ) ) || ( year % 400 == 0);
-	}
-
-	var View = function (nodes, styles, controller)
-	{
-		this.activeLabelsEvents = {}
-
-		this.initialize = function (monthDate, events, switcherStates)
-		{
-			this.tbody = this.catchTableStructure();
-
-			//this.fillDates(monthDate, events);
-			//this.fillLabels(monthDate, switcherStates)
-			this.switcherStates = switcherStates;
-			this.bindEvents();
-		}
-
-		this.modelChanged = function (monthDate, events, switcherStates)
-		{
-			//we have a 30% speedup when we temporary remove tbody from dom
-			this.tbody.remove();
-			this.fillDates(monthDate, events)
-			this.fillLabels(monthDate, switcherStates)
-			this.tbody.appendTo( nodes.table );
-		}
-
-		this.catchTableStructure = function() {
-			var tbody = nodes.table[0].tBodies[0];
-			nodes.lastRow = $( tbody.rows[ tbody.rows.length - 1 ] );
-			nodes.daysCells = [];
-
-			for( row = 0, rowsCount = tbody.rows.length; row < rowsCount; ++row ) {
-				for( cell = 0, cellsCount = tbody.rows[ row ].cells.length; cell < cellsCount; ++cell ) {
-					nodes.daysCells.push( $( tbody.rows[ row ].cells[ cell ] ) );
-				}
-			}
-
-			return $( tbody );
-		}
-
-		this.fillDates = function (monthDate, events)
-		{
-			var firstDay = new Date(monthDate);
-			firstDay.setDate(1);
-
-			var offset;
-			if( o.startAtSunday ) {
-				offset = firstDay.getDay() - 1;
-			} else {
-				offset = (firstDay.getDay() == 0 ? 5: firstDay.getDay() - 2);
-			}
-
-			var length = getDaysInMonth( monthDate );
-
-			var prevMonth = new Date(monthDate);
-			prevMonth.setMonth(monthDate.getMonth() - 1);
-			var prLength = getDaysInMonth( prevMonth );
-
-			var nextMonth = new Date(monthDate)
-			nextMonth.setMonth(monthDate.getMonth() + 1)
-
-			for (var h = offset, l = prLength; h >= 0; h--)
-			{	// head
-				var iDate = new Date(prevMonth);
-				iDate.setDate(l--);
-
-				this.formDayString( iDate, nodes.daysCells[ h ]);
-			}
-
-			for (var i = 1; i <= length; i++)
-			{	// body
-				var iDate = new Date(monthDate);
-				iDate.setDate(i);
-
-				this.formDayString( iDate, nodes.daysCells[ i + offset ], true, events );
-			}
-
-			for (var j = i + offset, k = 1; j < nodes.daysCells.length; j++)
-			{	// tail
-				var iDate = new Date(nextMonth);
-				iDate.setDate(k);
-
-				this.formDayString( iDate, nodes.daysCells[j] );
-				++k;
-			}
-
-			this.toggleExtraWeek(monthDate.getDay() > 5);
-		}
-
-		this.toggleExtraWeek  = function (show)
-		{
-			nodes.lastRow[show ? 'show' : 'hide']();
-		}
-
-		this.formDayString = function( d, label, isActive, events )
-		{
-			events = events || [];
-			isActive = isActive || false;
-
-			function getDateNumber( d ) {
-				var day = d.getDate().toString();
-				if( day.length == 1 ) { day = "0" + day; }
-
-				var month = d.getMonth().toString();
-				if( month.length == 1 ) { month = "0" + month; }
-
-				return parseInt( d.getFullYear().toString() + month + day, 10)
-			}
-
-			var today = new Date(),
-				isInFuture = ( getDateNumber( d ) > getDateNumber( today ) ),
-				isCurrentDay = ( getDateNumber( d ) == getDateNumber( o.currentDate ) ),
-				hasEvents = ( $.inArray( d.getDate(), events ) > -1 );
-
-			label[isCurrentDay ? 'addClass' : 'removeClass']( styles.current );
-
-			if( !isActive || isInFuture ) {
-				label.addClass( styles.inactive ).html(d.getDate());
-			} else if( hasEvents ) {
-
-				var ref = o.dayRef,
-					subs = [ [ '%Y', d.getFullYear()], [ '%M', d.getMonth() + 1], [ '%D', d.getDate()] ];
-				for( var i = 0; i < subs.length; ++i ) {
-					ref = ref.replace( subs[i][0], subs[i][1] );
-				}
-
-				label.removeClass( styles.inactive ).html( $("<a>")
-						.html( d.getDate() )
-						.attr( 'href', ref ) );
-			} else {
-				label.removeClass( styles.inactive ).html(d.getDate());
-			}
-		}
-
-		this.bindEvents = function ()
-		{
-			var that = this;
-			for (var sws in this.switcherStates) {
-				nodes[sws].mousedown( function (item) { return function ( ev ) {
-					if(that.switcherStates[item]) {
-						that.controller[item]();
-					}
-
-					ev.preventDefault();
-				} }(sws));
-			}
-		}
-
-		this.fillLabels = function (monthDate, switcherStates)
-		{
-			this.switcherStates = switcherStates
-			for (var sws in switcherStates)
-			{
-				if(!switcherStates[sws]) {
-					nodes[sws].addClass(this.disabledStyle(sws));
-				}
-				else {
-					nodes[sws].removeClass(this.disabledStyle(sws));
-				}
-			}
-			nodes.monthLabel.html( o.monthNames[ monthDate.getMonth() ] );
-			nodes.yearLabel.html ( monthDate.getFullYear() );
-		}
-
-		this.disabledStyle = function (sws)
-		{
-			if(sws == 'prevMonth' || sws == 'prevYear') return styles.prevDisabled;
-			else return styles.nextDisabled;
-		}
-	}
-
-	var Controller = function (view, model)
-	{
-		this.prevMonth = function () { model.switchMonth(-1); };
-		this.nextMonth = function () { model.switchMonth( 1); };
-
-		this.prevYear  = function () { model.switchYear(-1); };
-		this.nextYear  = function () { model.switchYear( 1); };
-
-		view.controller = this;
-		model.initialize();
-	}
-
-	var Model = function ( selectedDay, view)
-	{
-		this.enabledMonthsRange = [];
-		this.datesCache = {};
-		//if ajax request is already sent, we do not change the model before the answer
-		this.ajaxPending = false;
-
-		this.initialize = function ()
-		{
-			var startDate = new Date();
-			startDate.setFullYear( 1999, 3, 15 );
-			this.enabledMonthsRange = [ startDate, new Date()];
-			this.monthDate = new Date( selectedDay );
-			view.initialize(this.monthDate, null, this.getSwitcherStates(this.monthDate));
-			this.switchMonth( 0 );
-		}
-
-		this.switchMonth = function (go)
-		{
-			this.monthDate.setMonth(this.monthDate.getMonth() + go);
-			var self = this;
-			this.fetchMonthEvents( this.monthDate, function( events ) {
-				view.modelChanged(self.monthDate, events, self.getSwitcherStates( self.monthDate ) );
-			} )
-		}
-
-		this.switchYear = function (go)
-		{
-			this.monthDate.setFullYear(this.monthDate.getFullYear() + go, this.monthDate.getMonth(), this.monthDate.getDate());
-			while( !this.insideRange( this.enabledMonthsRange, this.monthDate ) ) {
-				this.monthDate.setMonth( this.monthDate.getMonth() - 1 );
-			}
-			var self = this;
-			this.fetchMonthEvents( this.monthDate, function( events ) {
-				view.modelChanged(self.monthDate, events, self.getSwitcherStates( self.monthDate ) );
-			} )
-		}
-
-		//now we generate random data, because endpoint does not exist. FIXIT
-		this.fetchMonthEvents = function( date, onFetch ) {
-			if( this.ajaxPending ) {
-				return;
-			}
-			this.ajaxPending = true;
-			var hash = date.getFullYear() + "-",
-				month = (date.getMonth() + 1).toString();
-
-			hash += ( (month.length == 1) ? "0" : "" ) + month;
-
-			if( hash in this.datesCache ) {
-				this.ajaxPending = false;
-				onFetch( this.datesCache[ hash ].slice( 0 ) );
-			} else {
-				var self = this;
-				//here we emulate ajax request
-				setTimeout( function() {
-					var daysNum = date.getMonth(),
-						result = [];
-
-					for( var i = 1; i <= daysNum; ++i ) {
-						if( Math.random() > 0.5 ) {
-							(function( day ) {
-								result.push( day );
-							}(i));
-						}
-					}
-
-					self.datesCache[ hash ] = result.slice( 0 );
-					self.ajaxPending = false;
-					onFetch( result.slice( 0 ) );
-				}, 0 );
-			}
-		}
-
-		this.getSwitcherStates = function (monthDate)
-		{
-			var prevMonth = new Date(monthDate);
-			prevMonth.setMonth(prevMonth.getMonth() - 1);
-			var pm = this.insideRange(this.enabledMonthsRange, prevMonth);
-
-			var nextMonth = new Date(monthDate)
-			nextMonth.setMonth(nextMonth.getMonth() + 1);
-			var nm = this.insideRange(this.enabledMonthsRange, nextMonth);
-
-			var prevYear = new Date(monthDate);
-			prevYear.setFullYear(monthDate.getFullYear() - 1, monthDate.getMonth(), monthDate.getDate());
-			var py = this.insideRange(this.enabledMonthsRange, prevYear);
-
-			var nextYear = new Date(monthDate);
-			nextYear.setFullYear(monthDate.getFullYear() + 1, 0, 1 );
-			var ny = this.insideRange(this.enabledMonthsRange, nextYear);
-
-			return { prevMonth: pm, nextMonth: nm, prevYear: py, nextYear: ny };
-		}
-
-		this.insideRange = function (range, iDate) { return iDate >= range[0] && iDate <= range[1] };
-	}
-
-	var nodes =
-	{
-		container: this,
-		table: this.find('table'),
-
-		prevMonth: this.find('.cal-nav-month .cal-nav-prev'),
-		nextMonth: this.find('.cal-nav-month .cal-nav-next'),
-		prevYear:  this.find('.cal-nav-year .cal-nav-prev'),
-		nextYear:  this.find('.cal-nav-year .cal-nav-next'),
-
-		monthLabel: this.find('.cal-nav-month .cal-month'),
-		yearLabel: this.find('.cal-nav-year .cal-year')
-	}
-
-	var styles =
-	{
-		inactive : 'other',
-		hasEvents: '',
-		current  : 'current',
-		future: 'other',
-
-		nextDisabled : 'cal-nav-next-dis',
-		prevDisabled : 'cal-nav-prev-dis'
-	}
-
-	var view = new View(nodes, styles);
-	var model = new Model( o.currentDate, view);
-	var controller = new Controller(view, model);
+	this.calendar( {
+		onFetch: onFetch,
+		activeUntil: new Date(),
+		startMonth: new Date( 1999, 3, 1 ),
+		endMonth: new Date()
+	} );
 };
 
 }( jQuery, window ));

Modified: trunk/htdocs/js/jquery_fn.js
===================================================================
--- trunk/htdocs/js/jquery_fn.js	2010-12-17 07:46:57 UTC (rev 17959)
+++ trunk/htdocs/js/jquery_fn.js	2010-12-17 08:25:51 UTC (rev 17960)
@@ -193,7 +193,7 @@
 		}
 	});
 
-    return this;
+	return this;
 }
 
 /** jQuery overlay plugin
@@ -264,7 +264,6 @@
 		}
 	});
 }
-
 jQuery.fn.calendar = function( o ) {
 	//global variables for all instances
 	var defaultOptions = {
@@ -315,9 +314,13 @@
 		var controller = new Controller(view, model, options);
 	}
 
-	function getDateNumber( d ) {
+	function getDateNumber( d, dropDays ) {
+		dropDays = dropDays || false;
 		var day = d.getDate().toString();
 		if( day.length == 1 ) { day = "0" + day; }
+		if( dropDays ) {
+			day = "";
+		}
 
 		var month = d.getMonth().toString();
 		if( month.length == 1 ) { month = "0" + month; }
@@ -559,72 +562,124 @@
 		model.initialize();
 	}
 
-	var Model = function ( selectedDay, view, options )
+	var Model = function( selectedDay, view, options )
 	{
-		this.enabledMonthsRange = [];
-		this.datesCache = null;
-		//if ajax request is already sent, we do not change the model before the answer
-		this.ajaxPending = false;
+		this.events = null;
 
 		this.initialize = function ()
 		{
+			var self = this;
 			var startMonth = options.startMonth || new Date( 1900, 0, 1 ),
 				endMonth = options.endMonth || new Date( 2050, 0, 1 );
+			startMonth.setDate( 1 );
 			this.enabledMonthsRange = [ startMonth, endMonth ];
 			this.monthDate = new Date( selectedDay );
 			this.selectedDay = new Date( selectedDay );
-			view.initialize(this.monthDate, null, this.getSwitcherStates(this.monthDate));
-			this.switchMonth( 0 );
 
-			/*
+			function bootstrapView() {
+				view.initialize( self.monthDate, null, self.getSwitcherStates( self.monthDate ) );
+				self.switchMonth( 0 );
+			}
+
 			if( !options.onFetch ) {
-				view.initialize(this.monthDate, null, this.getSwitcherStates(this.monthDate));
-				this.switchMonth( 0 );
+				bootstrapView();
 			} else {
-				var self = this;
-				this.fetchEvents( function( events ) {
-					self.initEvents( events );
-					view.initialize(this.monthDate, null, this.getSwitcherStates(this.monthDate));
-					self.switchMonth( 0 );
+				options.onFetch( function( events ) {
+					self.events = self.initEvents( events );
+					bootstrapView();
 				} );
 			}
-			*/
 		}
 
 		this.switchMonth = function (go)
 		{
+			var dir = go || -1;
 			var date = new Date( this.monthDate );
 			date.setMonth(date.getMonth() + go);
-			this.switchDate( date, go );
+			this.switchDate( date, dir );
 		}
 
 		this.switchYear = function (go)
 		{
-			var date = new Date( this.monthDate );
-			date.setFullYear(date.getFullYear() + go, date.getMonth(), date.getDate());
-			if( !this.insideRange( this.enabledMonthsRange, date ) ) {
-				var add = getDateNumber( date ) < getDateNumber( this.enabledMonthsRange[ 0 ] ) ? 1 : -1;
-				while( !this.insideRange( this.enabledMonthsRange, date ) ) {
-					date.setMonth( date.getMonth() + add );
+			var sgn = ( go > 0 ) ? 1 : ( go < 0 ) ? - 1 : 0,
+				count = sgn * ( ( Math.abs( go )  ) * 12 );
+			this.switchMonth( count );
+		}
+
+		this.initEvents = function( data ) {
+			return function() {
+				var datesArr = [];
+				var datesCache = [];
+
+				if( typeof data === "object" ) {
+					var id = "";
+					for( var year in data ) {
+						for( var month in data[ year ] ) {
+							id = makeHash( year, month );
+							datesCache[ id ] = jQuery.map( data[ year ][ month ], function( item ) { return parseInt( item, 10 ); } );
+							datesArr.push( id );
+						}
+					}
 				}
-			}
-			this.switchDate( date, go );
+				datesArr.sort();
+
+				function outOfBounds( date ) {
+					return ( datesArr.length === 0 || date < datesArr[ 0 ] || date > datesArr[ datesArr.length - 1 ] );
+				}
+
+				function makeHash( year, month ) {
+					year = year.toString(); month = month.toString();
+					return parseInt( year + ( (month.length == 1) ? "0" : "" ) + month, 10 );
+				}
+
+				return {
+					getPrev: function( date ) {
+						if( outOfBounds( date ) ) { return false; }
+						for( var i = datesArr.length - 1; i >= 0 ; --i ) {
+							if( datesArr[ i ] < date ) {
+								return datesCache[ i ];
+							}
+						}
+						return false;
+					},
+
+					getNext: function( date ) {
+						if( outOfBounds( date ) ) { return false; }
+						for( var i = 0; i < datesArr.length; ++i ) {
+							if( datesArr[ i ] > date ) {
+								return datesCache[ i ];
+							}
+						}
+						return false;
+					},
+
+					hasEventsAfter: function( date, dir ) {
+						var hash = makeHash( date.getFullYear(), date.getMonth() + 1 );
+						var arr = jQuery.grep( datesArr, function( n, i ) {
+							return ( dir > 0 ) ? n > hash : n < hash;
+						} );
+
+						return arr.length > 0;
+					},
+
+					getEvents: function( date ) {
+						var hash = makeHash( date.getFullYear(), date.getMonth() + 1 );
+						return ( hash in datesCache ) ? datesCache[ hash ] : false;
+					}
+				}
+			}();
 		}
 
 		this.switchDate = function( date, dir ) {
 			dir = dir || -1;
-			if( this.ajaxPending ) {
-				return;
+			date = this.fitDate( date, this.monthDate, dir );
+
+			if( date !== false ) {
+				this.monthDate = date;
+
+				var events = ( this.events ) ? this.events.getEvents( date ) : null;
+				view.modelChanged( date, this.selectedDay, events, this.getSwitcherStates( date ) );
 			}
-			this.monthDate = date;
-
-			var self = this;
-			this.fetchMonthEvents( this.monthDate, function( events, date ) {
-				if( typeof events === "boolean" && events === false ) {
-				} else {
-					view.modelChanged(self.monthDate, self.selectedDay, events, self.getSwitcherStates( self.monthDate ) );
-				}
-			}, dir );
 		}
 
 		this.selectDate = function( date ) {
@@ -632,46 +687,68 @@
 			view.dateSelected( this.selectedDay );
 		}
 
-		this.fetchMonthEvents = function( date, onFetch ) {
-			this.ajaxPending = true;
+		this.getSwitcherStates = function (monthDate)
+		{
+			var yearStart = new Date( monthDate.getFullYear(), 0, 1 ),
+				yearEnd = new Date( monthDate.getFullYear(), 11, 1 );
 
-			//if there is no resource to fetch data, we think, that every month has events
-			if( !options.onFetchMonth ) {
-				onFetch( [] );
-				this.ajaxPending = false;
-			} else {
-				var self = this;
-				options.onFetchMonth( date, function( data ) {
-					self.ajaxPending = false;
-					onFetch( data );
-				} );
-			}
+			return {
+				prevMonth: this.isActivePrev( monthDate ) !== false,
+				prevYear: this.isActivePrev( yearStart ) !== false,
+				nextMonth: this.isActiveNext( monthDate ) !== false,
+				nextYear: this.isActiveNext( yearEnd ) !== false
+			};
 		}
 
-		this.getSwitcherStates = function (monthDate)
-		{
-			var prevMonth = new Date(monthDate);
-			prevMonth.setMonth(prevMonth.getMonth() - 1);
-			var pm = this.insideRange(this.enabledMonthsRange, prevMonth);
+		this.isActiveNext = function( date ) { return this.isActiveDate( date, 1 ); };
+		this.isActivePrev = function( date ) { return this.isActiveDate( date, -1 ); };
+		this.isActiveDate = function( date, dir ) {
+			var checkEvents = !!( this.events !== null );
+			var d = new Date( date );
+			d.setMonth( d.getMonth() + dir );
 
-			var nextMonth = new Date(monthDate)
-			nextMonth.setMonth(nextMonth.getMonth() + 1);
-			var nm = this.insideRange(this.enabledMonthsRange, nextMonth);
+			return this.insideRange( this.enabledMonthsRange, d ) && ( !checkEvents || this.events.hasEventsAfter( d, dir ) );
+		}
 
-			var prevYear = new Date(monthDate);
-			prevYear.setFullYear(monthDate.getFullYear() - 1, 11, 31 );
-			var py = this.insideRange(this.enabledMonthsRange, prevYear);
+		this.fitDate = function( date, currentDate,  dir ) {
+			date = new Date( date );
+			var checkEvents = !!( this.events !== null );
+			if( !this.insideRange( this.enabledMonthsRange, date ) ) {
+				if( getDateNumber( date, true ) < getDateNumber( this.enabledMonthsRange[ 0 ], true ) ) {
+					date = new Date( this.enabledMonthsRange[ 0 ] );
+				} else {
+					date = new Date( this.enabledMonthsRange[ 1 ] );
+				}
+			}
 
-			var nextYear = new Date(monthDate);
-			nextYear.setFullYear(monthDate.getFullYear() + 1, 0, 1 );
-			var ny = this.insideRange(this.enabledMonthsRange, nextYear);
+			if( !checkEvents || this.events.getEvents( date ) ) {
+				return date;
+			}
 
-			return { prevMonth: pm, nextMonth: nm, prevYear: py, nextYear: ny };
+			saveDate = new Date( date );
+
+			var sgn = ( dir > 0 ) ? 1 : -1;
+			while( this.insideRange( this.enabledMonthsRange, date ) && !this.events.getEvents( date ) ) {
+				date.setMonth( date.getMonth() + sgn );
+				if( this.events.getEvents( date ) ) {
+					return date;
+				}
+			}
+
+			date = saveDate;
+			while( ( getDateNumber( date, true ) !== getDateNumber( currentDate, true ) ) ) {
+				if( this.events.getEvents( date ) ) {
+					return date;
+				}
+				date.setMonth( date.getMonth() - sgn );
+			}
+
+			return false;
 		}
 
 		this.insideRange = function (range, iDate) {
-			return getDateNumber( iDate ) >= getDateNumber( range[0] )
-					&& getDateNumber( iDate ) <= getDateNumber( range[1] )
+			return getDateNumber( iDate, true ) >= getDateNumber( range[0], true )
+					&& getDateNumber( iDate, true ) <= getDateNumber( range[1], true );
 		};
 	}
 

Tags: can3p, js, livejournal
Subscribe
  • Post a new comment

    Error

    Anonymous comments are disabled in this journal

    default userpic

    Your reply will be screened

    Your IP address will be recorded 

  • 0 comments