Палсеич— (lusever) wrote in changelog,
Палсеич—
lusever
changelog

[livejournal] r17900: LJSUP-7377: Wishlist.

Committer: pkornilov
LJSUP-7377: Wishlist.
U   trunk/htdocs/js/jquery_fn.js
Modified: trunk/htdocs/js/jquery_fn.js
===================================================================
--- trunk/htdocs/js/jquery_fn.js	2010-12-10 10:17:04 UTC (rev 17899)
+++ trunk/htdocs/js/jquery_fn.js	2010-12-10 12:14:33 UTC (rev 17900)
@@ -91,6 +91,10 @@
 				{
 					$this.hasClass('placeholder') && $this.removeClass('placeholder').val('');
 				});
+				
+				if (this.value !== this.getAttribute('placeholder')) {
+					$this.removeClass('placeholder');
+				}
 			});
 			return this;
 		}
@@ -148,3 +152,480 @@
 {
 	return fn ? this.bind('input keyup paste', fn) : this.trigger('input');
 }
+
+/* function based on markup:
+	tab links: ul>li>a
+	current tab: ul>li.current
+	tab container: ul>li
+	tab container current: ul>li.current
+*/
+jQuery.fn.tabsChanger = function(container)
+{
+	var links = this.children("li").children("a");
+	
+	if (container) {
+		container = jQuery(container);
+	} else {
+		// next sibling of links
+		container = links.parent().parent().next();
+	}
+	
+	links.click(function(e)
+	{
+		var item = jQuery(this).parent(),
+			index = item.index(),
+			containers = container.children("li");
+
+		if (containers[index]) {
+			links.parent().removeClass("current");
+			item.addClass("current");
+
+			containers.removeClass("current")
+				.eq(index)
+				.addClass("current");
+
+			e.preventDefault();
+		}
+	});
+}
+
+/** jQuery overlay plugin
+ * After creation overlay visibility can be toggled with
+ * $( '#selector' ).overlay( 'show' ) and $( '#selector' ).overlay( 'hide' )
+*/
+jQuery.fn.overlay = function( opts ) {
+	var options = {
+			hideOnInit: true,
+			hideOnClick: true
+		};
+
+	function Overlay( layer, options) {
+		this.layer = jQuery( layer );
+		this.options = options;
+		this.updateState( this.options.hideOnInit );
+		this.bindEvents();
+	}
+
+	Overlay.prototype.bindEvents = function() {
+		var overlay = this;
+
+		if( this.options.hideOnClick ) {
+			overlay.layer.mousedown( function( ev ) {
+				ev.stopPropagation();
+			} );
+
+			jQuery( document ).mousedown(function( ev ) {
+				overlay.updateState( true );
+				ev.stopPropagation();
+			} );
+		}
+	};
+
+	Overlay.prototype.updateState = function( hide ) {
+		this.layerVisible = !hide;
+		if( this.layerVisible ) {
+			this.layer.show();
+		} else {
+			this.layer.hide();
+		}
+	}
+
+	Overlay.prototype.proccessCommand = function ( cmd ) {
+		switch( cmd ) {
+			case 'show' :
+				this.updateState( false );
+				break;
+			case 'hide' :
+				this.updateState( true );
+				break;
+		}
+	}
+
+	var cmd;
+	if( typeof opts === "string" ) {
+		cmd = opts;
+	}
+
+	return this.each( function() {
+		if( !this.overlay ) {
+			var o = jQuery.extend( {}, options, opts || {} );
+			this.overlay = new Overlay( this, o );
+		}
+
+		if( cmd.length > 0 ) {
+			this.overlay.proccessCommand( opts )
+		}
+	});
+}
+
+jQuery.fn.calendar = function( o ) {
+	//global variables for all instances
+	var defaultOptions = {
+		monthNames: [ "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" ],
+		dayRef: "/%Y/%M/%D",
+		onFetchMonth: null,
+		currentDate: new Date(),
+		//allow user to select dates in this range
+		activeUntil: null,
+		activeFrom: null,
+		//allow user to switch months between these dates
+		startMonth: null,
+		endMonth: null,
+		startAtSunday: true
+	};
+
+	var nodeSelectors =
+	{
+		table: 'table',
+
+		prevMonth: '.cal-nav-month .cal-nav-prev',
+		nextMonth: '.cal-nav-month .cal-nav-next',
+		prevYear:  '.cal-nav-year .cal-nav-prev',
+		nextYear:  '.cal-nav-year .cal-nav-next',
+
+		monthLabel: '.cal-nav-month .cal-month',
+		yearLabel: '.cal-nav-year .cal-year'
+	}
+
+	var styles =
+	{
+		inactive : 'other',
+		future : 'other',
+		current  : 'current',
+		nextDisabled : 'cal-nav-next-dis',
+		prevDisabled : 'cal-nav-prev-dis',
+		cellHover : 'hover'
+	}
+
+	function initCalendar( node, options ) {
+		var nodes = { container: node };
+		for( var i in nodeSelectors ) {
+			nodes[ i ] = nodes.container.find( nodeSelectors[ i ] );
+		}
+
+		var view = new View(nodes, styles, options);
+		var model = new Model( options.currentDate, view, options);
+		var controller = new Controller(view, model, options);
+	}
+
+	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)
+	}
+
+	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() ];
+	}
+
+	function isLeapYear(year){
+		return ( year % 4 === 0 && (year % 100 != 0 ) ) || ( year % 400 == 0);
+	}
+
+	var View = function (nodes, styles, o)
+	{
+		this.initialize = function (monthDate, events, switcherStates)
+		{
+			this.tbody = this.catchTableStructure();
+
+			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.detach();
+			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 = jQuery( 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 ) {
+					var node = jQuery( tbody.rows[ row ].cells[ cell ] );
+					nodes.daysCells.push( node );
+				}
+			}
+
+			return jQuery( 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 ], null, this.isActiveDate(iDate, monthDate ) );
+			}
+
+			for (var i = 1; i <= length; i++)
+			{	// body
+				var iDate = new Date(monthDate);
+				iDate.setDate(i);
+
+				this.formDayString( iDate, nodes.daysCells[ i + offset ], events, this.isActiveDate(iDate, monthDate ) );
+			}
+
+			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], null, this.isActiveDate(iDate, monthDate ) );
+				++k;
+			}
+
+			this.toggleExtraWeek(monthDate.getDay() > 5);
+		}
+
+		this.toggleExtraWeek  = function (show)
+		{
+			nodes.lastRow[show ? 'show' : 'hide']();
+		}
+
+		this.isActiveDate = function( date, currentMonth ) {
+			var isActive = true;
+
+			isActive = ( currentMonth.getFullYear() === date.getFullYear() && currentMonth.getMonth() === date.getMonth() );
+
+			if( isActive && ( o.activeFrom || o.activeUntil ) ) {
+				isActive = ( o.activeFrom && getDateNumber( o.activeFrom ) <= getDateNumber( date ) )
+				|| ( o.activeUntil && getDateNumber( o.activeUntil ) >= getDateNumber( date ) );
+			}
+
+			return isActive;
+		}
+
+		this.formDayString = function( d, label, events, isActive )
+		{
+			events = events || [];
+			label.data( 'day', d );
+			label.data( 'isActive', isActive );
+
+			var today = new Date(),
+				isCurrentDay = ( getDateNumber( d ) == getDateNumber( o.currentDate ) ),
+				hasEvents = ( jQuery.inArray( d.getDate(), events ) > -1 );
+
+			label[isCurrentDay ? 'addClass' : 'removeClass']( styles.current );
+
+			if( !isActive ) {
+				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( jQuery("<a>")
+						.html( d.getDate() )
+						.attr( 'href', ref ) );
+			} else {
+				label.removeClass( styles.inactive ).html(d.getDate());
+			}
+		}
+
+		this.bindEvents = function ()
+		{
+			var self = this;
+			for (var sws in this.switcherStates) {
+				nodes[sws].mousedown( function (item) { return function ( ev ) {
+					if(self.switcherStates[item]) {
+						self.controller[item]();
+					}
+
+					ev.preventDefault();
+				} }(sws));
+			}
+
+			var cells = nodes.daysCells;
+			for( var i = 0, l = cells.length; i < l; ++i ) {
+				cells[i].mouseover( function() {
+						var cell = jQuery( this );
+						cell.data( 'isActive' ) && cell.addClass( styles.cellHover );
+					} )
+					.mouseout( function() {
+						var cell = jQuery( this );
+						cell.removeClass( styles.cellHover );
+					} )
+					.click( function() {
+						var cell = jQuery( this );
+						if( cell.data( 'isActive' ) ) {
+							self.controller.cellSeleted( cell.data( 'day' ) );
+						}
+					} );
+			}
+		}
+
+		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, o )
+	{
+		this.prevMonth = function () { model.switchMonth(-1); };
+		this.nextMonth = function () { model.switchMonth( 1); };
+
+		this.prevYear  = function () { model.switchYear(-1); };
+		this.nextYear  = function () { model.switchYear( 1); };
+
+		this.cellSeleted = function( date ){
+			if( o.onDaySelected && view.isActiveDate( date, model.monthDate ) ) {
+				o.onDaySelected( date );
+			}
+		};
+
+		view.controller = this;
+		model.initialize();
+	}
+
+	var Model = function ( selectedDay, view, options )
+	{
+		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 startMonth = options.startMonth || new Date( 1900, 0, 1 ),
+				endMonth = options.endMonth || new Date( 2050, 0, 1 );
+			this.enabledMonthsRange = [ startMonth, endMonth ];
+			this.monthDate = new Date( selectedDay );
+			view.initialize(this.monthDate, null, this.getSwitcherStates(this.monthDate));
+
+			this.switchMonth( 0 );
+		}
+
+		this.switchMonth = function (go)
+		{
+			var date = new Date( this.monthDate );
+			date.setMonth(date.getMonth() + go);
+			this.switchDate( date );
+		}
+
+		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 );
+				}
+			}
+			this.switchDate( date );
+		}
+
+		this.switchDate = function( date ) {
+			if( this.ajaxPending ) {
+				return;
+			}
+			this.monthDate = date;
+
+			var self = this;
+			this.fetchMonthEvents( this.monthDate, function( events ) {
+				view.modelChanged(self.monthDate, events, self.getSwitcherStates( self.monthDate ) );
+			} )
+		}
+
+		this.fetchMonthEvents = function( date, onFetch ) {
+			this.ajaxPending = true;
+
+			//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 );
+				} );
+			}
+		}
+
+		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, 11, 31 );
+			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] };
+	}
+
+	return this.each( function() {
+		if( "monthNames" in o && o.monthNames == null ) {
+			delete o.monthNames;
+		}
+		var options = jQuery.extend( {}, defaultOptions, o);
+		initCalendar( jQuery(this), options );
+	} );
+}

Tags: js, livejournal, lusever
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