	var seconds_counter = 0;
	var play_time;
	var game_id = 0;
	
	var grey = 'rgb(128, 128, 128)';
	//var grey = 'grey';
	var minutes;
	var seconds;
	var miliseconds;
	var place;
	var name_default_value;
	var login_default_value;
	var password_default_value;
	var on_forum = 'ololo';
	var pattern = /^([A-Za-z0-9_\-\.])+\@([A-Za-z0-9_\-\.])+\.([A-Za-z]{2,4})$/;
	var final_name;
    var global_timer;

$(document).ready(function() {
    $('#puzzle_close').live("click", function() {
        clearInterval(play_time);
        seconds_counter = 0;
        
        $('#bombing_video').hide();
        $('#shadow').hide();
        $('.x').css('top', '-1000px');
        $('#puzzler').remove();
        $('#minutes').val('0');
        $('#seconds').val('0');
        $('#miliseconds').val('0');
        $('#place').val('0');

        restart();
        clearInterval(play_time);
        seconds_counter = 0;
    });

    //var test = $("<span/>").attr({'id': 'test_span'}).css({position: 'absolute', 'z-index': '99999', 'top': '0px', 'right': '0px', 'width': '100px', 'height': '100px'});
    //$("body").append(test);
    //updateInterval = setInterval(function () { $("#test_span").text(seconds_counter); }, 100);
});

function show_video() {
    $('#bombing_video').show();
    $('.score_result').hide();
    $('#puzzle_close').css({top: '45px', left: '538px'}).show();
    if($('#puzzle_close').css('position') == 'absolute') {
        $('#puzzle_close').css({top: '0px'});
    }
    //$('div#links').css('top', '-1308px');
    $('div#handler_top').css('left','14px').css('top','-566px').css('width','540px');
	$('div#handler_bottom').css('left','13px').css('top','-80px').css('width','555px');
	$('div#handler_right').css('left','536px').css('top','-617px').css('height','543px');
	$('div#handler_left').css('left','12px').css('top','-1176px').css('height','555px');
    $('div#links').css('position', 'absolute');
    $("#links img:first").attr('src', '/images/restart.gif');
    $("#links img:last").attr('src', '/images/vidos.gif');
	$('div#links').css('top','588px').css('width','320px');
    //$('div#links').show();
	$('div#trololo').css('background','url("/images/back_shadow.png") no-repeat scroll 0 0 transparent');

	$("div.score_wrapper").hide();
	$("div.score_wrapper_high").hide();
	$('#puzzler').remove();
}

/*function check_forum_user(login, password){
	if ($('#login').val() != '' && $('#login').val() != 'Логин' && $('#password').val() != '' && $('#login').css('background-color') != grey){
		var xmlhttp;
		if (window.XMLHttpRequest){
			// code for IE7+, Firefox, Chrome, Opera, Safari
			xmlhttp=new XMLHttpRequest();
		}else if (window.ActiveXObject){
			// code for IE6, IE5
			xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
		}else{
			alert("Your browser does not support XMLHTTP!");
		}
		xmlhttp.onreadystatechange=function(){
			if(xmlhttp.readyState==4){
				if (xmlhttp.responseText == "false"){
					alert('false');
				}else{
					div = document.getElementById('forum_user_data');
					div.innerHTML = xmlhttp.responseText;
					//alert(xmlhttp.responseText);
					var check_user = document.getElementById('f_user_exists');
					if (check_user.value == '1'){
						show_last_form(login);
					}else{
						document.getElementById('f_user_not_exist').style.display = 'block';
					}
				}
			}
		}
		xmlhttp.open("GET", "/scripts/check_forum_user.php?login="+login+"&password="+password, true);
		xmlhttp.send(null);
	}
}*/

function validating(){
	//hide all error messages
	$('#f_user_not_exist').fadeOut(200);
	$('#f_user_empty').fadeOut(200);
	$('#f_password_wrong').fadeOut(200);
	$('#user_wrong_name').fadeOut(200);
	$('#email_wrong').fadeOut(200);
	
	if (($('#name').hasClass("selected") || $('#name').hasClass("active")) && (!$('#name').hasClass("inactive"))){
		//if ($('#name').val() == '' || $('#name').val() == 'Ваше имя'){
		if ($('#name:not(.inactive)').val() == '' || $('#name:not(.inactive)').val() == name_default_value){
			$('#user_wrong_name').fadeIn(100);
		}else if ($('#email:not(.inactive)').val() == '' || $('#email:not(.inactive)').val() == 'E-mail'){
			$('#email_wrong').fadeIn(100);
		}else if(!pattern.test($('#email').val())){
			$('#email_wrong').fadeIn(100);
		}else{
		//all ok we can pass data to script
			on_forum = false;
			final_name = $('#name').val();
		}
	}else
	//check if fields of forum user name and password are filled correctly
	if ($('#login').hasClass("selected") || $('#login').hasClass("active")){
		//if ($('#login').val() == '' || $('#login').val() == 'Логин'){
		if ($('#login:not(.inactive)').val() == '' || $('#login:not(.inactive)').val() == login_default_value){
            alert(login_default_value);
			$('#f_user_empty').fadeIn(100);
		}else if ($('#password:not(.inactive)').val() == '' || $('#password:not(.inactive)').val() == password_default_value){
			$('#f_password_wrong').fadeIn(100);
		}else{
		//all ok we can pass data to script
			on_forum = true;
			final_name = $('#login').val();
		}
	}
	
	if($('#f_user_wrong_password').val() == '1'){
		$('#f_password_wrong').fadeIn(100);
	}
	if($('#f_user_wrong_login').val() == '1'){
		$('#f_user_not_exist').fadeIn(100);
	}
}

function save_name(){
	var login = document.getElementById('login').value;
	var password = document.getElementById('password').value;
    var user_logged_in = document.getElementById('user_logged_in').value;
	var name = document.getElementById('name').value;
	var email = document.getElementById('email').value;
	var place = document.getElementById('place').value;
	var id = document.getElementById('id').value;
	
	//check if fields of user name and email are filled correctly
	validating();
	
	if (on_forum != 'ololo'){
		var xmlhttp;
		if (window.XMLHttpRequest){
			// code for IE7+, Firefox, Chrome, Opera, Safari
			xmlhttp=new XMLHttpRequest();
		}else if (window.ActiveXObject){
			// code for IE6, IE5
			xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
		}else{
			alert("Your browser does not support XMLHTTP!");
		}
		xmlhttp.onreadystatechange=function(){
			if(xmlhttp.readyState==4){
				if (xmlhttp.responseText == "false"){
					alert('false');
				}else{
					if (on_forum){
						div = document.getElementById('f_check_answer');
					}else{
						div = document.getElementById('trololo');
					}
					div.innerHTML = xmlhttp.responseText;
					if ($('#saved').val() == '1'){
						show_last_form(minutes, seconds, miliseconds, final_name, place, $('#id').val());
					}else{
						validating();
					}
					//alert(xmlhttp.responseText);
				}
			}
		}
		if (on_forum){
			xmlhttp.open("GET", encodeURI("/scripts/save_name.php?id="+id+"&forum="+on_forum+"&login="+login+"&password="+password+"&user_logged_in="+user_logged_in+"&game_id=" + game_id), true);
            //xmlhttp.open("GET", "/scripts/save_name.php?id="+id+"&forum="+on_forum+"&login="+login+"&password="+password+"&user_logged_in="+user_logged_in+"&game_id=" + game_id, true);
		}else{
			xmlhttp.open("GET", encodeURI("/scripts/save_name.php?id="+id+"&forum="+on_forum+"&name="+name+"&email="+email+"&game_id=" + game_id), true);
            //xmlhttp.open("GET", "/scripts/save_name.php?id="+id+"&forum="+on_forum+"&name="+name+"&email="+email+"&game_id=" + game_id, true);
		}
		xmlhttp.send(null);
	}
}

