/* LCore.js
 *
 * Base Javascript class for Lulu-specific functionality.
 * This file will eventually replace lulu/lulu.js.
 */

/** initialize LULU namespace **/
if (typeof(LULU) == 'undefined') {
	var LULU = {};
	var $L = LULU;
}

if ((typeof __start != 'object') || (typeof __start.getTime != 'function')) {
    var __start = new Date();
}
$L.__executionStart = __start;
$L._getExecutionTime = function ( ) {
    var end = new Date();
    return time = (end.getTime() - $L.__executionStart.getTime()) / 1000;
}

$L.reportExecutionTime = function ( ) {
    var stats = $jq('#lulu_site_query_stats');
    if (stats.length == 1) {
        stats.append($jq("<div>").text("Client load & execution: " + $L._getExecutionTime() + " seconds"));
    }
}


/*
 * provides a layer of prototypal abstraction for the instantiation of Objects
 */
$L.object = function ( o, hostObject ) {
	if ( typeof hostObject == 'undefined' ) {
		function F() {};
		F.prototype = o;
		that = new F();
	} else {
		that = hostObject;
	}
	if ( (typeof o.constructor == 'undefined') || (o.constructor == window) ) {
	    throw $L.Error("!! LCore: The 'new' keyword was not used when instantiating this object", {obj: o});
	} else {
        if ( typeof o.constructor.name == 'undefined' ) {
            var str = o.constructor.toString();
            that.typeOf = str.substring( 0, str.indexOf( "(" ) ).replace( "function ", "" ).replace( " ", "" );
        } else {
            that.typeOf = o.constructor.name;
        }
    }
	return that;
};

$L.isArray = function ( obj ) {
	return ( ( obj instanceof Array ) || ( obj.constructor.toString().indexOf("Array") > -1 ) );
};

$L.add = function( name, object ) {
	if ( typeof $L[name] != 'undefined') {
		throw $L.Error( "!! LCore: Attempted to create duplicate member '"+name+"'; dumping original and duplicate", {original: $L[name], duplicate: object} );
	} else {
		$L[name] = object;
	}
}

$L.addManager = function( name, manager ) {
	$L.add( name, manager );
};

$L.Error = function( msg, details ) {
	var err = new Error( msg );
	if ( typeof details == 'object' ) {
		for ( x in details ) {
			err[x] = details[x];
		}
	}
	return err;
};

$L.trace = function( traceKeyword, payload ) {
	if ( $L._tracer instanceof LTracer ) {
		return $L._tracer.trace( traceKeyword, payload );
	} else {
		return false;
	}
};

function LTracer ( endpoint )
{
	var obj = $L.object(this);
	
	obj.trace = function( traceKeyword, payload ) {
		if ( typeof payload == 'undefined' ) payload = {};
		if ( obj._$isGetAvailable() ) {
			obj._fire( obj._buildTrace( traceKeyword, payload ) );
			while ( obj._missed.length > 0 ) {
				var missed = obj._missed.pop();
				obj._fire( obj._buildTrace( obj._missed[0], obj._missed[1] ) );
			}
		} else {
			obj._store( traceKeyword, payload );
		}
		return obj;
	};

	obj._endpoint = endpoint;
	obj._missed = [];
	
	obj._buildTrace = function ( k, p ) {
		var data = [];
		for ( x in p ) {
			data.push( '&data['+x+']=' + p[x] );
		}
        return '?context=trace&event=' + k + data.join('');
	}
	
	obj._fire = function( trace ) {
		var url = obj._endpoint + trace;
		obj._$get( url );
	};
	
	obj._store = function( k, p ) {
		obj._missed.push( [ k, p ] );
	};
	
	obj._$get = function( url ) {
		$jq.get( url );
	};
	
	obj._$isGetAvailable = function() {
		if ( typeof $jq == 'undefined' ) {
			return false;
		} else {
			return true;
		}
	};
	
	return obj;
}

/**
 * LManager.js
 *
 * Base Manager class. Extend using $L.object( )
 *
 * @author Karim Nassar
 * @copyright Lulu Enterprises,  1 April, 2008
 **/

