/*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);