function send_stats(action) {
    $.ajax({
       type: "GET",
       url: "/scripts/add_stats.php",
       data: "action=" + action,
       success: function(msg){
         return;
       }
     });
}

function send_stats_ingame_start(pieces, hole) {
	var status_string = '';

	for(var i = 0; i < pieces.length; i++) {
		// since the hole is not saved in the pieces array, 
		// adjust the index if it is bejond the hole position
		var pieceIndex = pieces.eq(i).attr('current');
		
		status_string = status_string + pieceIndex + '.';
	}

    $.ajax({
       type: "POST",
       url: "/scripts/add_stats_ingame.php?action=1",
       data: "array=" + status_string,
	   dataType: "text",
       success: function(msg){
			game_id = parseInt(msg);
       }
     });
}

function send_stats_ingame(s_row, s_col, d_row, d_col) {
	if(game_id == 0)
		return;
		
    $.ajax({
       type: "POST",
       url: "/scripts/add_stats_ingame.php",
       data: "id=" + game_id + "&srow=" + s_row + "&scol=" + s_col + "&drow=" + d_row + "&dcol=" + d_col,
       success: function(msg){
         return;
       }
     });
}

function show_last_form(min, sec, milisec, login, place, id){
	//restyle drag-handlers and links again for last form and remove shadow from trololo
	$('div#handler_top').css('left','14px').css('top','-582px').css('width','600px');
	$('div#handler_bottom').css('left','13px').css('top','-309px').css('width','595px');
	$('div#handler_right').css('left','592px').css('top','-648px').css('height','340px');
	$('div#handler_left').css('left','0px').css('top','-987px').css('height','340px');
    $('div#links').css('position', 'absolute');
	$('div#links').css('bottom','5px').css('width','320px');
	$('div#trololo').css('background','none');
	var xmlhttp;
		if (window.XMLHttpRequest){
			// code for IE7+, Firefox, Chrome, Opera, Safari
			xmlhttp=new XMLHttpRequest();
		}else if (window.ActiveXObject){
			// code for IE6, IE5
			xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
		}else{
			alert("Your browser does not support XMLHTTP!");
		}
		xmlhttp.onreadystatechange=function(){
			if(xmlhttp.readyState==4){
				if (xmlhttp.responseText == "false"){
					alert('false');
				}else{
					div = document.getElementById('trololo');
					div.innerHTML = xmlhttp.responseText;

//                    var score_height = $('div.score_wrapper').innerHeight();
//                    var score_height_int = parseInt(score_height);
//                    score_height_int = score_height_int + 30;
//                    if($('div.score_wrapper').hasClass('not_best_score')) {
//                        $('div#links').css('position', 'absolute').css('top','386px').fadeIn();
//                    } else {
//                        $('div#links').css('position', 'absolute').css('top', score_height_int + 'px').fadeIn();
//                    }

                    $("#links img:first, #links1 img:first").attr('src', '/images/restart.gif');
                    $("#links img:last, #links1 img:last").attr('src', '/images/vidos.gif');
				}
			}
		}
		xmlhttp.open("GET", encodeURI("/scripts/show_last_form.php?minutes="+min+"&seconds="+sec+"&miliseconds="+milisec+"&login="+login+"&place="+place+"&id="+id), true);
		xmlhttp.send(null);
}

function show_score_table(){
	var xmlhttp;
		if (window.XMLHttpRequest){
			// code for IE7+, Firefox, Chrome, Opera, Safari
			xmlhttp=new XMLHttpRequest();
		}else if (window.ActiveXObject){
			// code for IE6, IE5
			xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
		}else{
			alert("Your browser does not support XMLHTTP!");
		}
		xmlhttp.onreadystatechange=function(){
			if(xmlhttp.readyState==4){
				if (xmlhttp.responseText == "false"){
					alert('false');
				}else{
					div = document.getElementById('trololo');
					div.innerHTML = xmlhttp.responseText;

//                    var score_height = $('div.score_wrapper').innerHeight();
//                    var score_height_int = parseInt(score_height);
//                    score_height_int = score_height_int + 30;
//                    if($('div.score_wrapper').hasClass('not_best_score')) {
//                        $('div#links').css('position', 'absolute').css('top','386px').fadeIn();
//                    } else {
//                        $('div#links').css('position', 'absolute').css('top', score_height_int + 'px').fadeIn();
//                    }
//                    $('div#links').css('top','-' + score_height_int + 'px');
				}
			}
		}
		xmlhttp.open("GET", "/scripts/show_score_table.php", true);
		xmlhttp.send(null);
}

$('#password').ready(function(){
	prepare_inputs();
});

function prepare_inputs(){
$('table.score_forms tr td input').each(function() {
    var user_logged_in = $("#user_logged_in").val();
    
    var default_value = this.value;
    $(this).focus(function() {
        if(this.value == default_value) {
            this.value = '';
        }
    });
    $(this).blur(function() {
        if(this.value == '') {
            this.value = default_value;
        }
    });
    name_default_value = $('#name').val();
    
    if(user_logged_in == 0) {
        login_default_value = $('#login').val();
        password_default_value = $('#password').val();
    }
});

$('#name').focus(function(){
	$('#login').addClass('inactive').removeClass('active').removeClass('selected');
	$('#password').addClass('inactive').removeClass('active').removeClass('selected');
	$('#name').addClass('selected').removeClass('active').removeClass('inactive');
	$('#email').addClass('active').removeClass('selected').removeClass('inactive');
});
$('#email').focus(function(){
	$('#login').addClass('inactive').removeClass('active').removeClass('selected');
	$('#password').addClass('inactive').removeClass('active').removeClass('selected');
	$('#name').addClass('active').removeClass('selected').removeClass('inactive');
	$('#email').addClass('selected').removeClass('active').removeClass('inactive');
});
$('#login').focus(function(){
	$('#name').addClass('inactive').removeClass('active').removeClass('selected');
	$('#email').addClass('inactive').removeClass('active').removeClass('selected');
	$('#login').addClass('selected').removeClass('active').removeClass('inactive');
	$('#password').addClass('active').removeClass('selected').removeClass('inactive');
});
$('#password').focus(function(){
	$('#name').addClass('inactive').removeClass('active').removeClass('selected');
	$('#email').addClass('inactive').removeClass('active').removeClass('selected');
	$('#login').addClass('active').removeClass('selected').removeClass('inactive');
	$('#password').addClass('selected').removeClass('active').removeClass('inactive');
});
}

