﻿/*

$Id: popular-products.js,v 1.5 2011/08/28 10:33:40 JiriF Exp $

--------------------------------------------------------------------------------
Popular products area object for presenting contents in Oriflame v3.5 frontpage.
Requires jQuery, jQuery easing to be loaded before to work properly.
Also the dbg() function used for writing messages into console should be present.
--------------------------------------------------------------------------------

<![CDATA[

*/
var pp = popularProducts = createDebugObject( {
	
	// used to set up debug messages level
	objectName : "popularProducts",
	// category for which to filter at start
	defaultCategory : "all",
	// product info URL for AJAX
	productInfoUrl : "/front-page/products-area-contents.jhtml",
	// product detail URL base
	productUrl : "/products/product-detail.jhtml?prodCode=",
	// open / close category filter button class indicating open state
	btnOpenClass : "category-filter-open",
	// category filter button class indicating selected state
	btnSelectedClass : "selected",
	// animation settings
	animation : {
		// category filter animation
		filter : {
			duration : 0.8,
			easing : "easeInOutCubic"
		},
		// product detail animation
		product : {
			delay : 1.0,
			timeoutStorage : 0,
			currentProduct : "",
			hoverOpacity : 0.3,
			maskOpacity : 0.3,
			duration : 0.5,
			easing : "easeInOutCubic"
		}
	},
	// element selectors
	sel : {
		wrapper : "#products-area-wrapper",
		header : "#products-area-wrapper .module-header h2",
		categoryLabel : "#choose-category-label span",
		categoryOpenButton : "#choose-category-button",
		categoryFilter : "#category-filter",
		filterMenu : "#filter-menu",
		menuLinks : "#filter-menu-links",
		filterButtons : "#filter-menu-links .filter-button",
		productsArea : "#products-area",
		productThumbs : "#product-thumbs",
		productHover : ".hover-image",
		productDetail : "#product-info",
		productDetailShadow : "#product-info-shadow",
		productDetailCloseButton : "#product-info-close"
	},
	
	// init --------------------------------------------------------------------
	
	/**
	 * Init function can receive startup property values in options object.
	 */
	init : function( options ) {
		// pp.out( "popularProducts.init()" );
		if( ! options ) {
			pp.out( "Empty data for initialization.", 1 );
			return;
		}
		// initialize properties with provided data
		// TODO : better control of values specified in options
		for( var p in options ) pp[ p ] = options[ p ];
		// initialize main HTML structures
		var w = jQuery( pp.sel.wrapper );
		w.append(
			"<div class='module-header'><h2>" + pp.escq( pp.tr.moduleHeader ) + "</h2></div>" +
			"<div id='choose-category'>" +
				"<div id='choose-category-label'><span>" + pp.escq( pp.tr.categoryLabel ) + "</span></div>" +
				"<a id='choose-category-button'></a>" +
			"</div>" +
			"<div id='category-filter'>" +
				"<div id='filter-menu'>" +
					"<div id='filter-menu-links-wrapper'>" +
						"<div id='filter-menu-links'></div>" +
					"</div>" +
					"<div id='filter-menu-shadow'></div>" +
				"</div>" +
			"</div>" +
			"<div id='products-area'>" +
				"<div id='product-thumbs'></div>" +
				// "<div id='product-info-shadow'></div>" +
				"<div id='product-info'></div>" +
			"</div>"
		);
		// category filter functionality setup
		pp.categoryFilterSetup();
	},
	
	/**
	 * Category selector and button animations setup. Category filter is a clipping
	 * mask here, filter menu moves up and down inside it.
	 */
	categoryFilterSetup : function() {
		// open button init
		var btn = jQuery( pp.sel.categoryOpenButton );
		var m = jQuery( pp.sel.filterMenu );
		var f = jQuery( pp.sel.categoryFilter );
		btn.click( function() {
			// pp.out( m.height() );
			if( jQuery( this ).hasClass( pp.btnOpenClass ) ) {
				// close it
				m.stop().animate(
					{ top : "-" + m.height() + "px" },
					{
						duration : pp.animation.filter.duration * 1000,
						easing : pp.animation.filter.easing,
						complete : function() {
							btn.removeClass( pp.btnOpenClass );
							f.hide( 0 );
						}
					}
				);
			} else {
				// open it
				btn.addClass( pp.btnOpenClass );
				f.show( 0 ).height( m.height() );
				m.stop().css( {
					top : "-" + m.height() + "px"
				} ).animate(
					{ top : 0 },
					{
						duration : pp.animation.filter.duration * 1000,
						easing : pp.animation.filter.easing
					}
				);
			}
		} );
		// category buttons init
		if( ! pp.categories ) {
			pp.out( "Empty popular product categories, can't initialize filters properly.", 1 );
			return;
		}
		var l = jQuery( pp.sel.menuLinks );
		for( var c in pp.categories )
			if( pp.categories[ c ].enabled ) {
				// pp.out( c );
				var b = jQuery(
					"<a class='filter-button" +
					( ( c == pp.defaultCategory ) ? " selected" : "" ) +
					"'>" + pp.escq( pp.categories[ c ].title ) + "</a>"
				);
				b.data( "category", c )
				.click( function() {
					// pp.out( "catg : " + jQuery( this ).data( "category" ) );
					pp.lastFilterButton = jQuery( this );
					pp.loadProducts( pp.lastFilterButton.data( "category" ) );
				} )
				.appendTo( l );
				if( c == pp.defaultCategory ) pp.lastFilterButton = b; 
			}
		// set default category filter
		pp.loadProducts( pp.defaultCategory );
		// apply product detail mouse events
		jQuery( pp.sel.productDetail ).mouseleave( function() {
			pp.out( "detail mouseleave() ..." );
			pp.hideDetail();
		} )
		/* jQuery( pp.sel.productDetailShadow ).mouseenter( function() {
			pp.out( "mask mouseenter() ..." );
			pp.hideDetail();
		} ) */
	},
	
	/**
	 * Marks last clicked category button as selected after successful AJAX load.
	 */
	selectFilterButton : function() {
		if( pp.lastFilterButton )
			pp.lastFilterButton.addClass( pp.btnSelectedClass );
		pp.lastFilterButton = null;
	},
	
	/**
	 * Hides category filter if open.
	 */
	hideCategoryFilter : function() {
		if( jQuery( pp.sel.categoryFilter ).is( ":visible" ) )
			jQuery( pp.sel.categoryOpenButton ).click();
	},
	
	/**
	 * Starts loading new products data via AJAX.
	 */
	loadProducts : function( category ) {
		pp.out( "Loading products info for category '" + category + "' ..." );
		var settings = {
			type : "POST",
			url : pp.productInfoUrl,
			dataType : "json",
			cache : false,
			success : pp.productsLoaded,
			error : pp.productsError
		};
		if( category && category != "all" )
			settings.data = "category=" + category;
		jQuery.ajax( settings );
	},
	
	/**
	 * Product info was successfully loaded. Fills the product area with product
	 * data.
	 */
	productsLoaded : function( data ) {
		pp.out( "Product info successfully loaded." );
		jQuery( pp.sel.filterButtons ).removeClass( pp.btnSelectedClass );
		pp.selectFilterButton();
		pp.hideCategoryFilter();
		var pt = jQuery( pp.sel.productThumbs );
		pt.html( "" );
		if( ! data || ! data.products || ! data.products.length ) {
			pp.out( "Empty popular products data for selected category.", 2 );
			return;
		}
		// create product thumbnails
		for( var p in data.products )
			if( data.products[ p ].title || data.products[ p ].image ) {
				var l = jQuery(
					"<a class='product' id='prod" + data.products[ p ].code +
					// "' title='" + pp.escq( data.products[ p ].title ) +
					"'><img src='" + data.products[ p ].image +
					"' alt='" + pp.escq( data.products[ p ].title ) +
					// "' title='" + pp.escq( data.products[ p ].title ) +
					"' /></a>"
				);
				// store product data on element, apply mouse over and append to DOM
				l.data( data.products[ p ] )
				.click( function() {
					pp.out( "click() " + jQuery( this ).data( "title" ) );
					// pp.hideDetail();
					if( pp.animation.product.timeoutStorage ) {
						clearTimeout( pp.animation.product.timeoutStorage );
						pp.animation.product.timeoutStorage = 0;
					}
					pp.animation.product.currentProduct = jQuery( this ).data( "code" );
					pp.showDetail();
					/* pp.animation.product.timeoutStorage = setTimeout(
						"pp.showDetail()", pp.animation.product.delay * 1000
					); */
				} )
				.hover(
					function() {
						var t = jQuery( this );
						var h = t.find( pp.sel.productHover );
						if( ! h.length ) {
							t.append( "<div class='hover-image'></div>" );
							h = t.find( pp.sel.productHover );
						}
						h.fadeTo(
							0 /* pp.animation.product.duration * 1000 */,
							pp.animation.product.hoverOpacity,
							pp.animation.product.easing
						);
					},
					function() {
						var h = jQuery( this ).find( pp.sel.productHover );
						if( ! h.length ) return;
						h.fadeOut(
							0 /* pp.animation.product.duration * 1000 */,
							pp.animation.product.easing,
							function() {
								jQuery( this ).remove();
							}
						);
					}
				)
				/* .mouseenter( function() {
					pp.out( "mouseenter() " + jQuery( this ).data( "title" ) );
					pp.hideDetail();
					if( pp.animation.product.timeoutStorage ) {
						clearTimeout( pp.animation.product.timeoutStorage );
						pp.animation.product.timeoutStorage = 0;
					}
					pp.animation.product.currentProduct = jQuery( this ).data( "code" );
					pp.animation.product.timeoutStorage = setTimeout(
						"pp.showDetail()", pp.animation.product.delay * 1000
					);
				} ) */
				.appendTo( pt );
			}
	},
	
	/**
	 * Error occured while loading or parsing product info JSON data.
	 */
	productsError : function( jqXHR, textStatus, errorThrown ) {
		pp.out( "Product info loading error : " + textStatus + " : " + errorThrown, 1 );
		pp.lastFilterButton = null;
		pp.hideCategoryFilter();
	},
	
	/**
	 * Helper function for escaping quotes.
	 */
	escq : function( s ) {
		if( typeof s != "string" ) return s;
		return s.replace( /\"/g, "&#34;" ).replace( /\'/g, "&#39;" ).replace( /\\/g, "&#92;" );
	},
	
	/**
	 * Displays detailed product info in a popup layer.
	 */
	showDetail : function() {
		var code = pp.animation.product.currentProduct;
		// pp.hideCategoryFilter();
		// product detail placement
		var p = jQuery( "#prod" + code );
		var tw = parseInt( p.width() ) + parseInt( p.css( "marginLeft" ) ) +
			parseInt( p.css( "marginRight" ) );
		var paw = parseInt( jQuery( pp.sel.productsArea ).width() );
		var align = ( p.position().left >= ( 2 * tw ) ) ? "right" : "left";
		var d = jQuery( pp.sel.productDetail );
		var w = parseInt( d.width() ) + parseInt( d.css( "paddingLeft" ) ) +
			parseInt( d.css( "paddingRight" ) );
		pp.out(
			"Displaying product detail ... " + code +
			", position [ " + p.position().left + ", "  + p.position().top +
			" ], align " + align + ", paw " + paw + ", w " + w + ", tw " + tw
		);
		// update product detail contents before showing it
		d.css( {
			left : (
				( align == "left" ) ?
				( p.position().left + 3 ) :
				( p.position().left - w + tw )
			) + "px",
			top : p.position().top + 3 + "px"
		} ).html(
			"<div class='description " + align + "'>" +
				"<a class='title' target='_top' href='" +
				pp.productUrl + p.data( "code" ) + "'>" +
				pp.escq( p.data( "title" ) ) + "</a>" +
				pp.ratingsMarkup( p.data() ) +
				pp.priceMarkup( p.data() ) +
				"<a class='go-to-product' href='" + pp.productUrl + p.data( "code" ) + "'>" +
					pp.tr.toProduct +
				"</a>" +
			"</div>" +
			"<a class='image " + align + "' href='" + pp.productUrl + p.data( "code" ) +
			// "' title='" + p.data( "title" ) +
			"' alt='" + pp.escq( p.data( "title" ) ) +
			"'><img src='" + p.data( "image" ) +
			// "' title='" + p.data( "title" ) +
			"' alt='" + pp.escq( p.data( "title" ) ) + "' /></a>" +
			"<div id='product-info-close'></div>"
		).fadeIn(
			pp.animation.product.duration * 1000, pp.animation.product.easing
		);
		// product detail mask
		/* jQuery( pp.sel.productDetailShadow ).fadeTo(
			pp.animation.product.duration * 1000, pp.animation.product.maskOpacity,
			pp.animation.product.easing
		); */
		// close button event
		jQuery( pp.sel.productDetailCloseButton ).click( function() {
			pp.hideDetail();
		} );
		// clear timeout data
		pp.animation.product.currentProduct = "";
		pp.animation.product.timeoutStorage = 0;
	},
	
	/**
	 * Helper function to create product detail ratings HTML markup.
	 */
	ratingsMarkup : function( data ) {
		if( ! data ) return "";
		var r = data.ratings.totalRating;
		var m = "<a class='a-ratings' target='_top' href='" +
			pp.productUrl + data.code + "&amp;tab=1'>" +
			"<div class='rating-stars'>";
		if( data.ratings.viewTotalRating )
			m += "<div id='rating-totalRating'>" + r + "</div>";
		// 1st star : less than 1 empty, 1 full
		m += "<div title='" + r + "' class='star" +
			( ( r < 1.0 ) ? "Empty" : "Full" ) +
			"'></div>";
		// 2nd star : 1 - 1.25 empty, 1.25 - 1.75 half, 1.75 - 2 full
		m += "<div title='" + r + "' class='star" +
			( ( r < 1.25 ) ? "Empty" : ( ( r < 1.75 ) ? "Half" : "Full" ) ) +
			"'></div>";
		// 3rd star : 2 - 2.25 empty, 2.25 - 2.75 half, 2.75 - 3 full
		m += "<div title='" + r + "' class='star" +
			( ( r < 2.25 ) ?
				"Empty" :
				( ( r < 2.75 ) ? "Half" : "Full" )
			) + "'></div>";
		// 4th star : 3 - 3.25 empty, 3.25 - 3.75 half, 3.75 - 4 full
		m += "<div title='" + r + "' class='star" +
			( ( r < 3.25 ) ? "Empty" : ( ( r < 3.75 ) ? "Half" : "Full" ) ) +
			"'></div>";
		// 5th star : 4 - 4.25 empty, 4.25 - 4.75 half, 4.75 - 5 full
		m += "<div title='" + r + "' class='star" +
			( ( r < 4.25 ) ? "Empty" : ( ( r < 4.75 ) ? "Half" : "Full" ) ) +
			"'></div>";
		m += ( ( data.ratings.ratesOnNextLine ) ?
			"<div class='nextLineRateCount'>" :
			"<span class='rating-stars-ratingCount'>("
		) + data.ratings.ratingCount + " " + pp.escq( pp.tr.ratingCount ) +
		( ( data.ratings.ratesOnNextLine ) ? "</div>" : ")</span>" );
		if( data.ratings.canRate )
			m += "<span class='rating-stars-rateIt'>" + pp.escq( pp.tr.rateIt ) +
				"</span>";
		m += "</div></a>";
		return m;
	},
	
	/**
	 * Helper function to create product detail price HTML markup.
	 */
	priceMarkup : function( data ) {
		if( ! data || data.multisku || data.dontSell ) return "";
		var m = "<div class='priceBox'>";
		if( data.discount ) {
			m += "<p class='price red'>" + pp.escq( data.price ) + "</p>" +
				"<p class='origPrice'>" + pp.escq( data.origPrice ) + "</p>";
		} else {
			m += "<p class='price'>" + pp.escq( data.price ) + "</p>";
		}
		m += "</div>";
		return m;
	},
	
	/**
	 * Hides product detail info.
	 */
	hideDetail : function() {
		pp.out( "Hiding product detail ..." );
		if( jQuery( pp.sel.productDetail ).is( ":visible" ) )
			jQuery( pp.sel.productDetail ).fadeOut(
				pp.animation.product.duration * 1000, pp.animation.product.easing
			);
		/* if( jQuery( pp.sel.productDetailShadow ).is( ":visible" ) )
			jQuery( pp.sel.productDetailShadow ).fadeOut(
				pp.animation.product.duration * 1000, pp.animation.product.easing
			); */
	}
	
} );

