/**
 * WJAnimation is the base animation class for Javascript
 *
 * Read the September 3th Blog for more information
 *
 * @since Wed Sep 03 2008
 * @revision $Revision$
 * @author Ron Rademaker
 **/

var WJAnimation = Class.create({
	/**
	 * initialize
	 *
	 * Creates a new animation for container
	 *
	 * @since Wed Sep 03 2008
	 * @access public
	 * @param htmlelement container
	 * @param object options 
	 * @return void
	 **/
	initialize: function(container, options) {
		this.container = $(container);
		this.initAnimation(options);
	},

	/**
	 * initAnimation
	 *
	 * Sets the options for the animation (extend for real implementations)
	 *
	 * @since Wed Sep 03 2008
	 * @access public
	 * @param object options
	 * @return void
	 **/
	initAnimation: function(options) {
		var defaultSpeed = 5;
		var defaultSmoothness = 50; // 20 steps per second
		this.options = options || {};

		this.options.speed = this.options.speed || defaultSpeed;
		this.options.smoothness = this.options.smoothness || defaultSmoothness;
	},
	
	/**
	 * animate
	 *
	 * Starts the animation
	 *
	 * @since Wed Sep 03 2008
	 * @access public
	 * @param object goal
	 * @return void
	 **/
	animate: function(goal) {
		clearInterval(this.animating);
		this.goal = goal;
		this.loc = {x: this.container.offsetLeft, y: this.container.offsetTop};
		this._calculateSpeed();
		this.animating = this.move.bind(this).repeat(this.options.smoothness / 1000);
	},

	/**
	 * _calculateSpeed
	 *
	 * Calculates xspeed and yspeed for the current goal
	 *
	 * @since Wed Sep 03 2008
	 * @access public
	 * @return void
	 **/
	_calculateSpeed: function() {
		var angle = Math.atan( (this.loc.x - this.goal.x) / (this.loc.y - this.goal.y) );
		this.options.xspeed = this.options.speed * Math.sin(angle);
		this.options.yspeed = this.options.speed * Math.cos(angle);
	},

	/**
	 * move
	 *
	 * Moves the container to the new location
	 *
	 * @since Wed Sep 03 2008
	 * @access public
	 * @return void
	 **/
	move: function() {
		this.loc = this.getLocation(this.loc);

		var done = (this.loc.x == this.goal.x) && (this.loc.y == this.goal.y);

		this.container.setStyle({"left": this.loc.x + "px", "top": this.loc.y + "px"});

		if (typeof(this.options.onAnimateStep) == "function") {
			this.options.onAnimateStep(this.loc, this.goal);
		}
		
		if (done) {
			clearInterval(this.animating);
			if (typeof(this.options.onEndAnimate) == "function") {
				this.options.onEndAnimate(this.goal);
			}
		}
	},

	/**
	 * stop
	 *
	 * Stops the current animation
	 *
	 * @since Tue Sep 09 2008
	 * @access public
	 * @return void
	 **/
	stop: function() {
		clearInterval(this.animating);
	},

	/**
	 * getLocation
	 *
	 * Calculates and returns the new position of the animation
	 *
	 * @since Wed Sep 03 2008
	 * @access public
	 * @param object cur
	 * @return void
	 **/
	getLocation: function(cur) {
		var newloc = {x: cur.x + this.options.xspeed, y: cur.y + this.options.yspeed};
		var diffs = {ox: (cur.x - this.goal.x), x: (newloc.x - this.goal.x), oy: (cur.y - this.goal.y), y: (newloc.y - this.goal.y)};
		if ( (this._getSign(diffs.ox) != this._getSign(diffs.x) ) ) {
			newloc.x = this.goal.x;
		}
		if ( (this._getSign(diffs.oy) != this._getSign(diffs.y) ) ) {
			newloc.y = this.goal.y;
		}
		return newloc;
	},

	/**
	 * _getSign
	 *
	 * Gets the sign for the passed integer
	 *
	 * @since Wed Sep 03 2008
	 * @access public 
	 * @param integer val
	 * @return integer
	 **/
	_getSign: function(val) {
		return (val > 0) ? 1 : ( (val < 0) ? -1 : 0);
	}

});
/**
 * WJSlowingAnimation is a WJAnimation implementation the starts fast and then slows down
 * WJAnimation is also a type 1 animation
 *
 * @since Wed Sep 03 2008
 * @author Ron Rademaker
 **/

var WJSlowingAnimation = Class.create(WJAnimation, {
	/**
	 * _calculateSpeed
	 *
	 * Slowing uses the same speed for x and y
	 *
	 * @since Wed Sep 03 2008
	 * @access public
	 * @return void
	 **/
	_calculateSpeed: function() {
		this.options.xspeed = this.options.speed;
		this.options.yspeed = this.options.speed;
	},
	
	/**
	 * initAnimation
	 *
	 * Adds the snap options, when the moving element comes with snap pixels of the goal, it will be snapped to the goal
	 *
	 * @since Wed Sep 03 2008
	 * @access public
	 * @param object options
	 * @return void
	 **/
	initAnimation: function($super, options) {
		$super(options);
		var defaultSnap = 0.5;

		this.options.snap = this.options.snap || defaultSnap;
	},

	/**
	 * getLocation
	 *
	 * Calculates and returns the new position of the animation
	 *
	 * @since Wed Sep 03 2008
	 * @access public
	 * @param object cur
	 * @return void
	 **/
	getLocation: function(cur) {
		var newloc = {x: cur.x + ( (this.goal.x - cur.x) / this.options.speed), y: cur.y + ( (this.goal.y - cur.y) / this.options.speed)};
		if (Math.abs(this.goal.x - cur.x) < this.options.snap) {
			newloc.x = this.goal.x;
		}
		if (Math.abs(this.goal.y - cur.y) < this.options.snap) {
			newloc.y = this.goal.y;
		}
		return newloc;
	}
});
/**
 * WJSlowingConstantAnimation is a WJConstantAnimation that will use a constant speed the last X pixels
 *
 * @since Wed Sep 03 2008
 * @author Ron Rademaker
 **/

var WJSlowingConstantAnimation = Class.create(WJSlowingAnimation, {
	/**
	 * initAnimation
	 *
	 * Adds the constant options, when the moving element comes with constant pixels of the goal, the speed will become constant
	 *
	 * @since Wed Sep 03 2008
	 * @access public
	 * @param object options
	 * @return void
	 **/
	initAnimation: function($super, options) {
		$super(options);
		var defaultConstant = 25;

		this.options.constfrom = this.options.constfrom || defaultConstant;
	},
	
	/**
	 * getLocation
	 *
	 * Calculates and returns the new position of the animation
	 *
	 * @since Wed Sep 03 2008
	 * @access public
	 * @param object cur
	 * @return void
	 **/
	getLocation: function(cur) {
		this.xspeed = Math.abs(this.goal.x - cur.x) < this.options.constfrom ? this.xspeed : (this.goal.x - cur.x) / this.options.speed;
		this.yspeed = Math.abs(this.goal.y - cur.y) < this.options.constfrom ? this.yspeed : (this.goal.y - cur.y) / this.options.speed;
		var newloc = {x: cur.x + this.xspeed, y: cur.y + this.yspeed};
		if ( (Math.abs(this.goal.x - cur.x) < this.options.snap) || (this._getSign(this.goal.x - cur.x) != this._getSign(this.goal.x - newloc.x) ) ) {
			newloc.x = this.goal.x;
		}
		if ( (Math.abs(this.goal.y - cur.y) < this.options.snap) || (this._getSign(this.goal.y - cur.y) != this._getSign(this.goal.y - newloc.y) ) ) {
			newloc.y = this.goal.y;
		}
		return newloc;
	}

});
/**
 * WJBoursinAnimation is the animation class for boursin, handles animations in the top
 *
 * @since Tue Sep 02 2008
 * @author Ron Rademaker
 **/