$('#name, #email, #login, #password').blur(function() {
	$(this).addClass('active').removeClass('selected').removeClass('inactive');
});

$('#show_score_table').live('click', function() {
	show_score_table();
});

function show_logo(){
	clearInterval(play_time);
	$('#logo').fadeIn();
	//document.getElementById('logo').style.display = '';
	$('.jqp-piece').fadeOut();
	$('.I_am_very_original').fadeOut();
	/*$('#puzzler').fadeOut();
	$('#logo').css('top', '48px');
	$('#logo').css('left', '48px');*/
	//$('#links').fadeIn();
	setTimeout(show_result, 1500);
}

function show_result(){
	//restyle drag-handlers and links
	$('div#handler_top').css('left','14px').css('top','-582px').css('width','600px');
	$('div#handler_bottom').css('left','13px').css('top','-47px').css('width','595px');
	$('div#handler_right').css('left','592px').css('top','-648px').css('height','600px');
	$('div#handler_left').css('left','0px').css('top','-1247px').css('height','600px');
	$('div#links').css('top','-1269px').css('width','320px');
    $('div#links').hide();
    
    //$('#puzzle_close').css({top: '25px', left: '600px'});
    $('#puzzle_close').hide();
    $('div#trololo').css('background','none');

	minutes = seconds_counter
		var xmlhttp;
		if (window.XMLHttpRequest){
			// code for IE7+, Firefox, Chrome, Opera, Safari
			xmlhttp=new XMLHttpRequest();
		}else if (window.ActiveXObject){
			// code for IE6, IE5
			xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
		}else{
			alert("Your browser does not support XMLHTTP!");
		}
		xmlhttp.onreadystatechange=function(){
			if(xmlhttp.readyState==4){
				if (xmlhttp.responseText == "false"){
					alert('false');
				}else{
					div = document.getElementById('trololo');
					div.innerHTML = xmlhttp.responseText;
					prepare_inputs();

                    if($('div.score_wrapper').length > 0) {
                        var score_height = $('div.score_wrapper').innerHeight();
                    } else {
                        var score_height = $('div.score_wrapper_high').innerHeight();
                        $("#links img:first, #links1 img:first").attr('src', '/images/restart_high.gif');
                        $("#links img:last, #links1 img:last").attr('src', '/images/vidos_high.gif');
                    }
//                    var score_height_int = parseInt(score_height);
//                    score_height_int = score_height_int + 30;
//                    if($('div.score_wrapper').hasClass('not_best_score')) {
//                        $('div#links').css('position', 'absolute').css('top','435px').fadeIn();
//                    } else {
//                        $('div#links').css('position', 'absolute').css('top', score_height_int + 'px').fadeIn();
//                    }
//                    $('div#links').css('position', 'absolute').css('bottom', '5px');
									
					minutes = $('#minutes').val();
					seconds = $('#seconds').val();
					miliseconds = $('#miliseconds').val();
					place = $('#place').val();
				}
			}
		}
		xmlhttp.open("GET", "/scripts/show_result.php?seconds="+seconds_counter+"&game_id="+game_id, true);
		xmlhttp.send(null);

    send_stats(2);
	seconds_counter = 0;
    clearInterval(play_time);
}

function restart() {
    seconds_counter = 0;
    clearInterval(play_time);

    $('#puzzler').remove();
	var newPuzzle = $('<img/>').attr({src: '/images/picture_no_shadows_new.jpg', id: 'puzzler'}).addClass('jqPuzzle jqp-r4-c4-h16-SABCDE');
	$('.trololo').prepend(newPuzzle);
    //if($('#logo').length < 1) {
        var newLogo = $('<img/>').attr({src: '/images/15s_logotype_sumtel.gif', id: 'logo'}).css({'width': '483px', 'height': '483px', 'position': 'absolute', 'top': '49px', 'left': '49px', 'display': 'none', 'z-index': '10000', 'padding': '0'});
        $('.trololo').append(newLogo);
    //}
	$('.jqPuzzle').removeClass('jqp-solved');
    
	//restyle drag-handlers and links to start position
	$('div#handler_top').css('left','14px').css('top','-566px').css('width','540px');
	$('div#handler_bottom').css('left','13px').css('top','-80px').css('width','555px');
	$('div#handler_right').css('left','536px').css('top','-617px').css('height','543px');
	$('div#handler_left').css('left','12px').css('top','-1176px').css('height','555px');
    $("#links img:first").attr('src', '/images/restart.gif');
    $("#links img:last").attr('src', '/images/vidos.gif');
	$('div#links').css('bottom','5px').css('width','320px');
	$('div#trololo').css('background','url("/images/back_shadow.png") no-repeat scroll 0 0 transparent');
    //$('#puzzle_close').css({top: '45px', left: '538px'});
    $('#puzzle_close').show();
	
	$("#bombing_video").hide();
	$("div.score_wrapper").remove();
	$("div.score_wrapper_high").remove();

    send_stats(3);
	
	$('img.jqPuzzle').each(function() {

		// define an additional micro format (to be used as a class name)
		
		/* Syntax:   .jqp[-LANGUAGE]-rROWS-cCOLS[-hHOLE][-sSHUFFLE_ROUNDS][-FLAGS]
		 * 
		 * Flags:    S - initially shuffle pieces
		 *           N - initially hide numbers
		 *           A - hide 'Shuffle' button
		 *           B - hide 'Original' button
		 *           C - hide 'Numbers' button
		 *           D - hide 'moves' display
		 *           E - hide 'seconds' display
		 */
		var microFormat = /\bjqp(-[a-z]{2})?-r(\d)-c(\d)(-h(\d+))?(-s(\d+))?(-[A-Z]+)?\b/;
				
		// execute regex and save matches
		var match = microFormat.exec(this.className);
		
		// build settings object from micro format
		var settings;
		if(match) {
			settings = {	
				rows: parseInt(match[2]),
				cols: parseInt(match[3]), 
				hole: parseInt(match[5]) || null,
				shuffle: match[8] && match[8].indexOf('S') != -1,
				numbers: match[8] ? match[8].indexOf('N') == -1 : true,
				language: match[1] && match[1].substring(1)
			};
			
			if(match[7]) {
				settings.animation = {};
				settings.animation.shuffleRounds = parseInt(match[7]);
			}
			
			if(match[8] && match[8].search(/[ABCDE]/) != -1) {
				settings.control = 	{};
				settings.control.shufflePieces = match[8].indexOf('A') == -1;
				settings.control.toggleOriginal = match[8].indexOf('B') == -1;
				settings.control.toggleNumbers = match[8].indexOf('C') == -1;
				settings.control.counter = match[8].indexOf('D') == -1;
				settings.control.timer = match[8].indexOf('E') == -1;
			}
		}
		// call the plugin
		$(this).jqPuzzle(settings);
	});
	$('#logo').fadeOut();
	$('#links').fadeOut();
    play_time = setInterval(function () {
	  seconds_counter++;
	}, 10);
}