function LManager ( managedType )
{
	
	var obj = $L.object( this );
	
	/** public variables */
	// none

	/** private variables */
	obj._managedMembers = {};
	obj._expectedMemberType = managedType || false;
	obj._selected = new Array();
	obj._lastAdded = null;

	/** public methods */
	obj.add = function( component ) {
		if ( typeof component.id == 'undefined' ) throw $L.Error( "No Id defined on add( "+obj._expectedMemberType+" )", { args: component } );
		component = obj._checkDataBeforeAdd( component );
		if ( ! obj._expectedMemberType || component.typeOf == obj._expectedMemberType ) { // if _expectedMemberType is defined, validate against it
			if ( typeof component.id == 'undefined' ) {
				throw $L.Error( obj.typeOf + ".add() cannot add an object without an id" );
			}
			obj._managedMembers["_"+component.id] = component; // prepend an underscore to protect from numeric ids
			obj._lastAdded = component;
			obj.select( component.id );
		} else {
			throw $L.Error( obj.typeOf + ".add() was not passed a valid '"+obj._expectedMemberType+"' object", { args: component } );
		}
		return obj;
	};

	obj.select = function( ids ) {
		if ( typeof ids == 'undefined' ) {
			obj._selected = [];
		} else if ( $L.isArray( ids )) {
			obj._selected = [];
			for ( var i=0; i < ids.length; ++i ) {
				obj._selected.push( obj.getById( ids[i] ) );
			}
		} else if ( ids == '*' ) {
			obj._selected = [];
			for ( x in obj._managedMembers ) {
				obj._selected.push( obj._managedMembers[x] );
			}
		} else if ( ids == ':last' ) {
			obj._selected = [ obj._lastAdded ];
		} else {
			obj._selected = [ obj.getById( ids ) ];
		}
		return obj;
	};
	
	obj.remove = function( ) {
		if ( obj._selected != null ) {
			for ( var i = 0; i < obj._selected.length; ++i ) {
				obj._removeById( obj._selected[i].id );
			}
		}
		return obj;
	};
	
	obj.getById = function( id ) {
		return obj._managedMembers["_"+id]; // underscore prepended by add() to protect from numeric ids
	};

	obj.each = function( iFunc ) {
		for ( var i = 0; i < obj._selected.length; ++i ) {
			obj._selected[i].__eachFunc = iFunc;
			obj._selected[i].__eachFunc();
			obj._selected[i].__eachFunc = undefined;
		}
		return obj;
	};

	obj.call = function( functionName ) {
		for ( var i = 0; i < obj._selected.length; ++i ) {
		    if ( typeof obj._selected[i][functionName] == 'function' ) {
			    (obj._selected[i][functionName])();
		    }
		}
		return obj;
	};

	/** private methods */
	
	obj._removeById = function( id ) {
		obj._cleanUpBeforeRemove( id );
		obj._managedMembers["_"+id] = undefined; // underscore prepended by add() to protect from numeric ids
		return obj;
	};

	obj._cleanUpBeforeRemove = function( id ) { 
		// override this method as needed.
		// use obj.getById( id ) to access the object
		return true;
	}
	
	obj._checkDataBeforeAdd = function( dataStruct ) {
		// override this method as needed
		// always return the dataStruct !!
		return dataStruct;
	}
	
	obj._initAddedComponent = function ( id ) {
	   	// override this method as needed.
		// use obj.getById( id ) to access the object
		return true;
	}

	return obj;
}/**
 * A class for deferring actions until later
 * 
 * @copyright Copyright © 2008, Lulu Press
 **/
function LProcrastinator () {
    var obj = $L.object(this);
    
    obj._onDocumentReady = [];
    
    obj.initialize = function() {
        $L.add('executeFunctionWhenDocumentReady', obj._addDocumentReadyFunction);
    };
    
    obj._addDocumentReadyFunction = function( f ) {
        obj._onDocumentReady.push(f);
        if (obj._onDocumentReady.length == 1) {
            if (obj._$isIe()) {
                setTimeout( obj._$initDocumentReady, 80 );
            } else {
                obj._$initDocumentReady();
            }
        }
    };    
    
    obj._executeDocumentReadyFunctions = function() {
        for ( i=0; i < obj._onDocumentReady.length; i++ ) {
            obj._onDocumentReady[i]();
        }
        $L.reportExecutionTime();
    };
    
    obj._$initDocumentReady = function() {
        $jq( 'document' ).ready( obj._executeDocumentReadyFunctions );
    };
    
    obj._$isIe = function() {
        return $jq.browser.msie;
    }
    
    obj.initialize();
    return obj;
};
$L.add('_procrastinator', new LProcrastinator());// (c) dynarch.com 2003
// Author: Mihai Bazon, http://dynarch.com/mishoo/