var WJBoursinAnimation = Class.create(WJSlowingConstantAnimation, {
	/**
	 * initialize
	 *
	 * Creates the new main animation for container
	 *
	 * @since Wed Sep 03 2008
	 * @access public
	 * @param htmlelement container
	 * @return void
	 **/
	initialize: function($super, container, options) {
		var options = options || { };
		options = $H(options).merge({snap: 1, constfrom: 25, speed: 5, smoothness: 50}).toObject();
		$super(container, options);
		this.x = this.options.size * -1;
		this.goal = {x: $(container).offsetLeft, y: $(container).offsetTop};
		this.pause = false;
	},

	/**
	 * animate
	 *
	 * Starts the animation
	 *
	 * @since Tue Sep 02 2008
	 * @access public
	 * @param integer direction
	 * @return void
	 **/
	animate: function($super, direction) {
		if (this.pause) {
			// do nothing, maybe add some "hold on, we're waiting for the recipe to load" message
		}
		else {
			var direction = direction || 1;
			if (direction == -1) {
				pageTracker._trackPageview("/animatie-vorige-recept.html");
			}
			else if (direction == 1) {
				pageTracker._trackPageview("/animatie-volgende-recept.html");
			}
			this.goal.x += (direction * this.options.size);
			$super(this.goal);
		}
	},

	/**
	 * fixLocation
	 *
	 * Fixes the current position after adding recipes (x and y will change)
	 *
	 * @since Wed Sep 03 2008
	 * @access public
	 * @param boolean loc
	 * @return void
	 **/
	fixLocation: function(loc) {
		if (!loc) { // only needed when prepending
			this.goal.x -= this.options.size; // fixes next animation
			this.container.setStyle({"left": this.goal.x + "px"}); // fixes current position
		}
	},

	/**
	 * initAnimation
	 *
	 * Calculates the variables for the animation
	 *
	 * @since Tue Sep 02 2008
	 * @access public
	 * @param object options
	 * @return void
	 **/
	initAnimation: function($super, options) {
		$super(options);
		var defaultSize = 1010;

		this.options.size = this.options.size || defaultSize;
	}
});
/**
 * WJBoursinRecipes manages the loaded recipes in the top bar
 *
 * @since Mon Sep 08 2008
 * @author Ron Rademaker
 **/

var WJBoursinRecipes = Class.create({
	/**
	 * initialize
	 *
	 * Initializes a new WJBoursinRecipes
	 *
	 * @since Mon Sep 08 2008
	 * @access public
	 * @param htmlelement container
	 * @param integer divsize
	 * @param object boursintop
	 * @return void
	 **/
	initialize: function(container, divsize, boursintop) {
		this.container = container;
		this.divsize = divsize;
		this.boursintop = boursintop;
		this.recipes = new Array();
		this.currecipe = -1;
		this.spin = new WJSpin();
		this._triedRecipe = 0;
		this.url = new WJUrl({ct: "wmdynamic", dt: "toprecipes", module: "Wmrecipeboursin", mode: "top", offset: 0, count: 3, "wmtrigger[]": ["requestufts"]});
	},

	/**
	 * loadRecipes
	 *
	 * Loads new recipes into the container, emptying it if full
	 * Animates to the new content if there was content, this is done by:
	 * - Removing everything right of the current recipe
	 * - Loading the new selection to the right of the current recipe
	 * - Scroll to the right
	 * - Remove everything to the left
	 * - Load a new recipe at the left
	 *
	 * @since Mon Sep 08 2008
	 * @access public
	 * @param object query
	 * @return void
	 **/
	loadRecipes: function(query, category, collection, kind, course, persons, preperation ,product, sortby) {
		this.registerLoadedRecipes();
		if (this.recipes.length == 0) {
			this._loadRecipes(query, category, collection, kind, course, persons, preperation ,product, sortby);
		}
		else {
			this._replaceRecipes(query, category, collection);
		}
	},

	/**
	 * _loadRecipes
	 *
	 * Loads the initial recipes
	 *
	 * @since Mon Sep 08 2008
	 * @access protected
	 * @param object query
	 * @return void
	 **/
	_loadRecipes: function(query, category, collection, kind, course, persons, preperation ,product, sortby) {
		this.container.setStyle({"position": "absolute", "width": (3 * this.divsize) + "px", "left": (-1 * this.divsize) + "px"});
		
		this.url.addParameter("collection", collection);
		
		this.spin.content(this.url, [this.container, this.boursintop.createMainAnimation.bind(this.boursintop), this.boursintop.activateNavigation.bind(this.boursintop), this.boursintop.loadHeaders.bind(this.boursintop), function() { this.boursintop.started = true;}.bind(this) ]);
		this.lastLeftOffset = 0;
		this.lastRightOffset = 2;
	},

	/**
	 * registerLoadedRecipes
	 *
	 * Registers the currently loaded recipes with this class and marks the visual one
	 *
	 * @since Mon Sep 08 2008
	 * @access public
	 * @return void
	 **/
	registerLoadedRecipes: function() {
		var formobj = $(document.loadedrecipes).serialize(true);
		if (formobj.loadedid) {
			this.recipes = formobj.loadedid;
			var recipeindex = (-1 * this.container.offsetLeft) / this.divsize;
			this.currecipe = parseInt(this.recipes[recipeindex]);
		}
	},

	/**
	 * showRecipe
	 *
	 * Uses the boursintop animation to scroll to the passed recipes, loads it if it's not yet present
	 *
	 * @since Mon Sep 08 2008
	 * @access public
	 * @param integer id
	 * @param function callback
	 * @param float delay
	 * @return void
	 **/
	showRecipe: function(id, callback, delay) {
		this.registerLoadedRecipes();
		$$(".pos_viewrecipe").each(function(s) { $(s).show(); });
		$$(".pos_recipe" + id).each(function(s) { $(s).hide(); });

		var goalloc = -1;
		var curloc = -1;
		for (var i = 0; (i < this.recipes.length) && (goalloc == -1 || curloc == -1); i++) {
			if (this.recipes[i] == id) {
				goalloc = i;
			}
			if (this.recipes[i] == this.currecipe) {
				curloc = i;
			}
		}
		if (goalloc == -1 && (this._triedRecipe < 3) ) {
			this._triedRecipe++;
			this._loadRecipe(id, true, callback, delay);
		}
		else if (curloc != goalloc) {
			this.boursintop.mainAnimation.animate(curloc - goalloc);
			callback.delay(delay);
		}
		else {
			callback.delay(delay);
		}
	},

	/**
	 * getCurrentIndex
	 *
	 * Gets the index of the current recipe
	 *
	 * @since Mon Sep 08 2008
	 * @access public
	 * @return integer
	 **/
	getCurrentIndex: function() {
		this.registerLoadedRecipes();
		for (var i = 0; i < this.recipes.length; i++) {
			if (this.recipes[i] == this.currecipe) {
				return i;
			}
		}
	},

	/**
	 * _loadRecipe
	 *
	 * Loads the recipe with id id and optionally scrolls to that recipe
	 *
	 * @since Mon Sep 08 2008
	 * @access protected
	 * @param integer id
	 * @param boolean scroll
	 * @param function callback
	 * @param float delay
	 * @return void
	 **/
	_loadRecipe: function(id, scroll, callback, delay) {
		this.url.addParameter("id", id);
		this.container.ajaxUpdateType = "appendChild";
		this.container.setStyle({"width": (this.container.getWidth() + this.divsize) + "px"});

		var callbacks = [this.container, function(s) { this.container.innerHTML.evalScripts(); }.bind(this), this.boursintop.activateNavigation.bind(this.boursintop), this.boursintop.loadHeaders.bind(this.boursintop)];
		if (scroll) {
			callbacks.push(this.showRecipe.bind(this, id, callback, delay) );
		}
		this.spin.content(this.url, callbacks);

		this.url.deleteParameter("id");
	},

	/**
	 * addRecipe
	 *
	 * Adds a recipe to the left or right of the container div
	 *
	 * @since Wed Sep 03 2008
	 * @access public
	 * @param boolean loc - false: left, true: right
	 * @return void
	 **/
	addRecipe: function(loc) {
		this.url.addParameter("count", 1);

		if (loc) {
			this.lastRightOffset++;
			this.url.addParameter("offset", this.lastRightOffset);
			this.container.ajaxUpdateType = "appendChild";
		}
		else {
			this.lastLeftOffset--;
			this.url.addParameter("offset", this.lastLeftOffset);
			this.container.ajaxUpdateType = "prependChild";
		}
		this.container.setStyle({"width": (this.container.getWidth() + this.divsize) + "px"});

		this.spin.content(this.url, [this.container, this.boursintop.mainAnimation.fixLocation.bind(this.boursintop.mainAnimation, loc), function(s) { this.container.innerHTML.evalScripts(); }.bind(this), this.boursintop.activateNavigation.bind(this.boursintop), this.boursintop.loadHeaders.bind(this.boursintop)]);
	},

	/**
	 * _clearRight
	 *
	 * Clears all recipes currently right of the current recipe
	 *
	 * @since Mon Sep 08 2008
	 * @access protected
	 * @return void
	 **/
	_clearRight: function() {
		this.registerLoadedRecipes();
		var loadedRecipes = this.container.getElementsByClassName("pos_toprecipe");
		var removing = false;
		for (var i = 0; i < loadedRecipes.length; i++) {
			var recipe = loadedRecipes[i].getElementsByTagName("input")[0].value;
			if (removing) {
				loadedRecipes[i].remove();
			}
			else if (recipe == this.currecipe) {
				removing = true;
			}
		}
	}
});