(function($) {
/*
 * jqPuzzle - Sliding Puzzles with jQuery
 * Version 1.02
 * 
 * Copyright (c) 2008 Ralf Stoltze, http://www.2meter3.de/jqPuzzle/
 * Dual-licensed under the MIT and GPL licenses.
 */
$.fn.jqPuzzle = function(settings, texts) {

	// default settings
	var defaults = {
		
		rows: 4, 					// number of rows [3 ... 9]
		cols: 4,		 			// number of columns [3 ... 9]
		hole: 16,					// initial hole position [1 ... rows*columns]
		shuffle: true,				// initially show shuffled pieces [true|false]
		numbers: true,				// initially show numbers on pieces [true|false]
		language: 'ru',				// language for gui elements [language code]
		
		// display additional gui controls
		control: {
			shufflePieces: false,	// display 'Shuffle' button [true|false]
			confirmShuffle: false,	// ask before shuffling [true|false]
			toggleOriginal: false,	// display 'Original' button [true|false]
			toggleNumbers: false,	// display 'Numbers' button [true|false]
			counter: false,			// display moves counter [true|false]
			timer: false,			// display timer (seconds) [true|false]
			pauseTimer: false		// pause timer if 'Original' button is activated 
									// [true|false]
		},
		
		// perform actions when the puzzle is solved sucessfully
		success: {
			fadeOriginal: true,		// cross-fade original image [true|false]
			callback: show_logo,	// callback a user-defined function [function]
									// the function is passed an object as its argument
									// which includes the fields 'moves' and 'seconds'
			callbackTimeout: 100	// time in ms after which the callback is called
		},		
		
		// animation speeds and settings
		animation: {
			shuffleRounds: 3,		// number of shuffle rounds [1 ... ]
			shuffleSpeed: 800,		// time in ms to perform a shuffle round
			slidingSpeed: 200,		// time in ms for a single move
			fadeOriginalSpeed: 600	// time in ms to cross-fade original image
		},
		
		// additional style information not specified via css
		style: {
			gridSize: 1,			// space between two pieces in px
			overlap: true,			// if true, adjacent piece borders will overlap
									// applies only if gridSize is set to 0
			backgroundOpacity: 0	// opacity of the original image behind the pieces
									// [0 ... 1] (0 means no display)
		}
	};
	
	// language localizations
	var i18n = {
		en: {
			shuffleLabel: 			'Shuffle',
			toggleOriginalLabel: 	'Original',
			toggleNumbersLabel: 	'Numbers',
			confirmShuffleMessage: 	'Do you really want to shuffle?',
			movesLabel:				'moves',
			secondsLabel:			'seconds'
		},
		ru: {
			shuffleLabel: 			'Перемешать',
			toggleOriginalLabel: 	'Упорядочить',
			toggleNumbersLabel: 	'Нумерация',
			confirmShuffleMessage: 	'Вы таки хотите перемешать?',
			movesLabel:				'ходов',
			secondsLabel:			'секунд'
		},
		fr: {
			shuffleLabel: 			'Mélanger',
			toggleOriginalLabel: 	'Original',
			toggleNumbersLabel: 	'Nombres',
			confirmShuffleMessage: 	'Veux-tu vraiment mélanger?',
			movesLabel:				'mouvements',
			secondsLabel:			'secondes'
		},
		de: {
			shuffleLabel:			'Mischen',
			toggleOriginalLabel: 	'Original',
			toggleNumbersLabel: 	'Nummern',
			confirmShuffleMessage: 	'Willst du wirklich mischen?',
			movesLabel:				'Züge',
			secondsLabel:			'Sekunden'
		},
		pt: {
			shuffleLabel:			'Embaralhar',
			toggleOriginalLabel:	'Original',
			toggleNumbersLabel:		'Numeros',
			confirmShuffleMessage:	'Tem certeza que deseja reembralhar?',
			movesLabel:				'movimentos',
			secondsLabel:			'segundos'
		}
	};
	
	// if rows or cols, but no hole was user-defined,
	// explicitly set hole position to last piece (bottom right)
	if(settings && !settings.hole && (settings.rows || settings.cols)) {
		settings.hole = (settings.rows || defaults.rows) * (settings.cols || defaults.cols);
	}

	// extend the user-defined settings object with default settings
	settings = $.extend(true, {}, defaults, settings);

	// extend the user-defined texts object with current language texts
	texts = $.extend((i18n[settings.language] || i18n[defaults.language]), texts);
	
	// create some handy shortcut variables
	var rows = settings.rows, 
		cols = settings.cols, 
		hole = settings.hole;
	var control = settings.control,
		success = settings.success,
		animation = settings.animation,
		style = settings.style;

	// check settings for valid values

	// keep rows and columns within limits
	if(rows < 3 || rows > 9) rows = defaults.rows;
	if(cols < 3 || cols > 9) cols = defaults.rows;

	// keep hole position within limits
	if((hole > (rows*cols)) || (hole < 1)) hole = rows*cols;
	hole--; // zero-based index
	
	// animation speed = 0 doesn't work
	if(animation.slidingSpeed < 1) animation.slidingSpeed = 1;
	if(animation.shuffleSpeed < 1)  animation.shuffleSpeed = 1;
	if(animation.fadeOriginalSpeed < 1) animation.fadeOriginalSpeed = 1;
	
	// keep shuffle rounds within limits
	if(animation.shuffleRounds < 1) animation.shuffleRounds = 1;


	// helper functions --------------------------------------------------------
	
	// checks if the puzzle is solved
	var checkSolution = function($pieces) {
		// iterate over pieces and check each piece
		for(var i = 0; i < $pieces.length; i++) {
			// since the hole is not saved in the pieces array, 
			// adjust the index if it is bejond the hole position
			var pieceIndex = (i < hole) ? i : i + 1;
			
			// check if current position match target (index) position
			if($pieces.eq(i).attr('current') != pieceIndex) return false;
		}
		return true;
	};
	
	// checks if the puzzle can be solved (pure math ...)
	var checkOrder = function(numbersArray) {
		var product = 1;
		for(var i = 1; i <= (rows*cols-1); i++) {
			for(var j = (i+1); j <= (rows*cols); j++) {
				product *= ((numbersArray[i-1] - numbersArray[j-1]) / (i-j));
			}
		}
		return Math.round(product) == 1;
	};
	
	// get the linear index from a row/col pair (zero-based)
	var getLinearPosition = function(row, col) {
		return parseInt(row)*cols + parseInt(col);
	};
	
	// get the row/col pair from a linear index (zero-based)
	var getMatrixPosition = function(index) {
		return {row: (Math.floor(index/cols)), col: (index%cols)};
	};
	
	// get the pixel width of a border (internet explorer returns keywords)
	// the left side values will be used
	var getBorderWidth = function($element) {
		// the reported css value
		var property = $element.css('border-left-width');
		// a border style must be set to get a valid border width
		if($element.css('border-left-style') != 'none') {
			switch(property) {
				case 'thin': return 2;
				case 'medium': return 4;
				case 'thick': return 6;
				default:
					return parseInt(property) || 0;	// parse pixel value
			}
		}
		return 0;
	};

	// a reusable timer component
	// pass in a timeout interval in ms, after which callback is called
	// callback gets passed one argument, the elapsed time is ms
	var Timer = function(interval, callback) {
		var startTime;
		var startPauseTime;
		var totalPause = 0;
		var timeout;
		
		var run = function() {	
			update(new Date().getTime());
			timeout = setTimeout(run, interval);
		};
		
		var update = function(now) {
			callback(now - totalPause - startTime);
		};

		// start the timer
		this.start = function() {
			if(startTime) return false;
			startTime = new Date().getTime();
			run();
		};

		// stop the timer
		this.stop = function() {
			if(!startTime) return false;
			clearTimeout(timeout);
			var now = new Date().getTime();
			if(startPauseTime) totalPause += now - startPauseTime;
			update(now);
			startTime = startPauseTime = undefined;
			totalPause = 0;
		};

		// pause the timer
		this.pause = function() {
			if(!startTime || startPauseTime) return false;
			clearTimeout(timeout);
			startPauseTime = new Date().getTime();
		};

		// resume the timer
		this.resume = function() {
			if(!startPauseTime) return false;
			totalPause += new Date().getTime() - startPauseTime;
			startPauseTime = undefined;
			run();
		};
	};


	// apply jqPuzzle to each image element within selection -------------------

	return this.filter('img').each(function(){
		var $srcImg = $(this);			// source image as jQuery object
		var lock = false;				// flag if animations are running
		var moves = 0;					// counter for single moves
		var seconds = 0;				// counter for seconds after first move
		var solved;						// flag if the puzzle is solved by the user
		var shuffled = settings.shuffle;// flag if the puzzle was shuffled
		var timer;						// a timer component
					
		// save the current hole position for further manipulation
		var currHole = hole;
		
		
		// create dummy elements to get computed css properties
		var $dummyPiece = $('<div/>').addClass('jqp-piece');
		var $dummyWrapper = $('<div/>').addClass('jqp-wrapper').append($dummyPiece);
		var $dummyGui = $('<div/>')
			.attr('class', $srcImg.attr('class') || '') // transfer classes
			.addClass('jqPuzzle')
			.append($dummyWrapper);		
	
		// replace original image with dummy
		$srcImg.replaceWith($dummyGui);
	
		// assign old image id to dummy
		$dummyGui.attr('id', $srcImg.attr('id') || '');
		
		// get computed css properties of dummy elements
		var computedStyles = {
			gui: {
				border: getBorderWidth($dummyGui),
				padding: {
					left: parseInt($dummyGui.css('padding-left')) || 0,
					right: parseInt($dummyGui.css('padding-right')) || 0,
					top: parseInt($dummyGui.css('padding-top')) || 0,
					bottom: parseInt($dummyGui.css('padding-bottom')) || 0
				}
			},
			wrapper: {
				border: getBorderWidth($dummyWrapper),
				padding: parseInt($dummyWrapper.css('padding-left')) || 0
			},
			piece: {
				border: getBorderWidth($dummyPiece)
			}
		};
		
		// re-replace dummy elements with original image
		$dummyGui.removeAttr('id');
		$dummyGui.replaceWith($srcImg);
		
		
		// wait for the image to be loaded, to be able to get its real width/height
		$srcImg.one('load', function() {

			// overlap piece borders if there is no margin between pieces
			// this way, piece borders will not be doubled
			var overlap = (style.gridSize === 0 && style.overlap);
					
			// total space of piece borders and grid lines, which will cover parts of the image
			var coveredWidth  = cols*(2*computedStyles.piece.border) + (cols-1)*style.gridSize;
			var coveredHeight = rows*(2*computedStyles.piece.border) + (rows-1)*style.gridSize;
			
			// recalc if overlap
			if(overlap) {
				coveredWidth  -= (cols-1)*computedStyles.piece.border;
				coveredHeight -= (rows-1)*computedStyles.piece.border;
			}

			// make sure to get the original image size, not scaled values
			// in mozilla, width() and height() do not work with hidden elements
			$srcImg.css({width: 'auto', height: 'auto', visibility: 'visible'}); 
			
			// pieces width and height, based on original image size
			var width  = Math.floor(($srcImg.width()-coveredWidth) / cols);
			var height = Math.floor(($srcImg.height()-coveredHeight) / rows);
			
			// reject too small images
			if(width < 30 || height < 30) return false;

			// recalc full image width and height to avoid rounding errors
			var fullWidth  = cols*width + coveredWidth;
			var fullHeight = rows*height + coveredHeight;

			// image source path
			var imgSrc = $srcImg.attr('src');
			
			// total width/height of a piece (including piece border and 1 grid size)
			var totalPieceWidth  = width + 2*computedStyles.piece.border + style.gridSize;
			var totalPieceHeight = height + 2*computedStyles.piece.border + style.gridSize;

			// handle internet explorer quirks mode box model
			var boxModelHack = {
				piece : $.boxModel ? 0 : 2*computedStyles.piece.border,
				wrapper: $.boxModel ? 0 : 2*(computedStyles.wrapper.border + computedStyles.wrapper.padding),
				gui: {
					width: $.boxModel ? 0 : 2*computedStyles.gui.border + 
						computedStyles.gui.padding.left + computedStyles.gui.padding.right,
					height: $.boxModel ? 0 : 2*computedStyles.gui.border + 
						computedStyles.gui.padding.top + computedStyles.gui.padding.bottom
				}
			};
			
			
			// helper functions ------------------------------------------------
			
			// pixel offset of an element, based on matrix position
			var getOffset = function(row, col) {
				var offset = {
					left: computedStyles.wrapper.padding + col*totalPieceWidth,
					top:  computedStyles.wrapper.padding + row*totalPieceHeight
				};
				
				if(overlap) {
					offset.left -= col * computedStyles.piece.border;
					offset.top  -= row * computedStyles.piece.border;
				}
				
				return offset;
			};

			// shuffle pieces
			var shuffle = function(rounds, speed) {
			
				/*if no shuffling needed*/
				/*
				solved = false;
				shuffled = true;
				moves = 0;
				seconds = 0;
				*/
                
				// when speed is defined, the function was triggered by a user event (button click)
				if(speed) {
					// do nothing, if disabled
					if($shuffleButton.is('.jqp-disabled')) return false;

					// do nothing, if locked
					if(lock) return false;
					
					// ask for confirmation
					if(control.confirmShuffle && (moves > 0) && 
					!window.confirm(texts.confirmShuffleMessage)) return false;

					lock = true; // set lock
					
					// if the puzzle is solved
					if(solved) {
						// reset gui
						$gui.removeClass('jqp-solved');
						
						// fade out original
						$background.fadeTo(animation.fadeOriginalSpeed, style.backgroundOpacity, function() {
							// opera gets kicked without remove()
							$background.remove().prependTo($wrapper);

							// re-enable all buttons
							$buttons.removeClass('jqp-disabled');							
						});
					}
				}

				// stop the timer
				if(timer) timer.stop();

				// reset flag and counters
				solved = false;
				shuffled = true;
				moves = 0;
				seconds = 0;
				
				// reset display
				if($display) $display.removeClass('jqp-disabled');
				if($counter) $counter.val(moves);
				if($timer) $timer.val(seconds);
				
				var shuffles = []; 
				var i = 0;
				// generate orders for several shuffle rounds
				while(i < rounds) {				
					// create an array for choosing random positions
					// based on its lenght, we can select free positions
					var choices = [];
					for(var j = 0; j < rows*cols; j++) {
						choices[j] = j;
					}
					// remove element on initial hole position
					choices.splice(hole, 1);
					
					shuffles[i] = [];
					// generate random numbers
					for(var j = 0; j < rows*cols; j++) {
						
						// but keep hole at initial position
						if(j == hole) {
							shuffles[i][j] = hole;
							continue;
						}
						
						// select a random position based on the length of the choices
						var randomIndex = Math.floor(Math.random()*choices.length);
						
						// save the value at this index as the next number in the current order
						shuffles[i][j] = choices[randomIndex];
						
						// remove this value from the choices array (reducing its length)
						choices.splice(randomIndex, 1);
					}
					
					// don't increase i if we are in last round 
					// and the generated order is not solvable
					if(((i+1) < rounds) || checkOrder(shuffles[i])) i++;
				}

				var animCounter = 0; // animation counter for save unlock
				
				// shuffle pieces in several rounds
				for(var i = 0; i < rounds; i++) {

					// set flag for the last round
					var lastRound = ((i+1) == rounds);

					// iterate over the generated orders
					// with j being the linear index for the destination order
					for(var j = 0; j < shuffles[i].length; j++) {
						
						// we cannot move the hole
						if(j == hole) {
							// update hole position
							if(lastRound) currHole = hole;
							continue;
						}

						// the value is the index of the current piece
						// in the original, ordered $pieces array
						var pieceIndex = shuffles[i][j];

						// since the hole is not saved in the $pieces array, 
						// adjust the index if it is bejond the hole position
						if(pieceIndex > hole) pieceIndex -= 1;
						
						// get the actual piece to be moved
						var $piece = $pieces.eq(pieceIndex);

						// get target position
						var target = getMatrixPosition(j);

						// get pixel offset new position
						var offset = getOffset(target.row, target.col);
						
						// update current row/cal in last round	(ie needs a string)
						if(lastRound) $piece.attr('current', j.toString());
						
						// either just set or animate styles
						if(speed === undefined) {
							$piece.css({left: offset.left, top: offset.top});
						} else {
							// animate!
							$piece.animate({left: offset.left, top: offset.top}, speed, null, function() {
								// unlock after last animation in last round
								animCounter++;
								if(animCounter == animation.shuffleRounds*(rows*cols-1)) {
									lock = false;
									animCounter = 0;
								}
							});
						}
					}
				} 
				/* comment here */
			};


			// create elements -------------------------------------------------

			// create a wrapper for the pieces
			var $wrapper = $('<div/>')
				.addClass('jqp-wrapper')
				.css({	
					width: fullWidth + boxModelHack.wrapper,
					height: fullHeight + boxModelHack.wrapper,
					borderWidth: computedStyles.wrapper.border,
					padding: computedStyles.wrapper.padding,
					margin: 0,
					//position: 'relative',
					overflow: 'hidden',
					display: 'block',
					visibility: 'inherit'
				});

			// create a single piece prototype to be cloned for the actual pieces
			var $protoPiece = $('<div/>')
				.addClass('jqp-piece')
				.css({
					width: width + boxModelHack.piece,
					height: height + boxModelHack.piece,
					//height: fullHeight,
					backgroundImage: 'url(' + imgSrc + ')',
					borderWidth: computedStyles.piece.border,
					margin: 0,
					padding: 0,
					position: 'absolute',
					overflow: 'hidden',
					display: 'block',
					visibility: 'inherit',
					cursor: 'default'
				})
				.append($('<span/>')
					.css({
						position: 'relative',
						top: '6px',
						left: '6px'
					})
				); // will hold the numbers

			// create pieces inside wrapper
			var $pieces = $([]); // create an empty jQuery object
			for(var i = 0; i < rows; i++) {
				for(var j = 0; j < cols; j++) {
					var index = getLinearPosition(i,j); // linear index
					
					// do not create piece at initial hole position
					if(index == hole) continue;
						
					// get piece position offset
					var offset = getOffset(i,j);

					// calculate background offset
					var bgLeft = -1 * (j*totalPieceWidth + computedStyles.piece.border);
					var bgTop  = -1 * (i*totalPieceHeight + computedStyles.piece.border);

					// recalc if overlap
					if(overlap) {
						bgLeft += j*computedStyles.piece.border;
						bgTop  += i*computedStyles.piece.border;
					}
					var piece_number = index +1;
					// create single pieces from prototype
					$pieces = $pieces.add($protoPiece.clone()
						.css({
							left: offset.left,
							top: offset.top,
							backgroundPosition: (bgLeft + 'px ' + bgTop + 'px')							
						})
						// add expando property to save the current position
						.attr('current', String(index)) // ie hack: String()
						.appendTo($wrapper)
						// add number to inner span
						//.children().html('<img src="/images/puzzle_numbers/'+ piece_number +'.png" />').end()
					);
				}
			}
	
			// initially shuffle pieces
			if(settings.shuffle) shuffle(1);

            send_stats_ingame_start($pieces, hole);
			
			// create background (original image) inside wrapper
			var $background = $('<div/>')
				.addClass('I_am_very_original')
				.css({
					width: fullWidth,
					height: fullHeight,
					left: computedStyles.wrapper.padding,
					top: computedStyles.wrapper.padding,
					backgroundImage: 'url(' + imgSrc + ')',
					borderWidth: 0,
					margin: 0,
					padding: 0,
					position: 'absolute',
					opacity: style.backgroundOpacity
				})
				.prependTo($wrapper);
			
			// create controls which will hold the buttons and the display
			var $controls = $('<div/>')
				.addClass('jqp-controls')
				.css({
					visibility: 'inherit',
					display: 'block',
					position: 'static'
				});

			var $shuffleButton, $originalButton, $numbersButton;
			
			// create a button prototype to be cloned for the actual buttons
			var $protoButton = $('<a/>').css('cursor', 'default');

			// create shuffle button
			if(control.shufflePieces) {
				$shuffleButton = $protoButton.clone()
					.text(texts.shuffleLabel)
					.appendTo($controls);
			}

			// create toggle original button
			if(control.toggleOriginal) {
				$originalButton = $protoButton.clone()
					.text(texts.toggleOriginalLabel)
					.appendTo($controls);
			}

			// create toggle numbers button
			if(control.toggleNumbers) {
				$numbersButton = $protoButton.clone()
					.text(texts.toggleNumbersLabel)
					.appendTo($controls);
				// immediately toggle button, if numbers are initially shown
				if(settings.numbers) $numbersButton.addClass('jqp-toggle');		
			}
			
			// keep a reference to all buttons for convenience
			var $buttons = $controls.children();

			var $display, $counter, $timer;
			if(control.counter || control.timer) {

				// create wrapper for counter/timer
				$display = $('<span/>')
					.css('cursor', 'default')
					.appendTo($controls);
			
				// create a text field prototype to be cloned for actual text fields
				var $protoField = $('<input/>')
					.val(0)
					.css({
						width: '5ex',
						cursor: 'default'
					})
					.attr('readonly', 'readonly');

				// create counter component
				if(control.counter)	$counter = $protoField.clone()
					.appendTo($display)
					.after(texts.movesLabel + ' ');
					
				// create timer component
				if(control.timer) $timer = $protoField.clone()
					.appendTo($display)
					.after(texts.secondsLabel);

				// disable display if the puzzle is not shuffled yet
				if(!settings.shuffle) $display.addClass('jqp-disabled');
			}
			
			// add link to jqPuzzle homepage
			var $credits_default = $('<a/>')
				//.text($image_in_href)
				.attr('href', 'javascript:void(0);')
				.attr('onclick', 'javascript:$(".jqPuzzle").hide();')
				.css({
					'float': 'right',
					fontFamily: 'Verdana, Arial, Helvetica, sans-serif',
					fontSize: '9px',
					lineHeight: '12px',
					textDecoration: 'none',
					color: '#FFFFFF',
					backgroundColor: '#fff',
					borderBottom: '0px dotted #FFFFFF',
					padding: '0px',
					margin: '0px',
					marginRight: computedStyles.wrapper.border,
					position: 'relative',
					top: '-53px',
					left: '50px',
					display: 'block'
				})
				//.html('<img src="/images/X.png" />');
				.html('<img src="/images/x.gif" />');
			
			//var $credits = '<img src="/images/X.png" style="position:relative; top:-50px; left:494px;" onclick="javascript: $(\'#bombing_video\').hide(); $(\'#shadow\').hide(); $(\'.x\').css(\'opacity\', \'0\'); $(\'.x\').css(\'top\', \'-1000px\'); restart()" />';
            //var $credits = '<img src="/images/spacer.gif" style="width: 46px; height: 46px; background: url(/images/X.png) no-repeat; _background: url(/images/X.gif) no-repeat; position:relative; top:-50px; left:494px;" onclick="javascript: $(\'#bombing_video\').hide(); $(\'#shadow\').hide(); $(\'.x\').css(\'top\', \'-1000px\'); restart()" />';
				

			// panel which holds controls and credits, used for height() calculations
			var $panel = $('<div/>')
				.css({
					width: fullWidth + 2*(computedStyles.wrapper.padding + computedStyles.wrapper.border),
					position: 'absolute', 
					display: 'block',
					visibility: 'inherit',//'visible',
					margin: '0px',
					padding: '0px',
					backgroundColor: 'transparent'
				})
                .append($controls);
                //.append($credits)
			
			var $wrapper_shadow = $('<div/>')
				.addClass('wrapper_shadow')
				.append($wrapper);
			
			// full gui (including wrapper and panel)
			var $gui = $('<div/>')
				.attr('class', $srcImg.attr('class') || '') // transfer classes
				.addClass('jqPuzzle') // always assign class jqPuzzle
				.css({
					//width: fullWidth + 2*(computedStyles.wrapper.padding + computedStyles.wrapper.border) + boxModelHack.gui.width,
					width: '494px',
					height: fullHeight + 2*(computedStyles.wrapper.padding + computedStyles.wrapper.border) + boxModelHack.gui.height,
					textAlign: 'left',
					overflow: 'hidden', 
					position: 'relative',
					display: 'block'
				})
				.append($panel).append($wrapper_shadow);

			// replace source image with jqPuzzle
			$srcImg.replaceWith($gui);

			// assign source image id to jqPuzzle
			var id = $srcImg.attr('id');
			if(id) $gui.attr('id', id);
			
			// opera has strange effect when calling hide() and val() before 
			// the elements are attached to the dom
			// hide numbers
			if(!settings.numbers) $pieces.children().hide();
			// fill inputs
			if($display) $display.children('input').val(0);

			// now, after everything is rendered, recalc gui height
			var guiHeight = $gui.height();
			var panelHeight = $panel.height();
			
			$gui.height($gui.height() + $panel.height());


			// attach events ---------------------------------------------------
			
			// prevent text selection
			if($.browser.msie) $gui[0].onselectstart = function() { return false; };
			else  $gui.mousedown(function() { return false; });
			
			// button press on mousedown
			$buttons.mousedown(function() {
				if(!$(this).is('.jqp-disabled')) $(this).addClass('jqp-down');
			});
			$buttons.mouseout(function() {
				$(this).removeClass('jqp-down');
			});
			$buttons.mouseup(function() {
				$(this).removeClass('jqp-down');	
			});
			
			// swap pieces on click
			$pieces.click(function() {
				// do nothing, if locked
				if(lock) return false;
				
				// do nothing, if solved after being shuffled
				if(solved) return false;
				
				lock = true; // set lock
				
				var $piece = $(this);
				
				// get current position from expando
				var current = $piece.attr('current');
				
				// get current matrix positions for piece and hole
				var source = getMatrixPosition(current);
				var dest = getMatrixPosition(currHole);

				// only swap pieces adjacent to the hole
				if(Math.abs(source.row - dest.row) + Math.abs(source.col - dest.col) != 1) {
						lock = false;
						return false;
					}

				// get offset for the new position
				var offset = getOffset(dest.row, dest.col);

				// update piece expando and current hole position
				$piece.attr('current', String(currHole)); // ie hack: String()
				currHole = current;

				// increase moves counter only if the puzzle was shuffled
				if(shuffled) moves++;

				// update counter field
				if($counter) $counter.val(moves);

				// start timer, if needed
				if(moves == 1) {
					//send_stats_ingame_start($pieces, hole);
				
					// initiate timer with update function
					if(!timer) timer = new Timer(333, function(ms) {
						seconds = Math.floor(ms/1000);
						if($timer) $timer.val(seconds);
					});
					timer.start();
				}
				
				// animate!
				$piece.animate({left: offset.left, top: offset.top}, animation.slidingSpeed, null, function() {
					// only check if the puzzle was shuffled
					if(shuffled) {
						// check if the puzzle is solved
						solved = checkSolution($pieces);
						if(solved) {
							if(timer) timer.stop();
							shuffled = false;
							$gui.addClass('jqp-solved');
							window.setTimeout(finishGame, 100);	
						}
						else lock = false;						
					}
					else lock = false;
					
					send_stats_ingame(source.row, source.col, dest.row, dest.col);
				});
			});

			// shuffle pieces on click
			if(control.shufflePieces) $shuffleButton.click(function() {
				shuffle(animation.shuffleRounds, animation.shuffleSpeed);	
			});

			// toggle original on click
			if(control.toggleOriginal) $originalButton.click(function() {
				// do nothing, if disabled
				if($originalButton.is('.jqp-disabled')) return false;

				// do nothing, if locked
				if(lock) return false;
				
				lock = true; // set lock
				
				if($originalButton.is('.jqp-toggle')) {
					// re-enable other buttons
					if(control.shufflePieces) $shuffleButton.removeClass('jqp-disabled');
					if(control.toggleNumbers) $numbersButton.removeClass('jqp-disabled');
			
					$originalButton.removeClass('jqp-toggle');
					
					// fade out original
					$background.fadeTo(animation.fadeOriginalSpeed, style.backgroundOpacity, function() {
						$(this).prependTo($wrapper);
						
						// resume timer
						if(control.pauseTimer && timer) timer.resume();
						
						lock = false;
					});
				} else {
					// disable other buttons
					if(control.shufflePieces) $shuffleButton.addClass('jqp-disabled');
					if(control.toggleNumbers) $numbersButton.addClass('jqp-disabled');
					
					$originalButton.addClass('jqp-toggle');

					// pause timer
					if(control.pauseTimer && timer) timer.pause();
					
					// fade in original
					$background.appendTo($wrapper).fadeTo(animation.fadeOriginalSpeed, 1, function() {
			
						lock = false;
					});
				}
				return false; // prevent default action
			});

			// toggle numbers on click
			if(control.toggleNumbers) $numbersButton.click(function() {
				// do nothing, if disabled
				if($numbersButton.is('.jqp-disabled')) return false;
				
				if ($numbersButton.is('.jqp-toggle')) {
					$numbersButton.removeClass('jqp-toggle');
					$pieces.children().hide();
				} else {
					$numbersButton.addClass('jqp-toggle');
					$pieces.children().show();
				}
			});		
			
			
			// work to do when the puzzle is solved
			var finishGame = function() {
				if(success.fadeOriginal) {
					// disable buttons
					if(control.toggleOriginal) $originalButton.addClass('jqp-disabled');
					if(control.toggleNumbers) $numbersButton.addClass('jqp-disabled');
					
					// fade in original
					$background.appendTo($wrapper).fadeTo(animation.fadeOriginalSpeed, 1.0, function() {
						lock = false; // reset lock
						solutionCallback(); // call user callback
					});
				} else {
					lock = false; // reset lock
					solutionCallback(); // call user callback	
				}
			};

			// call a user-defined callback after a timeout, when the puzzle is solved
			var solutionCallback = function() {
				if($.isFunction(success.callback)) {
					setTimeout(function() {
						success.callback();
					}, success.callbackTimeout);	
				}
			};

		}); // img load
		
		// unfortunately, image load does not fire consistently across browsers
		// (especially with cached images)
		// therefore, check image.load periodically (bah, brute force...)
		var interval = setInterval(function() {
			if($srcImg[0].complete) {
				clearInterval(interval);
				$srcImg.trigger('load');
			}
		}, 333);
		
		/*
		var interval;
		(function waitForImage($image) {
			if(!$image[0].complete) {
				interval = setInterval(function() {
					waitForImage($image);
				}, 100);
			} else {
				clearInterval(interval);
				$image.trigger('load');
			}
		})($srcImg);*/
		
		// ie shows some inconsistencies with img load and cached images
		// fortunately, in this cases img.complete is true from the beginning
		//if($.browser.msie && $srcImg[0] && $srcImg[0].complete) $srcImg.trigger('load');


	}).end(); // return full collection to allow chaining
};

// automagically apply jqPuzzle to all images with class 'jqPuzzle'
$(document).ready(function() {

	$('img.jqPuzzle').each(function() {

		// define an additional micro format (to be used as a class name)
		
		/* Syntax:   .jqp[-LANGUAGE]-rROWS-cCOLS[-hHOLE][-sSHUFFLE_ROUNDS][-FLAGS]
		 * 
		 * Flags:    S - initially shuffle pieces
		 *           N - initially hide numbers
		 *           A - hide 'Shuffle' button
		 *           B - hide 'Original' button
		 *           C - hide 'Numbers' button
		 *           D - hide 'moves' display
		 *           E - hide 'seconds' display
		 */
		var microFormat = /\bjqp(-[a-z]{2})?-r(\d)-c(\d)(-h(\d+))?(-s(\d+))?(-[A-Z]+)?\b/;
				
		// execute regex and save matches
		var match = microFormat.exec(this.className);
		
		// build settings object from micro format
		var settings;
		if(match) {
			settings = {	
				rows: parseInt(match[2]),
				cols: parseInt(match[3]), 
				hole: parseInt(match[5]) || null,
				shuffle: match[8] && match[8].indexOf('S') != -1,
				numbers: match[8] ? match[8].indexOf('N') == -1 : true,
				language: match[1] && match[1].substring(1)
			};
			
			if(match[7]) {
				settings.animation = {};
				//settings.animation.shuffleRounds = parseInt(match[7]);
                settings.animation.shuffleRounds = parseInt(7);
			}
			
			if(match[8] && match[8].search(/[ABCDE]/) != -1) {
				settings.control = 	{};
				settings.control.shufflePieces = match[8].indexOf('A') == -1;
				settings.control.toggleOriginal = match[8].indexOf('B') == -1;
				settings.control.toggleNumbers = match[8].indexOf('C') == -1;
				settings.control.counter = match[8].indexOf('D') == -1;
				settings.control.timer = match[8].indexOf('E') == -1;
			}
		}
		// call the plugin
		$(this).jqPuzzle(settings);
	});
});

})(jQuery);

