/*jPuzzle jQuery plugin (http://jquery.com/) jQuery => Copyright 2010, John Resig jPuzzle => Copyright 2010, Adrien Guéret Dual licensed under the MIT or GPL Version 2 licenses. http://jquery.org/license Last update: 26/07/2011*/ (function($) { $.fn.jPuzzle=function(options) { var defaults= { 'rows': 5, 'columns': 5, 'help': true, 'fixed': true, 'showHelpButton': true, 'textPlayButton': 'Jouer !', 'textHelpButtonShow': "Voir l'image finale", 'textHelpButtonHide': "Cacher l'image finale", 'onstart': null, 'onend': null }; var parameters=$.extend(defaults,options); parameters.rows=Math.max(1,parameters.rows); parameters.columns=Math.max(1,parameters.columns); //Format time in seconds to HH"MM'SS function formatTime(s) { s=parseInt(s,10); var h=Math.floor(s/3600); s-=h*3600; var formatTime=(h>9?h:'0'+h)+'"'; var m=Math.floor(s/60); s-=m*60; formatTime+=(m>9?m:'0'+m)+'\''; return formatTime+((s>9?s:'0'+s)); } //Function waiting for the loading of the image function loading() { if($(this).is('img')) { //If the image is not loaded, we have to wait for it if(this.complete) { init($(this)); } else { $(this).load(function() { init($(this)); }); } } } function init(obj) { //We store the image dimensions... var width=obj.width(); var height=obj.height(); //... and the source of the image var src=obj.attr('src'); //We calculate the pieces dimensions var pieceWidth=width/parameters.columns; var pieceHeight=height/parameters.rows; //And the height of the box containing all the pieces var sizeBox=pieceHeight>height/2?pieceHeight:height/2; //The main container var game=$('').css( { //'width': width+'px', 'display': 'inline-block', 'position': 'relative' }).addClass('jPuzzle-game').insertAfter(obj); //The tag containing the puzzle var puzzle=$('').css( { 'width': width+'px', 'height': height+'px', 'display': 'inline-block', 'position': 'relative' }).addClass('jPuzzle-container').appendTo(game); //The tag displaying the image as a background var image=$('').css( { 'width': width+'px', 'height': height+'px', 'opacity': 1, 'background-image': 'url("'+src+'")', 'display': 'inline-block' }).addClass('jPuzzle-image').appendTo(puzzle); //Box where the pieces will be generated var box=$('').css( { //'width': width+'px', 'minHeight': sizeBox+'px', 'display': 'inline-block', 'position': 'absolute', "bottom":"-170px", "left":"100px" }).addClass('jPuzzle-box').droppable( { 'accept': '.jPuzzle-piece', 'addClasses': false, 'drop': function(event,ui) { var piece=ui.draggable; var lastLocation=$.data(piece[0],'lastLocation'); if(lastLocation[0]!=this) { /* If the piece comes from a puzzle location, we have to adjust its position */ piece.appendTo($(this)); piece.css( { 'left': '+='+lastLocation.position().left, 'top':(piece.position().top+lastLocation.position().top-puzzle.outerHeight())+'px' }); $.data(piece[0],'lastLocation',$(this)); } } }).hide().insertAfter(puzzle); //Foot bar var infos=$('').css( { 'width': width+'px', 'display': 'inline-block' }).addClass('jPuzzle-infos').insertAfter(box); //The data-time attribute will contain the elapsed time var infosTime=$('').addClass('jPuzzle-time').attr('data-time',0).hide().appendTo(infos); var playButton=$('').val(parameters.textPlayButton).addClass('jPuzzle-playButton').click(function() { startGame(); $(this).hide(); }).appendTo(infos); if(parameters.showHelpButton) { var helpButton=$('').val(parameters.textHelpButtonShow).addClass('jPuzzle-helpButton').toggle(function() { //The final image is not displayed, we show it! $(this).val(parameters.textHelpButtonHide).attr('disabled',true); image.animate( { 'opacity': 1 },'slow',function() { helpButton.removeAttr('disabled'); }); //We hide the pieces of the puzzle to avoid the cheating $('.jPuzzle-piece').hide(); },function() { //The final image is displayed, we hide it! $(this).val(parameters.textHelpButtonShow).attr('disabled',true); image.animate( { 'opacity': (parameters.help?0.1:0) },'slow',function() { helpButton.removeAttr('disabled'); }); $('.jPuzzle-piece').show(); }).hide().appendTo(infos); } //And we remove the initial image! obj.remove(); var timer; function startGame() { if(parameters.onstart) { parameters.onstart(game); } box.show(); infosTime.show(); infosTime.text('00"00\'00'); if(parameters.showHelpButton) { helpButton.show(); } image.animate( { 'opacity': (parameters.help?0.1:0) },'slow'); for(var i=0;i').addClass('jPuzzle-piece') .attr('data-num',i+'_'+j).css( { 'width': pieceWidth+'px', 'height': pieceHeight+'px', 'display': 'block', 'left': Math.floor((Math.random()*(maxX+1)))+'px', 'top': Math.floor((Math.random()*(maxY+1)))+'px', 'z-index': Math.floor((Math.random()*(10))+1), 'background-image': 'url("'+src+'")', 'cursor': 'move', 'background-position': (-j*pieceWidth)+'px '+(-i*pieceHeight)+'px', 'position': 'absolute' }).draggable( { 'start': function(event,ui) { /* If the piece is already on its good location, we stop the dragging */ if($.data(this,'ok') && parameters.fixed) { return false; } //Otherwise, we put the piece in front of the others var zIndex=0; game.find('.jPuzzle-piece').each(function() { var x=parseInt($(this).css('z-index')); zIndex=Math.max(x,zIndex); }); $(this).css('z-index',zIndex+1); }, 'revert': 'invalid', 'containment': game }).appendTo(box); $.data(piece[0],'ok',false); $.data(piece[0],'lastLocation',box); var location=$('').addClass('jPuzzle-location') .attr('data-num',i+'_'+j).css( { 'width': pieceWidth+'px', 'height': pieceHeight+'px', 'left': j*pieceWidth+'px', 'top': i*pieceHeight+'px', 'display': 'block', 'position': 'absolute' }).droppable( { 'accept': function(ui) { /* We accept only puzzle pieces, and only if the location is empty We also check if the piece and the location are in the same puzzle game */ return (ui.hasClass('jPuzzle-piece') && $(this).is(':empty') && $(this).parents('.jPuzzle-game')[0]==ui.parents('.jPuzzle-game')[0]); }, 'addClasses': false, 'hoverClass': 'jPuzzle-location-hover', 'drop': function(event,ui) { var obj=$(this); ui.draggable.appendTo(obj); $.data(ui.draggable[0],'lastLocation',obj); ui.draggable.css( { 'left': '0px', 'top': '0px' }); /* If piece and location have the same data-num attribute, then the piece is correctly placed !*/ $.data(ui.draggable[0],'ok',false); if(obj.attr('data-num')==ui.draggable.attr('data-num')) { $.data(ui.draggable[0],'ok',true); /* If we can't remove a piece which is correctly placed, we can apply a small effect on it*/ if(parameters.fixed) { /* We modify the cursor to indicate the piece is not longer draggable */ ui.draggable.css('cursor','default'); /* This span will add a "flash effect" on the piece */ $('').css( { 'position': 'absolute', 'left': '0px', 'top': '0px', 'display':'block', 'width': '100%', 'height': '100%', 'background-color': '#fff' }).appendTo(ui.draggable) .fadeOut(2000,function() { $(this).remove(); }); } //The player has correctly placed one piece: what about the others? var total=0; game.find('.jPuzzle-piece').each(function() { if(!$.data(this,'ok')) { return false; } total++; }); if(total==parameters.rows*parameters.columns) { endGame(); } } } }).appendTo(puzzle); } } timer=setInterval(function() { infosTime.attr('data-time',parseInt(infosTime.attr('data-time'),10)+1); infosTime.text(formatTime(infosTime.attr('data-time'))); },1000); } function endGame() { //We stop the timer clearInterval(timer); var time=parseInt(infosTime.attr('data-time'),10); //We set the puzzle to its initial state box.hide(); game.find('.jPuzzle-piece,.jPuzzle-location').remove(); image.css('opacity','1'); if(parameters.showHelpButton) { helpButton.hide(); } infosTime.hide().attr('data-time',0); playButton.show(); //We eventually call a function defined by the user if(parameters.onend) { //We send to this function three useful variables parameters.onend(game,time,formatTime(time)); } } } return this.each(loading); }; })(jQuery);