/**
 * loadRelatedRecipes
 *
 * Static method to load recipes related to the given id
 *
 * @since Wed Jul 22 2009
 * @access public
 * @return void
 **/
WJBoursinRecipes.loadRelatedRecipes = function(id, category, type, count) {
	var category = category || "boursin";
	var type = type || "product";
	var count = count || 1;
	var spin = new WJSpin();
	var url = new WJUrl({ct: "wmdynamic", dt: "relatedrecipes", module: "Wmrecipeboursin", id: id, mode: "relatedby" + type, category: category, type: "xml", count: count, "uft[]": "searchresult", "disableuft[]": "aeroplane"} );
	spin.content(url, [$$(".pos_relatedrecipes").first(), function() {
		var jslinks = $$(".pos_relatedrecipes").first().select(".javascript");
		if (jslinks) {
			boursinNavigator.rewriteJavascriptLinks(jslinks);
		}
	}] );
};
/**
 * WJBoursinTop is the class that glues all the parts of the boursin top together
 *
 * @since Wed Sep 03 2008
 * @author Ron Rademaker
 **/

var WJBoursinTop = Class.create({
	/**
	 * initialize
	 *
	 * Starts the boursin top
	 *
	 * @since Wed Sep 03 2008
	 * @access public
	 * @return void
	 **/
	initialize: function() {
		this.started = false;
		this.observing = false;
		this.searchOpen = false;
		this.testOverlayObserver = this.testOverlay.bindAsEventListener(this);
	},

	/**
	 * start
	 *
	 * Starts the top (allows for creation before the navigator)
	 *
	 * @since Mon Sep 08 2008
	 * @param htmlelement container
	 * @return void
	 **/
	start: function(container, category, collection, kind, course, persons, preperation ,product, sortby ) {
		var kind = kind || "";
		var course = course || "";
		var persons = persons || "";
		var preperation = preperation || "";
		var product = product || "";
		var sortby = sortby || "";
		
		if (category == null) {
			category = "boursin";
		}
		this.container = $(container);
		this.divsize = 1010;
		
		if (document.loadedrecipes) {
			this.recipes = new WJBoursinRecipes(this.container, this.divsize, this);
			this.recipes.loadRecipes({}, category, collection, kind, course, persons, preperation ,product, sortby); // loadRecipes calls loadSearchform
		}
	},

	/**
	 * showRecipe
	 *
	 * Shows a recipes (waits for the top to be started if it hasn't started yet)
	 *
	 * @since Mon Sep 08 2008
	 * @access public
	 * @param integer id
	 * @return void
	 **/
	showRecipe: function() {
		if (this.started) {
			$(window.document).stopObserving("mousemove", this.testOverlayObserver);
			this.observing = false;
			this.showOverlay();
			
			var id = "";
			
			if (this.searchOpen) {
				this.extendedSearchClick();
			}
			if ($("recipe_id") ) {
				id = $("recipe_id").getTextContent();
				this.recipes.showRecipe(id, this.hideOverlay.bind(this), 3);
			}
		}
		else {
			this.showRecipe.bind(this).delay(0.5);
		}
	},

	/**
	 * hideOverlay
	 *
	 * Hides the overlay on the image of the recipe
	 * Starts an observer to show the overlay on mouseover
	 *
	 * @since Mon Sep 08 2008
	 * @access public
	 * @return void
	 **/
	hideOverlay: function() {
		if (this.showAnimation) {
			this.showAnimation.stop();
		}
		if (this.hideAnimation) {
			this.hideAnimation.stop();
		}

		var elem = this.container.select(".pos_overlay")[this.recipes.getCurrentIndex()];
		if (!elem) {
			return;
		}
		this.hideAnimation = new WJSlowingConstantAnimation(elem, {snap: 20, constfrom: 50, speed: 20});
		this.hideAnimation.animate({x: elem.offsetLeft, y: 297});
		this.lastAction == "hide";

		if (!this.observing) {
			$(window.document).observe("mousemove", this.testOverlayObserver);
			this.observing = true;
		}
	 },

	/**
	 * testOverlay
	 *
	 * Shows or hides the overlay according to the current y position
	 *
	 * @since Tue Sep 09 2008
	 * @access public
	 * @param event e
	 * @return void
	 **/
	testOverlay: function(e) {
		if (this.searchOpen) {
			return;
		}
		if (e.pointerY() > this.container.getHeight() ) {
			if (!this.lastAction != "hide") {
				this.hideOverlay();
			}
		}
		else {
			if (!this.lastAction != "show") {
				this.showOverlay();
			}
		}
	 },

	 /**
	  * showOverlay
	  *
	  * Displays a hidden overlay
	  *
	  * @since Mon Sep 08 2008
	  * @access public
	  * @param event e
	  * @return void
	  **/
	 showOverlay: function(e) {
		if (this.hideAnimation) {
			this.hideAnimation.stop();
		}
		if (this.showAnimation) {
			this.showAnimation.stop();
		}

		this.lastAction == "show";
		var elem = this.container.select(".pos_overlay")[this.recipes.getCurrentIndex()];
		if (!elem) {
			return;
		}
		this.showAnimation = new WJSlowingConstantAnimation(elem, {snap: 1, constfrom: 50, speed: 10});
		this.showAnimation.animate({x: elem.offsetLeft, y: 0});

	 },

	/**
	 * activateNavigation
	 *
	 * Activates navigation on buttons in the loaded container
	 *
	 * @since Tue Sep 02 2008
	 * @access public
	 * @param document response
	 * @return void
	 **/
	activateNavigation: function(response) {
		$A(this.container.select(".pos_nextrecipe") ).each(function(s) {
			$(s).observe("click", this.mainAnimation.animate.bind(this.mainAnimation, -1) );
			$(s).observe("mouseover", function() { $(this.getElementsByTagName("img").item(0) ).src = $(this.getElementsByTagName("img").item(0) ).src.replace("pijlnaarrechts.", "pijlnaarrechtson."); }.bind(s) );
			$(s).observe("mouseout", function() { $(this.getElementsByTagName("img").item(0) ).src = $(this.getElementsByTagName("img").item(0) ).src.replace("pijlnaarrechtson.", "pijlnaarrechts."); }.bind(s) );
		}.bind(this) );
		$A(this.container.select(".pos_previousrecipe") ).each(function(s) {
			$(s).observe("click", this.mainAnimation.animate.bind(this.mainAnimation, 1) );
			$(s).observe("mouseover", function() { $(this.getElementsByTagName("img").item(0) ).src = $(this.getElementsByTagName("img").item(0) ).src.replace("pijlnaarlinks.", "pijlnaarlinkson."); }.bind(s) );
			$(s).observe("mouseout", function() { $(this.getElementsByTagName("img").item(0) ).src = $(this.getElementsByTagName("img").item(0) ).src.replace("pijlnaarlinkson.", "pijlnaarlinks."); }.bind(s) );
		}.bind(this) );
		$A(this.container.select(".pos_viewrecipe") ).each(function(s) {
			$(s).observe("click", function() { document.location.href = $(s).getElementsByTagName("input")[0].value}.bind(s) );
			//	$(s).observe("click", function() { boursinNavigator.navigateTo(this.firstChild.value); }.bind(s) );
		});
	},

	/**
	 * extendedSearchClick
	 *
	 * Opens or closes the extended search form
	 *
	 * @since Wed Sep 03 2008
	 * @access public
	 * @param event e
	 * @return void
	 **/
	extendedSearchClick: function(e) {
		var elem = $("quicksearch");
		if (!elem.hasClassName("pos_open") ) {
			pageTracker._trackPageview("/open-uitgebreid-zoeken.html");
			this.searchOpen = true;
			elem.addClassName("pos_open stl_open");
			this.oldY = elem.offsetTop;
			if (this.recipes) {
				var animation = new WJSlowingConstantAnimation(elem, {snap: 10, constfrom: 30, onEndAnimate: this.switchButton.bind(this, elem.select(".pos_extendedsearchbutton"), "/images/receptenzoekeromlaag.gif"), onAnimateStep: this.resizeInformation.bind(this, elem, true)});
				animation.animate({x: elem.offsetLeft, y: 141});
			}
			else {
				var animation = new WJSlowingConstantAnimation(elem, {snap: 10, constfrom: 30, onEndAnimate: this.switchButton.bind(this, elem.select(".pos_extendedsearchbutton"), "/images/receptenzoekeromhoog.gif"), onAnimateStep: this.resizeRecipeSearch.bind(this, elem, true)});
				animation.animate({x: elem.offsetLeft, y: 413});
			}
		}
		else {
			this.searchOpen = false;
			pageTracker._trackPageview("/sluiten-uitgebreid-zoeken.html");
			elem.removeClassName("pos_open stl_open");
			if (this.recipes) {
				var animation = new WJSlowingConstantAnimation(elem, {snap: 10, constfrom: 30, onEndAnimate: this.switchButton.bind(this, elem.select(".pos_extendedsearchbutton"), "/images/receptenzoekeromhoog.gif"), onAnimateStep: this.resizeInformation.bind(this, elem, false)});
				animation.animate({x: elem.offsetLeft, y: this.oldY});
			}
			else {
				var animation = new WJSlowingConstantAnimation(elem, {snap: 10, constfrom: 30, onEndAnimate: this.switchButton.bind(this, elem.select(".pos_extendedsearchbutton"), "/images/receptenzoekeromlaag.gif"), onAnimateStep: this.resizeRecipeSearch.bind(this, elem, true)});
				animation.animate({x: elem.offsetLeft, y: this.oldY});
			}
		}
	},

	/**
	 * resizeRecipeSearch
	 *
	 * Resizes the recipesearch div as the search form becomes visible
	 *
	 * @since Thu Sep 11 2008
	 * @access public
	 * @param htmlelement search
	 * @param boolean opening
	 * @param object loc
	 * @param object goal
	 * @return void
	 **/
	resizeRecipeSearch: function(search, opening, loc, goal) {
		var div = search.up();
		var flowdiv = $$(".pos_recipesearchflow")[0];
		var flowdivheight = flowdiv.getHeight() + (loc.y + 56) - div.getHeight();
		div.setStyle({"height": (loc.y + 56) + "px"});
		flowdiv.setStyle({"height": flowdivheight + "px"});

		$A(div.select(".pos_layer") ).each(function(opening, s) {
			if (opening) {
				$(s).setStyle({"top": ( (loc.y - this.oldY + 56) * -1) + "px", "height": (loc.y - this.oldY) + "px"});
			}
			else {
				$(s).setStyle({"top": ( (loc.y - this.oldY) * -1) + "px", "height": (loc.y - this.oldY) + "px"});
			}
		}.bind(this, opening) );
	},

	/**
	 * resizeInformation
	 *
	 * Resizes the information div to make it look like the search bar scrolls over it
	 *
	 * @since Wed Sep 03 2008
	 * @access public
	 * @param htmlelement elem
	 * @param boolean opening
	 * @param object loc
	 * @param object goal
	 * @return void
	 **/
	resizeInformation: function(elem, opening, loc, goal) {
		if (opening) {
			$A($(elem).up().select(".pos_information") ).each(function(s) {
				$(s).setStyle({"height": Math.max(0, (loc.y - goal.y - 6) ) + "px"});
			});
			if (loc.y <= 300) {
				$$(".pos_viewrecipe").each(function(s) { $(s).hide(); });
			}
			if (loc.y <= 190) {
				$$(".pos_previousrecipe").each(function(s) { $(s).hide(); });
			}
			if (loc.y <= 160) {
				$$(".pos_nextrecipe").each(function(s) { $(s).hide(); });
			}
		}
		else {
			$A($(elem).up().select(".pos_information") ).each(function(s) {
				$(s).setStyle({"height": (goal.y - 141 - Math.abs(goal.y - loc.y) ) + "px"});
			});
			if (loc.y >= 380) {
				$$(".pos_viewrecipe").each(function(s) { $(s).show(); });
			}
			if (loc.y >= 210) {
				$$(".pos_previousrecipe").each(function(s) { $(s).show(); });
			}
			if (loc.y >= 190) {
				$$(".pos_nextrecipe").each(function(s) { $(s).show(); });
			}
		}
	},

	/**
	 * switchButton
	 *
	 * Loads a new button into the passed div
	 *
	 * @since Wed Sep 03 2008
	 * @access public
	 * @param array divs
	 * @param string url
	 * @return void
	 **/
	switchButton: function(divs, url) {
		$A(divs).invoke("select", "img").flatten().each(function(i) { i.src = url; });
		$$(".pos_hr").each(function(s) { $(s).toggle(); });
		$$(".pos_extendedsearchlink").each(function(s) { $(s).toggle(); });
	},

	/**
	 * loadHeaders
	 *
	 * Rewrites headers to images
	 *
	 * @since Tue Sep 02 2008
	 * @access public
	 * @param document response
	 * @return void
	 **/
	loadHeaders: function(response) {
		this.showDelimeters.bind(this).delay(0.5);
	},

	/**
	 * showDelimeters
	 *
	 * Shows the lines between the h3 elements, called after the delay to allow the images to load
	 *
	 * @since Fri Sep 05 2008
	 * @access public
	 * @return void
	 **/
	showDelimeters: function() {
		$A(this.container.select(".pos_deli") ).each(function(s) {
			$(s).setStyle({"display": "inline"});
		});
	},

	/**
	 * checkNewRecipe
	 *
	 * Checks if the recipes is approaching the div borders and loads new recipes if so
	 *
	 * @since Wed Sep 03 2008
	 * @access public
	 * @param object loc
	 * @return void
	 **/
	checkNewRecipe: function(loc) {
		if (loc.x == 0) {
			this.recipes.addRecipe(false);
		}
		if (loc.x == (-1 * (this.container.getWidth() - this.divsize ) ) ) {
			this.recipes.addRecipe(true);
		}
	},

	/**
	 * doQuickSearch
	 *
	 * Performs a quick search based on a keyword
	 *
	 * @since Mon Sep 08 2008
	 * @access public
	 * @param event e
	 * @return void
	 **/
	doQuickSearch: function(e) {
		boursinNavigator.navigateTo({ct: "wmdynamic", dt: "searchresult", module: "Wmrecipeboursin", mode: "dosearch", count: "10", category: "boursin", type: "xml", keyword: $("trefwoord").value, "wmtrigger[]": ["requestufts"]});
	},

	/**
	 * createMainAnimation
	 *
	 * Creates the main animation
	 *
	 * @since Wed Sep 03 2008
	 * @access public
	 * @return void
	 **/
	createMainAnimation: function() {
		this.mainAnimation = new WJBoursinAnimation(this.container, {size: this.divsize, onEndAnimate: this.checkNewRecipe.bind(this)} );
	}
});

