(function($) {

if (!$.fn.closest) {
    $.fn.closest = function(expr) {
        var self = $(this);
        if (self.is(expr))
            return self;
        return self.parents(expr).eq(0);
    };
}

jQuery.easing.raphaelLike = function(n) {
    return Math.pow(n - 1, 3) + 1;
};

$.fn.problemcloud = function() {
    return $(this).each(function() {
        var root = $(this);
        var canvas = root.find(".pccanvas");
        var problems = root.find("ul.pcproblems");
        var solutionContainer = root.find("div.pcsolutioncontainer");
        
        root.height(problems.height());       
        problems.css({"left": 0, "top": 0}).width(problems.width()).height(problems.height()).css({"position": "absolute"});
        
        var problemsWidth = problems.outerWidth(true);
        var paper = Raphael(canvas[0], problemsWidth, problems.height());
        
        var createProblemObj = function(problem) {
            return {problem: problem[0], path: paper.path().attr({"opacity": 0, "stroke": "#dfe5e3", "stroke-width": 4})};
        };
        
        var openSolution = function(problem, solution) {			
            solution.stop(true, true);
            solutionContainer.prepend(solution);
            
            var problems = solution.data("problems");
            if (problems) {
                var found = false;
                $.each(problems, function() {
                    if (this.problem == problem[0]) {
                        pobj = this;
                        return false;
                    }
                });
                if (!pobj) {
                    problems.push(createProblemObj(problem));
                    solution.data("problems", problems);
                }                
            } else {
                solution.data("problems", [createProblemObj(problem)]).css({"opacity": 0, "display": "block"});
            }
            
            problem.addClass("selected");
			
			if(window.snoobi) {
				snoobi.trackPageView("/virtual/Ongelmapilvi/" + encodeURIComponent(problem.data('problem')), "Ongelmapilvi - " + problem.data('problem'), "Generoidut klikkaukset"); 				
			}
			
            positionSolutions(solution[0], !problems);
        };
        
        var positionSolutions = function(newSolution, adding) {
            var offs = root.offset();
            var x = problemsWidth, y = 0;
            solutionContainer.find(">div").each(function() {
                var solution = $(this), problems;
                if (problems = solution.data("problems")) {
                    solution.stop(true, true);
                    
                    if (this == newSolution && adding)
                        solution.css({"left": x, "top": y, "opacity": 0, "display": "block"}).animate({"opacity": 1.0}, 500);
                    else
                        solution.animate({"left": x, "top": y}, 500, "raphaelLike");
                    
                    if (this == newSolution)
                        solution.css({"backgroundColor": "#FF8"}).animate({"backgroundColor": "#FFF"}, 300);
                        
                    var toPosY = y + solution.outerHeight() / 2;
                    
                    $.each(problems, function() {
                        var from = $(this.problem), positioner;
                        from.append(positioner = $("<span style=\"display: inline-block; font-size: 0;\"></span>"));
                        var fromPos = positioner.position();
                        fromPos.top -= 5;
                        positioner.remove();
                        
                        var pathString = (
                              "M"
                            + fromPos.left + "," + fromPos.top
                            + "C"
                            + (fromPos.left + (x - fromPos.left) / 4) + "," + fromPos.top + " "
                            + (x - (x - fromPos.left) / 4) + "," + toPosY + " "
                            + x + "," + toPosY);
                        
                        if (!this.path.attr("path"))
                            this.path.attr("path", pathString).animate({"opacity": 0.7}, 500);
                        else
                            this.path.animate({"path": pathString, "opacity": 0.7}, 500, ">");
                    });
                    
                    y += solution.outerHeight(true);
                }
            });
            
            var height = Math.max(y, problems.height());
            paper.setSize(problemsWidth, height);
            root.animate({"height": height}, 500, "raphaelLike");
        };
        
        var removeSolution = function(solution) {
            $.each(solution.data("problems"), function() {
                $(this.problem).removeClass("selected");
                var path = this.path;
                path.animate({"opacity": 0}, 500, function() { path.remove(); });
            });
            solution.removeData("problems");
            positionSolutions();
            solution.stop(true, true);
            solution.animate({opacity: 0.0}, 500, 'linear', function() { $(this).css({"display": "none"}); });
        };
        
        var problemClick = function(prob) {
            var m;
            if ($(prob).is("a") && (m = $(prob).attr("href").match(/#(.*)/))) {
                openSolution($(prob), $("#" + m[1]));
                return false;
            }
        }
        
        problems.click(function(e) {
            problemClick(e.target);
        });
        
        solutionContainer.click(function(e) {
            if ($(e.target).closest(".close").length) {
                removeSolution($(e.target).closest("div.pcsolution"));
                return false;
            }
        });
        
        problems.find("a").each(function() {
            $(this).data("problem", $(this).attr("title"));
            $(this).attr("title", "");
        });
        
        $(function() {
            var m;
            if (m = /#solution-(\d+)/.exec(location.href)) {
                problemClick($("#problem-" + m[1]));
            }
        });
    });
};

})(jQuery);

/*
 * jQuery Color Animations
 * Copyright 2007 John Resig
 * Released under the MIT and GPL licenses.
 */

(function(jQuery){

	// We override the animation for all of these color styles
	jQuery.each(['backgroundColor', 'borderBottomColor', 'borderLeftColor', 'borderRightColor', 'borderTopColor', 'color', 'outlineColor'], function(i,attr){
		jQuery.fx.step[attr] = function(fx){
			if ( fx.state == 0 ) {
				fx.start = getColor( fx.elem, attr );
				fx.end = getRGB( fx.end );
			}

			fx.elem.style[attr] = "rgb(" + [
				Math.max(Math.min( parseInt((fx.pos * (fx.end[0] - fx.start[0])) + fx.start[0]), 255), 0),
				Math.max(Math.min( parseInt((fx.pos * (fx.end[1] - fx.start[1])) + fx.start[1]), 255), 0),
				Math.max(Math.min( parseInt((fx.pos * (fx.end[2] - fx.start[2])) + fx.start[2]), 255), 0)
			].join(",") + ")";
		}
	});

	// Color Conversion functions from highlightFade
	// By Blair Mitchelmore
	// http://jquery.offput.ca/highlightFade/

	// Parse strings looking for color tuples [255,255,255]
	function getRGB(color) {
		var result;

		// Check if we're already dealing with an array of colors
		if ( color && color.constructor == Array && color.length == 3 )
			return color;

		// Look for rgb(num,num,num)
		if (result = /rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(color))
			return [parseInt(result[1]), parseInt(result[2]), parseInt(result[3])];

		// Look for rgb(num%,num%,num%)
		if (result = /rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(color))
			return [parseFloat(result[1])*2.55, parseFloat(result[2])*2.55, parseFloat(result[3])*2.55];

		// Look for #a0b1c2
		if (result = /#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(color))
			return [parseInt(result[1],16), parseInt(result[2],16), parseInt(result[3],16)];

		// Look for #fff
		if (result = /#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(color))
			return [parseInt(result[1]+result[1],16), parseInt(result[2]+result[2],16), parseInt(result[3]+result[3],16)];

		// Otherwise, we're most likely dealing with a named color
		return null;
	}
	
	function getColor(elem, attr) {
		var color;

		do {
			color = jQuery.curCSS(elem, attr);

			// Keep going until we find an element that has color, or we hit the body
			if ( color != '' && color != 'transparent' || jQuery.nodeName(elem, "body") )
				break; 

			attr = "backgroundColor";
		} while ( elem = elem.parentNode );

		return getRGB(color);
	};
})(jQuery);