$L.add( 'ieIsInArray', function (needle, arrayHaystack) {
 for (x=0; x < arrayHaystack.length; x++)
     if (arrayHaystack[x] == needle) return true;
 return false;
});

$L.add('iePngFix', function(){
    if (/MSIE [56].*Windows/.test(navigator.userAgent)) {
        var blank = new Image;
     blank.src = '/images/blank.gif';

     var imgs = document.getElementsByTagName("img");
     var inputs = document.getElementsByTagName("input");

     for (var i = imgs.length; --i >= 0;) {
         var img = imgs[i];
         var src = img.src;
         var classes = img.className.split(' ');

         if ( !$L.ieIsInArray('noPNGFix', classes) ) {
             if (!/\.png/.test(src))
                 continue;
             var s = img.runtimeStyle;
             s.width = img.offsetWidth + "px";
             s.height = img.offsetHeight + "px";
             s.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + src + "',sizingMethod='scale')";
             img.src = blank.src;
         }       
     }

     for (var i = inputs.length; --i >= 0;) {
         var  input = inputs[i];
         if (input.type == 'image') {
             var src = input.src;
             var classes = input.className.split(' ');

             if ( !$L.ieIsInArray('noPNGFix', classes) ) {
                 if (!/\.png/.test(src))
                     continue;
                 var s = input.runtimeStyle;
                 s.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + src + "',sizingMethod='image')";
                 input.src = blank.src;
             } 
         }   
     }
    }
});

if (typeof $L._procrastinator != 'undefined') {
    $L.executeFunctionWhenDocumentReady(function () {
        $L.iePngFix();
    });
}$L.add('EventLogger', {
        logProductView: function (contentId) {
            $jq.ajax({url: 'http://www.lulu.com/event/log_product_view.php?fCID='+contentId});
        },
        logExternalProductView: function (productId) {
            $jq.ajax({url: 'http://www.lulu.com/event/log_product_view.php?productId='+productId});
        }
	}
);function LInternationalUpdater (domainRoot, url) {
    
    var obj = $L.object(this);
    
    obj._domainRoot = domainRoot;
    obj._url = decodeURIComponent(url);
    obj._endpoint = obj._domainRoot + '/account/callbacks/update_intl_pref.php';
    
    
    obj.storeIntl = function (couId, lang, currency) {
        var reqData = {fCOUID : couId, fLang : lang, fCurrency : currency};
        try {
            obj._$postReq(reqData);
        } catch (ex) {
            reqData.fRedirect = window.location;
            var q = [];
            for ( p in reqData ) {
                q.push(p + "=" + reqData[p]);
            }
            window.location = obj._url + '?' + q.join("&");
        }
    };
    
    obj._$postReq = function ( reqData ) {
        $jq.ajax({cache: false, data: reqData, dataType: 'json', type: 'POST', url: obj._endpoint, success: obj._updatePage});
    };
    
    obj._updatePage = function (response) {
        if (response.ok == 1) {
            if (obj._url != null) {
                window.location = obj._url;
            } else {
                window.location.reload(true);
            }
        } else {
            $jq('#regionSelection').html( response.message );
        }
    };
    
    obj._init = function ( ) {
        // strip out language code from URL
        if (obj._url.match('^/[a-z][a-z]/(.)*')) {
            obj._url = obj._url.substring(3);
        } else if (obj._url.match('^/[a-z][a-z]$')) {
            obj._url = '/';
        } else if (obj._url.match('^/browse/search.php(.)*')) {
            // add random number to prevent caching on search page
            if (obj._url.match('\\?')) {
                obj._url += '&' + Math.random();
            } else {
                obj._url += '?' + Math.random();
            }
        }
    };
    
    obj._init();
    return obj;
}