var animation = new WJBoursinTop();
/**
 * SWFLoader is the javascript loader class for flash embedding
 * Launches the expressinstall when the user doesn't have the required flash version installed
 *
 * This class also handles communication between the Flash movie and the JavaScript class
 *
 * Changelog
 * ---------
 *
 * Niels Nijens - Mon Nov 10 2008
 * --------------------------------
 * - Added Debug interface for the new Debug class
 *
 * Niels Nijens - Thu Nov 06 2008
 * --------------------------------
 * - Added deeplinking
 *
 * Niels Nijens - Mon May 19 2008
 * --------------------------------
 * - Fixed version retrieval for Flash Player 10 beta
 *
 * Niels Nijens - Tue Apr 01 2008
 * --------------------------------
 * - Changed required version from 9.0.47 to 9.0.115 due to some flash functionalities not working
 *
 * Niels Nijens - Mon Mar 10 2008
 * --------------------------------
 * - Changed required version from 9.0.28 to 9.0.47 due to components bug
 *
 * Niels Nijens - Fri Nov 16 2007
 * --------------------------------
 * - Changed SWFCall(); so it will run registered callback functions
 *
 * Niels Nijens - Mon Oct 22 2007
 * --------------------------------
 * - Added unload function
 * - Added timeout function to unload SWFObjects
 *
 * Niels Nijens - Mon Oct 22 2007
 * --------------------------------
 * - Made load() and loadSWFObject() the same, loadSWFObject() missed the expressinstall functionality
 * - Fixed bgcolor error
 *
 * Niels Nijens - Mon Oct 15 2007
 * --------------------------------
 * - Made SWFError(); able to call from Flash
 * - Added arguments to function calls from Flash (thanks to Giso)
 *
 * Niels Nijens - Fri Sep 21 2007
 * --------------------------------
 * - Added addFlashConfigVars(); for WMFlashConfig
 *
 * Niels Nijens - Mon Sep 17 2007
 * --------------------------------
 * - Added size check for the expressinstall
 * - Added addAlternateContentCallback();
 *
 * Niels Nijens - Fri Sep 14 2007
 * --------------------------------
 * - Added workaround (onunload) for IE video streaming bug in Flash Player
 * - Added default alternate content
 *
 * To do
 * ---------
 * -
 *
 * @since Thu Sep 13 2007
 * @author Niels Nijens (niels@connectholland.nl)
 **/
var SWFLoader = Class.create();
SWFLoader.prototype = {

	/**
	 * initialize
	 *
	 * Initialize a new SWFLoader
	 *
	 * @since initial
	 * @return void
	 **/
	initialize: function() {
		this.swfobjects = {};
		this.deeplinkListener = null;
		this.requiredVersion = {"major" : 9, "minor" : 0, "rev" : 115};
		this.expressInstallVersion = {"major" : 6, "minor" : 0, "rev" : 65};
		this.expressInstallSize = {"width" : 215, "height" : 138};
		window.SWFCall = this.SWFCall.bind(this);
		window.SWFError = this.SWFError.bind(this);
		window.SWFLogStart = this.SWFLogStart.bind(this);
		window.SWFLogEnd = this.SWFLogEnd.bind(this);
		window.SWFDebug = this.SWFDebug.bind(this);
		window.SWFDeeplink = this.SWFDeeplink.bind(this);
		this.initUnload();
	},

	/**
	 * load
	 *
	 * Adds a SWF file to the document when flash is available
	 * Otherwise adds alternate content to the element
	 *
	 * @since initial
	 * @param string element
	 * @param string swfname
	 * @param string swffile
	 * @param integer width
	 * @param integer height
	 * @param string bgcolor - transparent also works
	 * @param boolean wmode
	 * @param object swfvars
	 * @return void
	 **/
	load: function(element, swfname, swffile, width, height, bgcolor, swfvars) {
		swfobject = this.getSWFObject(element, swfname, swffile, width, height, bgcolor, swfvars);
		this.loadSWFObject(element, swfobject);
	},

	/**
	 * loadSWFObject
	 *
	 * Adds a SWF file to the document when flash is available
	 * Otherwise adds alternate content to the element
	 * (This function should be used for extends of SWFObject, for example MPlayerObject)
	 *
	 * @since initial
	 * @param string element
	 * @param object swfobject
	 * @return void
	 **/
	loadSWFObject: function(element, swfobject) {
		swfName = swfobject.getAttribute("swfname");
		swfWidth = swfobject.getAttribute("width");
		swfHeight = swfobject.getAttribute("height");
		
		this.checkDeeplinking(swfobject);
		
		if (this.checkPlayerVersion() ) {
			this.addSWFObject.bind(this).defer(element, swfobject);
		}
		else if (this.checkExpressInstallVersion() && this.checkExpressInstallSize(swfWidth, swfHeight) ) {
			installObject = this.getSWFObject(element, swfName, "/lib/swfloader/expressinstall.swf", swfWidth, swfHeight, "transparent", {"SWFContainer" : element, "MMredirectURL" : escape(window.location), "MMdoctitle" : document.title});
			this.addSWFObject.bind(this).defer(element, installObject);
		}
		else {
			this.addAlternateContent(element, swfWidth, swfHeight);
		}
	},

	/**
	 * checkPlayerVersion
	 *
	 * Returns true if the required flash version is available
	 *
	 * @since initial
	 * @return boolean
	 **/
	checkPlayerVersion: function() {
		installedVersion = this.getPlayerVersion();
		if (this.getVersionInt(installedVersion) >= this.getVersionInt(this.requiredVersion) ) {
			return true;
		}
		return false;
	},

	/**
	 * checkPlayerVersion
	 *
	 * Returns true if the flash version required for the express install is available
	 *
	 * @since initial
	 * @return boolean
	 **/
	checkExpressInstallVersion: function() {
		installedVersion = this.getPlayerVersion();
		if (this.getVersionInt(installedVersion) >= this.getVersionInt(this.expressInstallVersion) ) {
			return true;
		}
		return false;
	},

	/**
	 * checkExpressInstallSize
	 *
	 * Returns true if width and height are equal or greater than the required size for the express install
	 *
	 * @since initial
	 * @param integer width
	 * @param integer height
	 * @return boolean
	 **/
	checkExpressInstallSize: function(width, height) {
		if (width >= this.expressInstallSize.width && height >= this.expressInstallSize.height) {
			return true;
		}
		return false;
	},

	/**
	 * getPlayerVersion
	 *
	 * Returns the installed version of flash
	 *
	 * @since initial
	 * @return object
	 **/
	getPlayerVersion: function() {
		if (!this.installedVersion) {
			this.installedVersion = {"major" : 0, "minor" : 0, "rev" : 0};
			if(navigator.plugins && navigator.mimeTypes.length) {
				version = this.getPlayerVersionFF();
			} else {
				version = this.getPlayerVersionIE();
			}
			if (version) {
				var i = 0;
				for (property in this.installedVersion) {
					this.installedVersion[property] = version[i] != null ? parseInt(version[i]) : 0;
					i++;
				}
			}
		}
		return this.installedVersion;
	},

	/**
	 * getVersionInt
	 *
	 * Returns an Integer of the version
	 *
	 * @since Fri Sep 14 2007
	 * @param object version
	 * @return integer
	 **/
	getVersionInt: function(version) {
		return (version.major * Math.pow(1000, 2) ) + (version.minor * 1000) + version.rev;
	},

	/**
	 * getPlayerVersionFF
	 *
	 * Returns the Flash version for FireFox and other browsers
	 * Returns false if Flash isn't available
	 *
	 * @since initial
	 * @return mixed
	 **/
	getPlayerVersionFF: function() {
		var flashplayer = navigator.plugins["Shockwave Flash"];
		if(flashplayer && flashplayer.description) {
			var version = flashplayer.description.replace(/([a-zA-Z]|\s)+/, "").replace(/(\s+r|\s+b[0-9]+)/, ".").split(".");
			if (version[2] == "") {
				version[2] = "0";
			}
			
			return version;
		}
		return false;
	},

	/**
	 * getPlayerVersionIE
	 *
	 * Returns the Flash version for Internet Exploder browsers
	 * Returns false if Flash isn't available
	 *
	 * @since initial
	 * @return mixed
	 **/
	getPlayerVersionIE: function() {
		try {
			flashplayer = new ActiveXObject("ShockwaveFlash.ShockwaveFlash");
			flashplayer.AllowScriptAccess = "always";
			return flashplayer.GetVariable("$version").split(" ")[1].split(",");
		}
		catch(e) {
			return false;
		}
	},

	/**
	 * getSWFObject
	 *
	 * Adds a SWF to the swfobjects list and writes the SWFObject to the document
	 *
	 * @since Mon Oct 22 2007 (previously known as addSWF() )
	 * @param string element
	 * @param string swfname
	 * @param string swffile
	 * @param integer width
	 * @param integer height
	 * @param string bgcolor
	 * @param boolean wmode
	 * @param object swfvars
	 * @return SWFObject
	 **/
	getSWFObject: function(element, swfname, swffile, width, height, bgcolor, swfvars) {
		swfobject = new SWFObject(swfname, swffile, width, height, bgcolor.replace(/#/g, ""), swfvars);
		
		return swfobject;
	},

	/**
	 * addSWFObject
	 *
	 * Adds a SWF to the swfobjects list and writes the SWFObject to the document
	 *
	 * @since initial
	 * @param string element
	 * @param object swfobject
	 * @return void
	 **/
	addSWFObject: function(element, swfobject) {
		swfname = swfobject.getAttribute("swfname");
		this.swfobjects[swfname] = {};
		this.swfobjects[swfname]["element"] = $(element);
		this.swfobjects[swfname]["object"] = swfobject;
		this.swfobjects[swfname]["object"].write(element);
	},

	/**
	 * addAlternateContent
	 *
	 * Adds alternate content to the document where the Flash should have been
	 *
	 * @since initial
	 * @param string element
	 * @param integer width
	 * @param integer height
	 * @param string bgcolor
	 * @return void
	 **/
	addAlternateContent: function(element, width, height, bgcolor) {
		if ($(element) ) {
			Element.setStyle(element, {"width" : width, "height" : height, "text-align" : "center"});
			$(element).innerHTML = "<a class='swfloadergetflash' href='http://www.adobe.com/go/flashplayer' target='_blank'><img src='/lib/swfloader/images/getflashplayer.gif' border='0'/></a>";
			element.fire("swfloader:loaded-getflashplayer");
			return true;
		}
		return false;
	},

	/**
	 * addAlternateContentCallback
	 *
	 * Callback function for when the autoupdate fails or has been canceled by the user
	 *
	 * @since Mon Sep 17 2007
	 * @param object swf
	 * @return void
	 **/
	addAlternateContentCallback: function(element, width, height) {
		this.addAlternateContent(element, width, height);
	},
	
	/**
	 * unload
	 *
	 * Unloads a SWFObject from the document
	 *
	 * @since Wed Oct 31 2007
	 * @param string swfname
	 * @return void
	 **/
	unload: function(swfname) {
		this.processUnload.bind(this).defer(swfname);
	},
	
	/**
	 * processUnload
	 *
	 * Unloads a SWFObject from the document
	 *
	 * @since Wed Oct 31 2007
	 * @param string swfname
	 * @return void
	 **/
	processUnload: function(swfname) {
		divElement = this.getDivByName(swfname);
		if (divElement) {
			divElement.removeChild(divElement.getElementsByTagName("embed")[0]);
			this.swfobjects[swfname] = undefined;
		}
	},
	
	/**
	 * getSWFObjectByName
	 *
	 * Returns the SWFObject with name swfname if it exists
	 * Otherwise returns false
	 *
	 * @since initial
	 * @param string swfname
	 * @return mixed
	 **/
	getSWFObjectByName: function(swfname) {
		if (this.swfobjects[swfname]) {
			return this.swfobjects[swfname]["object"];
		}
		else {
			return false;
		}
	},

	/**
	 * getDivByName
	 *
	 * Returns the div element with name swfname if it exists
	 * Otherwise returns false
	 *
	 * @since initial
	 * @param string swfname
	 * @return mixed
	 **/
	getDivByName: function(swfname) {
		if (this.swfobjects[swfname]) {
			return this.swfobjects[swfname]["element"];
		}
		else {
			return false;
		}
	},

	/**
	 * SWFCall
	 *
	 * Javascript interface for calls from SWFs
	 *
	 * @since Thu Oct 11 2007
	 * @return mixed
	 **/
	SWFCall: function() {
		flashVars = $A(arguments);
		
		swfname = flashVars.shift();
		methodName = flashVars.shift();
		methodVars = flashVars;
		
		swfobject = this.getSWFObjectByName(swfname);
		if (swfobject) {
			callbackFunction = swfobject.getCallbackByMethod(methodName);
			if (typeof(callbackFunction["callback"]) == "function") {
				return callbackFunction["callback"].apply(swfobject, methodVars);
			}
			else if (typeof(swfobject[methodName]) == "function") {
				return swfobject[methodName].apply(swfobject, methodVars);
			}
			else {
				this.SWFError("Method " + methodName + " doesn't exist in object " + swfname + ".");
			}
		}
		else {
			this.SWFError("Object with name " + swfname + " doesn't exist.");
		}
	},
	
	/**
	 * SWFLogStart
	 *
	 * Starts time log
	 * (For developers only)
	 *
	 * @since Thu Mar 20 2007
	 * @param object debugInfo
	 * @return void
	 **/
	SWFLogStart: function(debugInfo) {
		if (console) {
			console.log("loading (" + debugInfo.id + "): " + debugInfo.url);
			console.time("response time (" + debugInfo.id + ")");
		}
	},
	
	/**
	 * SWFLogEnd
	 *
	 * Ends time log
	 * (For developers only)
	 *
	 * @since Thu Mar 20 2007
	 * @param object debugInfo
	 * @return void
	 **/
	SWFLogEnd: function(debugInfo) {
		if (console) {
			console.timeEnd("response time (" + debugInfo.id + ")");
			console.log(debugInfo);
		}
	},
	
	/**
	 * SWFError
	 *
	 * Prints an error in the console
	 * (For developers only)
	 *
	 * @since Thu Oct 11 2007
	 * @param string message
	 * @param boolean log
	 * @return void
	 **/
	SWFError: function(message, log) {
		if ( $("consolewindow") ) {
			if (!log) {
				message = "ERROR: " + message;
			}
			
			$("consolewindow").innerHTML += message + "<br/>";
			
			return;
		}
		if (console) {
			if (log) {
				console.log(message);
			}
			else {
				console.error("ERROR: " + message);
			}
			
			return;
		}
	},
	
	/**
	 * SWFDebug
	 *
	 * Displays Debug messages from the SWF
	 *
	 * @since Mon Nov 10 2008
	 * @return void
	 **/
	SWFDebug: function(level) {
		var args = $A(arguments);
		args.shift();
		if (typeof(console) != "undefined" && typeof(console.error) == "function" && typeof(console.warn) == "function" && typeof(console.log) == "function") {
			switch (level) {
				case 1:
					console.error.apply(console, args);
					break;
				case 2:
					console.warn.apply(console, args);
					break;
				default:
					console.log.apply(console, args);
					break;
			}
		}
		else {
			throw Error(args);
		}
	},
	
	/**
	 * checkDeeplink
	 *
	 * Checks if deeplinking should be enabled for swfobject
	 *
	 * @since Thu Nov 06 2008
	 * @param SWFObject swfobject
	 * @return void
	 **/
	checkDeeplinking: function(swfobject) {
		var swfvars = swfobject.getVariables();
		if (swfvars["deeplinking"] == true) {
			swfobject.deeplinking = true;
			if (this.deeplinkListener == null) {
				this.deeplinkListener = new PeriodicalExecuter(this.broadcastDeeplink.bind(this), 0.05);
			}
		}
	},
	
	/**
	 * SWFDeeplink
	 *
	 * Deeplink functionality for flash websites
	 *
	 * @since Thu Nov 06 2008
	 * @param string link
	 * @param string title
	 * @return void
	 **/
	SWFDeeplink: function(link, title) {
		document.location.hash = "#" + link;
		if (title != "") {
			document.title = title;
		}
	},
	
	/**
	 * broadcastDeeplink
	 *
	 * Deeplink listener. Broadcasts the deeplink to the SWFObject's that have deeplinking enabled
	 *
	 * @since Thu Nov 06 2008
	 * @param string link
	 * @param string title
	 * @return void
	 **/
	broadcastDeeplink: function() {
		var deeplink = document.location.hash.replace(/#/g, "");
		if (deeplink != "" && this.broadcastedDeeplink != deeplink) {
			this.broadcastedDeeplink = deeplink;
			
			for (var swfname in this.swfobjects) {
				if (this.swfobjects[swfname]["object"].deeplinking) {
					this.swfobjects[swfname]["object"].setDeeplink(deeplink);
				}
			}
		}
	},
	
	/**
	 * initUnload
	 *
	 * Adds an onunload event to the document
	 * This is needed for an IE video streaming bug in Flash Player
	 *
	 * See: http://blog.deconcept.com/2006/07/28/swfobject-143-released/
	 *
	 * @since Fri Sep 14 2007
	 * @return void
	 **/
	initUnload: function() {
		if (!window.opera && document.all) {
			__flash_unloadHandler = function(){};
			__flash_savedUnloadHandler = function(){};
			
			//window.attachEvent("onunload", this.cleanupSWFObjects);
		}
	},

	/**
	 * cleanupSWFObjects
	 *
	 * Cleans up all the object elements
	 *
	 * @since Fri Sep 14 2007
	 * @return void
	 **/
	cleanupSWFObjects: function() {
		if (window.opera || !document.all) {
			return;
		}

		var objects = document.getElementsByTagName("object");
		for (i = 0; i < objects.length; i++) {
			objects[i].style.display = 'none';
			for (var x in objects[i]) {
				if (typeof objects[i][x] == 'function') {
					objects[i][x] = function() { };
				}
			}
		}
	}
}

/**
 * SWFObject
 *
 * Changelog
 * ---------
 *
 * Niels Nijens - Fri Nov 16 2007
 * -------------------------------
 * - Added registerCallback(); and getCallbackByMethod(); to override function calls from flash
 *
 * Niels Nijens - Mon Nov 05 2007
 * -------------------------------
 * - Added getContainer(); to get the SWFObject's parent div
 *
 * Niels Nijens - Mon Oct 22 2007
 * -------------------------------
 * - Added methodExists(); for flash function calls
 *
 * Niels Nijens - Tue Sep 18 2007
 * -------------------------------
 * - Added getColor and getWmode to combine them into one variable bgcolor
 *
 * @since Thu Sep 13 2007
 * @author Niels Nijens (niels@connectholland.nl)
 **/
var SWFObject = Class.create();
SWFObject.prototype = {

	/**
	 * initialize
	 *
	 * Initialize a new SWFObject
	 *
	 * @since initial
	 * @param string swfname
	 * @param string swffile
	 * @param integer width
	 * @param integer height
	 * @param string bgcolor
	 * @param boolean wmode
	 * @param object swfvars
	 * @return void
	 **/
	initialize: function(swfname, swffile, width, height, bgcolor, swfvars) {
		this.deeplinking = false;
		this.registeredCallbacks = {};
		
		this.initAttributes({"swffile" : swffile, "swfname" : swfname, "width" : width, "height" : height});
		this.initParams({"quality" : "high", "menu" : "false", "scale" : "noscale", "AllowScriptAccess" : "always", "bgcolor" : this.getColor(bgcolor), "wmode" : this.getWMode(bgcolor)});
		this.initVariables(swfvars);
		this.addFlashConfigVars();
	},

	/**
	 * initAttributes
	 *
	 * Creates and Fills the attributes object with variables
	 * (Is used for basic variables like filepath and the name of the SWF)
	 *
	 * @since initial
	 * @param object attributes
	 * @return void
	 **/
	initAttributes: function(attributes) {
		this.attributes = {};

		for(property in attributes) {
			if (attributes[property]) {
				this.setAttribute(property, attributes[property]);
			}
		}
	},

	/**
	 * initParams
	 *
	 * Creates and Fills the params object with variables
	 * (Is used for basic variables like bgcolor and wmode)
	 *
	 * @since initial
	 * @param object params
	 * @return void
	 **/
	initParams: function(params) {
		this.params = {};

		for(property in params) {
			if (params[property]) {
				this.addParam(property, params[property]);
			}
		}
	},

	/**
	 * initVariables
	 *
	 * Creates and Fills the variables object with variables
	 * (Is used for variables to be send to flash)
	 *
	 * @since initial
	 * @param object variables
	 * @return void
	 **/
	initVariables: function(variables) {
		this.variables = {};

		for(property in variables) {
			if (variables[property]) {
				this.addVariable(property, variables[property]);
			}
		}
	},

	/**
	 * addFlashConfigVars
	 *
	 * Adds the variables required for WMFlashConfig
	 *
	 * @since Fri Sep 21 2007
	 * @return void
	 **/
	addFlashConfigVars: function() {
		this.addVariable("swfname", this.getAttribute("swffile").substr(this.getAttribute("swffile").lastIndexOf("/") + 1, this.getAttribute("swffile").lastIndexOf(".swf") - (this.getAttribute("swffile").lastIndexOf("/") + 1) ) );
		this.addVariable("swfpath", this.getAttribute("swffile").substr(0, this.getAttribute("swffile").lastIndexOf("/") + 1) );
	},

	/**
	 * getColor
	 *
	 * Returns the bgcolor
	 *
	 * @since Tue Sep 18 2007
	 * @param string bgcolor
	 * @return mixed
	 **/
	getColor: function(bgcolor) {
		if (bgcolor == "transparent") {
			return false;
		}
		return bgcolor;
	},

	/**
	 * getWMode
	 *
	 * Returns the wmode
	 *
	 * @since Tue Sep 18 2007
	 * @param string bgcolor
	 * @return mixed
	 **/
	getWMode: function(bgcolor) {
		if (bgcolor != "transparent") {
			return false;
		}
		return bgcolor;
	},

	/**
	 * setAttribute
	 *
	 * Adds a variable with name and value to the attributes object
	 *
	 * @since initial
	 * @param string name
	 * @param mixed value
	 * @return void
	 **/
	setAttribute: function(name, value){
		this.attributes[name] = value;
	},

	/**
	 * getAttribute
	 *
	 * Returns the value of an attribute by name
	 *
	 * @since initial
	 * @param string name
	 * @return mixed
	 **/
	getAttribute: function(name) {
		return this.attributes[name];
	},

	/**
	 * addParam
	 *
	 * Adds a variable with name and value to the params object
	 *
	 * @since initial
	 * @param string name
	 * @param mixed value
	 * @return void
	 **/
	addParam: function(name, value) {
		this.params[name] = value;
	},

	/**
	 * getParams
	 *
	 * Returns the params object
	 *
	 * @since initial
	 * @return object
	 **/
	getParams: function() {
		return this.params;
	},

	/**
	 * addVariable
	 *
	 * Adds a variable with name and value to the variables object
	 *
	 * @since initial
	 * @param string name
	 * @param mixed value
	 * @return void
	 **/
	addVariable: function(name, value){
		this.variables[name] = value;
	},

	/**
	 * getVariables
	 *
	 * Returns the variables object
	 *
	 * @since initial
	 * @return object
	 **/
	getVariables: function() {
		return this.variables;
	},

	/**
	 * getVariablePairs
	 *
	 * Returns the variables object as an array with strings
	 *
	 * @since initial
	 * @return array
	 **/
	getVariablePairs: function(){
		variables = this.getVariables();
		variablePairs = new Array();
		for(property in variables){
			variablePairs.push(property + "=" + variables[property]);
		}
		return variablePairs;
	},

	/**
	 * getSWFHTML
	 *
	 * Returns the HTML for the SWF
	 *
	 * @since initial
	 * @return string
	 **/
	getSWFHTML: function() {
		if (navigator.plugins && navigator.mimeTypes && navigator.mimeTypes.length) {
			this.addVariable("MMplayerType", "PlugIn");
			SWFNode = this.getSWFHTMLEmbed();
		} else {
			this.addVariable("MMplayerType", "ActiveX");
			SWFNode = this.getSWFHTMLObject();
		}
		return SWFNode;
	},

	/**
	 * getSWFHTMLEmbed
	 *
	 * Returns the HTML <embed> for the SWF
	 * (FireFox browsers)
	 *
	 * @since initial
	 * @return string
	 **/
	getSWFHTMLEmbed: function() {
		SWFNode = "<embed type='application/x-shockwave-flash' src='" + this.getAttribute("swffile") + "'";
		SWFNode += " width='" + this.getAttribute("width") + "' height='" + this.getAttribute("height") + "'";
		SWFNode += " id='" + this.getAttribute("swfname") + "' name='" + this.getAttribute("swfname") + "' ";
		SWFNode += this.getParamHTML(true);
		SWFNode += this.getVariableHTML(true);
		SWFNode += "/>";

		return SWFNode;
	},

	/**
	 * getSWFHTMLObject
	 *
	 * Returns the HTML <object> for the SWF
	 * (Internet Exploder browsers)
	 *
	 * @since initial
	 * @return string
	 **/
	getSWFHTMLObject: function() {
		SWFNode = "<object id='" + this.getAttribute("swfname") + "' classid='clsid:D27CDB6E-AE6D-11cf-96B8-444553540000'";
		SWFNode += " width='" + this.getAttribute("width") + "' height='" + this.getAttribute("height") + "'>";
		SWFNode += "<param name='movie' value='" + this.getAttribute("swffile") + "'/>";
		SWFNode += this.getParamHTML(false);
		SWFNode += this.getVariableHTML(false);
		SWFNode += "</object>";

		return SWFNode;
	},

	/**
	 * getParamHTML
	 *
	 * Returns the params as HTML string
	 *
	 * @since initial
	 * @param boolean embed
	 * @return string
	 **/
	getParamHTML: function(embed) {
		paramHTML = "";

		params = this.getParams();
		for(var property in params) {
			if (embed) {
				paramHTML += property + "='" + params[property] + "' ";
			}
			else {
				paramHTML += '<param name="'+ property +'" value="'+ params[property] +'" />';
			}
		}

		return paramHTML;
	},

	/**
	 * getVariableHTML
	 *
	 * Returns the variables as HTML string
	 *
	 * @since initial
	 * @param boolean embed
	 * @return string
	 **/
	getVariableHTML: function(embed) {
		variableHTML = "";

		variablestring = this.getVariablePairs().join("&");
		if (variablestring.length > 0) {
			if (embed) {
				variableHTML += "flashvars='" + variablestring + "'";
			}
			else {
				variableHTML += "<param name='flashvars' value='" + variablestring + "'/>";
			}
		}

		return variableHTML;
	},

	/**
	 * write
	 *
	 * Writes the SWF HTML to the document
	 * Returns true on success
	 *
	 * @since initial
	 * @return boolean
	 **/
	write: function(element) {
		if ($(element) ) {
			$(element).innerHTML = this.getSWFHTML();
			return true;
		}
		return false;
	},

	/**
	 * methodExists
	 *
	 * Returns if the method exists on the flash element
	 *
	 * @since Mon Oct 22 2007
	 * @param string methodName
	 * @return boolean
	 **/
	methodExists: function(methodName) {
		element = $(this.getAttribute("swfname") );
		if (element) {
			if (typeof(element[methodName]) == "function") {
				return true;
			}
		}
		return false;
	},
	
	/**
	 * registerCallback
	 *
	 * Registers a callback function to override the default function
	 *
	 * @since Fri Nov 16 2007
	 * @param string methodName
	 * @param function callbackFunction
	 * @return void
	 **/
	registerCallback: function(methodName, callbackFunction) {
		this.registeredCallbacks[methodName] = {};
		this.registeredCallbacks[methodName]["callback"] = callbackFunction;
	},
	
	/**
	 * getCallbackByMethod
	 *
	 * Returns the registered callback function
	 *
	 * @since Fri Nov 16 2007
	 * @param string methodName
	 * @return object
	 **/
	getCallbackByMethod: function(methodName) {
		if (this.registeredCallbacks[methodName] != undefined) {
			return this.registeredCallbacks[methodName];
		}
		return false;
	},
	
	/**
	 * getContainer
	 *
	 * Returns the parent (container) div of the SWFObject
	 *
	 * @since Mon Nov 05 2007
	 * @return mixed
	 **/
	getContainer: function() {
		return swfloader.getDivByName(this.getAttribute("swfname") );
	},
	
	/**
	 * setDeeplink
	 *
	 * Sets the deeplink
	 *
	 * @since Thu Nov 06 2008
	 * @param string link
	 * @return void
	 **/
	setDeeplink: function(link) {
		if (this.methodExists("setDeeplink") ) {
			$(this.getAttribute("swfname") ).setDeeplink(link);
		}
		else {
			this.setDeeplink.bind(this).delay(0.05, link);
		}
	}
}

var swfloader = new SWFLoader();
/**
 * MPlayerObject is the Flash Mediaplayer Object
 * Implements javascript communication with the Mediaplayer SWFObject
 *
 * Changelog
 * ---------
 *
 * Niels Nijens - Fri Apr 11 2008
 * -------------------------------
 * - Rewritten class for the new Mediaplayer version
 *
 * Niels Nijens - Mon Nov 05 2007
 * -------------------------------
 * - Copied from WmMediaPlayer.js
 * - Upgraded to ExternalInterface and SWFLoader / SWFObject
 *
 * To do
 * ---------
 * - Port seekPercentage(); and seek(); to Javascript
 *
 * @since Mon Nov 05 2007
 * @author Niels Nijens (niels@connectholland.nl)
 **/
var MPlayerObject = Class.create(SWFObject, {
	
	/**
	 * initialize
	 *
	 * Initialize a new GMapsObject
	 *
	 * @since initial
	 * @param string swfname
	 * @param string swffile
	 * @param integer width
	 * @param integer height
	 * @param string bgcolor
	 * @param boolean wmode
	 * @param object swfvars
	 * @return void
	 **/
	initialize: function($super, swfname, swffile, width, height, bgcolor, swfvars) {
		this.fullscreen = false;
		$super(swfname, swffile, width, height, bgcolor, swfvars);
	},
	
	/**
	 * setSize
	 *
	 * Sets the size of the player div
	 *
	 * @since initial
	 * @return void
	 **/
	setSize: function(width, height) {
		this.width = width;
		this.height = height;
		element = this.getContainer();
		if(element) {
			Element.setStyle(element.id, {width : this.width + "px", height : this.height + "px"});
		}
	},
	
	/**
	 * getVersion
	 *
	 * Returns the version of the mediaplayer
	 * (Returns -1 if the mediaplayer isn't instanciated yet)
	 *
	 * @since Fri Apr 11 2008
	 * @return string
	 **/
	getVersion: function() {
		if (this.methodExists("getVersion") ) {
			return $(this.getAttribute("swfname") ).getVersion();
		}
		return -1;
	},
	
	/**
	 * loadMedia
	 *
	 * Loads a file into the mediaplayer
	 *
	 * @since initial
	 * @param string file
	 * @param boolean play
	 * @return void
	 **/
	loadMedia: function(file, play) {
		if (this.methodExists("loadMedia") ) {
			if (file != null) {
				$(this.getAttribute("swfname") ).loadMedia(file, play);
			}
		}
		else {
			this.loadMedia.bind(this, file, play).delay(0.1);
		}
	},
	
	/**
	 * viewMedia
	 *
	 * Gets a playlist item by index and views it in the player
	 *
	 * @since Fri Apr 15 2008
	 * @access public
	 * @param integer index
	 * @return void
	 **/
	viewMedia: function(index) {
		if (this.methodExists("viewMedia") ) {
			$(this.getAttribute("swfname") ).viewMedia(index);
		}
		else {
			this.viewMedia.bind(this, index).delay(0.1)
		}
	},
	
	/**
	 * play
	 *
	 * Plays the media loaded into the Mediaplayer
	 *
	 * @since Fri Apr 11 2008 (previously known as playMedia)
	 * @return void
	 **/
	play: function() {
		if (this.methodExists("play") ) {
			$(this.getAttribute("swfname") ).play();
		}
		else {
			this.play.bind(this).delay(0.1);
		}
	},
	
	/**
	 * pause
	 *
	 * Pauses the media loaded into the Mediaplayer
	 *
	 * @since Fri Apr 11 2008 (previously known as pauseMedia)
	 * @return void
	 **/
	pause: function() {
		if (this.methodExists("pause") ) {
			$(this.getAttribute("swfname") ).pause();
		}
		else {
			this.pause.bind(this).delay(0.1);
		}
	},
	
	/**
	 * stop
	 *
	 * Stops the media loaded into the Mediaplayer
	 *
	 * @since Fri Apr 11 2008 (previously known as stopMedia)
	 * @return void
	 **/
	stop: function() {
		if (this.methodExists("stop") ) {
			$(this.getAttribute("swfname") ).stop();
		}
		else {
			this.stop.bind(this).delay(0.1);
		}
	},
	
	/**
	 * setVolume
	 *
	 * Sets the audio volume
	 *
	 * @since Fri Apr 11 2008
	 * @param integer volume
	 * @return void
	 **/
	setVolume:function(volume) {
		if (this.methodExists("volume") ) {
			$(this.getAttribute("swfname") ).setVolume(volume);
		}
		else {
			this.setVolume.bind(this, volume).delay(0.1);
		}
	},
	
	/**
	 * getVolume
	 *
	 * Returns the current audio volume
	 * (Returns -1 if the mediaplayer isn't instanciated yet)
	 *
	 * @since Fri Apr 11 2008
	 * @return integer
	 **/
	getVolume: function() {
		if (this.methodExists("getVolume") ) {
			return $(this.getAttribute("swfname") ).getVolume();
		}
		return -1;
	},
	
	/**
	 * toggleMute
	 *
	 * Toggles the audio on and off
	 *
	 * @since unknown
	 * @return void
	 **/
	toggleMute: function() {
		if (this.methodExists("toggleMute") ) {
			$(this.getAttribute("swfname") ).toggleMute();
		}
		else {
			this.toggleMute.bind(this).delay(0.1);
		}
	},
	
	/**
	 * getPosition
	 *
	 * Returns the current the position
	 * (Returns -1 if the mediaplayer isn't instanciated yet)
	 *
	 * @since Fri Apr 11 2008
	 * @return integer
	 **/
	getPosition: function() {
		if (this.methodExists("getPosition") ) {
			return $(this.getAttribute("swfname") ).getPosition();
		}
		return -1;
	},
	
	/**
	 * getPlaylist
	 *
	 * Returns the playlist of the mediaplayer
	 * (Returns -1 if the mediaplayer isn't instanciated yet)
	 *
	 * @since Fri Apr 11 2008
	 * @return array
	 **/
	getPlaylist: function() {
		if (this.methodExists("getPlaylist") ) {
			return $(this.getAttribute("swfname") ).getPlaylist();
		}
		return -1;
	},
	
	/**
	 * isPlaying
	 *
	 * Returns true if the Mediaplayer is playing
	 * (Returns -1 if the mediaplayer isn't instanciated yet)
	 *
	 * @since Fri Apr 11 2008
	 * @return boolean
	 **/
	isPlaying: function() {
		if (this.methodExists("getPlayerState") ) {
			return $(this.getAttribute("swfname") ).getPlayerState("playing");
		}
		return -1;
	},
	
	/**
	 * isPaused
	 *
	 * Returns true if the Mediaplayer is paused
	 * (Returns -1 if the mediaplayer isn't instanciated yet)
	 *
	 * @since Fri Apr 11 2008
	 * @return boolean
	 **/
	isPaused: function() {
		if (this.methodExists("getPlayerState") ) {
			return $(this.getAttribute("swfname") ).getPlayerState("paused");
		}
		return -1;
	},
	
	/**
	 * isStopped
	 *
	 * Returns true if the Mediaplayer is stopped
	 * (Returns -1 if the mediaplayer isn't instanciated yet)
	 *
	 * @since Fri Apr 11 2008
	 * @return boolean
	 **/
	isStopped: function() {
		if (this.methodExists("getPlayerState") ) {
			return $(this.getAttribute("swfname") ).getPlayerState("stopped");
		}
		return -1;
	},
	
	/**
	 * isMuted
	 *
	 * Returns true if the Mediaplayer is muted
	 * (Returns -1 if the mediaplayer isn't instanciated yet)
	 *
	 * @since Fri Apr 11 2008
	 * @return boolean
	 **/
	isMuted: function() {
		if (this.methodExists("getPlayerState") ) {
			return $(this.getAttribute("swfname") ).getPlayerState("stopped");
		}
		return -1;
	},
	
	/**
	 * toggleScreen
	 *
	 * Toggles the fullscreen mode on and off
	 *
	 * @since unknown
	 * @return void
	 **/
	toggleScreen: function() {
		if (!this.fullscreen) {
			this.fullscreen = true;
			this.setSize(this.width * 2, this.height * 2);
			this.element.className = this.element.className + " pos_fullscreen";
		}
		else {
			this.fullscreen = false;
			this.setSize(this.width / 2, this.height / 2);
			this.element.className = this.element.className.replace(/pos_fullscreen/i, "");
		}
	}
});
