/**
* visualCaptcha JS file by emotionLoop - 2013.08.17
*
* This file handles the JS for the visualCaptcha plugin.
*
* This license applies to this file and others without reference to any other license.
*
* @author emotionLoop | http://emotionloop.com
* @link http://visualcaptcha.net
* @package visualCaptcha
* @license GNU GPL v3
* @version 4.2.0
*/
(function( $ ) {
"use strict";
// "Global" variables for feature detection
var isMobile = false;
var isRetina = false;
var isOld = false;
var supportsAudio = false;
var userAgent = navigator.userAgent || 'Unknown';
// Known Mobile User Agents
var mobileRegExp = new RegExp( 'iPhone|iPad|iPod|Android|Windows (Phone|CE)|Bada|Meego|webOS|Palm|BlackBerry|Nokia|Symbian|PocketPC|Smartphone|Mobile', 'i' );
// User Agents that don't support background gradient + shadow + text-shadow + rounded corners
var oldRegExp = new RegExp( 'MSIE (6|7|8|9)', 'i' );
// Check if the user agent is a mobile one
if ( mobileRegExp.test(userAgent) ) {
isMobile = true;
}
// Check if the user agent is an old one
if ( oldRegExp.test(userAgent) ) {
isOld = true;
}
// Check if the device is retina-like
if ( window.devicePixelRatio !== undefined && window.devicePixelRatio > 1 ) {
isRetina = true;
}
// Check if the device supports audio, for accessibility
try {
var audioElement = document.createElement('audio');
if ( audioElement.canPlayType ) {
supportsAudio = true;
}
} catch( e ) {}
// If the device is old, add the img class to the dropzone so a background image is used instead
if ( isOld ) {
$('div.eL-captcha > div.eL-where2go').addClass( 'img' );
// Remove the paragraph
$('div.eL-captcha > div.eL-where2go > p').remove();
}
// If the device is retina-like, update the img src's and the dropzone class
if ( isRetina ) {
$('div.eL-captcha img').each( function( index, element ) {
if ( ! $(element).attr('src') ) {
return false;
}
var newImageSRC = $(element).attr( 'src' );
// If the images are the choices, add a &retina=1 to ask for the retina images
if ( newImageSRC.indexOf('.php') !== -1 ) {
newImageSRC = newImageSRC + '&retina=1';
} else {
// Otherwise add @2x to the path
newImageSRC = newImageSRC.replace( /(.+)(\.\w{3,4})$/, "$1@2x$2" );
}
$.ajax({
url: newImageSRC,
type: "HEAD",
success: function() {
$(element).attr( 'src', newImageSRC );
}
});
});
$('div.eL-captcha > div.eL-where2go').addClass( 'retina' );
}
// Check if the browser supports audio HTML5 tag for accessibility
if ( ! supportsAudio ) {
$('div.eL-captcha > .eL-accessibility').hide();
} else {
$('div.eL-captcha > p.eL-accessibility a').on( 'click.visualCaptcha touchstart.visualCaptcha', function( event ) {
event.preventDefault();
if ( ! $('div.eL-captcha > div.eL-accessibility').is(':visible') ) {
// Automatically load and play the audio file
$('div.eL-captcha > div.eL-accessibility > audio').each(function() {
this.load();
this.play();
});
// Generate the input for the accessibility answer
if ( ! $('#' + window.vCVals.a).length ) {
var validAccessibleElement = '';
$('div.eL-captcha > div.eL-accessibility > p').after( validAccessibleElement );
}
}
$('div.eL-captcha > p.eL-explanation').stop().slideToggle('fast');
$('div.eL-captcha > div.eL-possibilities').stop().slideToggle('fast');
$('div.eL-captcha > div.eL-where2go').stop().slideToggle('fast');
$('div.eL-captcha > div.eL-accessibility').stop().slideToggle('fast');
});
}
// If it's not mobile, load normal drag/drop behavior
if ( ! isMobile ) {
// Enable dragging on images
$('div.eL-captcha > div.eL-possibilities > img').draggable({ opacity: 0.6, revert: 'invalid' });
// Enable the images to be dropped their initial place, so users can switch images
$('div.eL-captcha > div.eL-possibilities').droppable({
drop: function( event, ui ) {
// If there still isn't any image in the drop area, don't allow rearranging them. No playing! :P
if ( ! $('#' + window.vCVals.n).length ) {
return false;
}
// If the image is being dropped back and was in the drop area, we need to remove the input with its answer
if ( $('#' + window.vCVals.n).val() === $(ui.draggable).data('value') ) {
$('#' + window.vCVals.n).remove();
}
// Re-enable dropping to the drop area
$('div.eL-captcha > div.eL-where2go').droppable( 'enable' );
},
accept: 'div.eL-captcha > div.eL-possibilities > img'
});
// Enable the images to be dropped in the drop area
$('div.eL-captcha > div.eL-where2go').droppable({
drop: function( event, ui ) {
// Don't allow the image to be dropped if there's one there already
if ( $('#' + window.vCVals.n).length ) {
return false;
}
// Generate the input with the answer
var validElement = '';
$('#' + window.vCVals.f).append( validElement );
// Disable dropping
$(this).droppable( 'disable' );
},
accept: 'div.eL-captcha > div.eL-possibilities > img'
});
} else {
// If it's mobile, we're going to make it possible to just tap an image and move it to the drop area automagically
$('div.eL-captcha > p.eL-explanation > span.desktopText').hide();// Hide desktop text
$('div.eL-captcha > p.eL-explanation > span.mobileText').show();// Show mobile text
$('div.eL-captcha > div.eL-possibilities > img').on('click touchstart', function( event ) {// Add tap behavior, but keep click in case that also works. There is no "duplication" problem since this code won't run twice
event.preventDefault();
var dropzoneSelector = 'div.eL-captcha > div.eL-where2go';
var clickedImageSelector = this;
var xPos = $(dropzoneSelector).position().left + parseInt( $(dropzoneSelector).css('margin-left'), 10 ) + parseInt( $(dropzoneSelector).css('padding-left'), 10 ) - parseInt( $(clickedImageSelector).css('margin-left'), 10 ) - parseInt( $(clickedImageSelector).css('padding-left'), 10 );
var yPos = $(dropzoneSelector).position().top + parseInt( $(dropzoneSelector).css('margin-top'), 10 ) + parseInt( $(dropzoneSelector).css('padding-top'), 10 ) - parseInt( $(clickedImageSelector).css('margin-top'), 10 ) - parseInt( $(clickedImageSelector).css('padding-top'), 10 );
var wDim = $(dropzoneSelector).width();
var hDim = $(dropzoneSelector).height();
var iwDim = $(clickedImageSelector).width();
var ihDim = $(clickedImageSelector).height();
// If it was dragged already to the droppable zone, move it back to the beginning
if ( $(clickedImageSelector).css('position') === 'absolute' ) {
if ( ! $('#' + window.vCVals.n).length ) {
return false;
}
// If the image is being dropped back and was in the drop area, we need to remove the input with its answer
if ( $('#' + window.vCVals.n).val() === $(clickedImageSelector).data('value') ) {
$('#' + window.vCVals.n).remove();
}
$(clickedImageSelector).css({
'position': 'relative',
'left': 'auto',
'top': 'auto'
});
} else {
// Don't allow the image to be dropped if there's one there already
if ( $('#' + window.vCVals.n).length ) {
return false;
}
// Generate the input with the answer
var validElement = '';
$('#' + window.vCVals.f).append(validElement);
// Calculate the middle of the dropzone
var xPos2Go = Math.round(xPos + (wDim/2) - (iwDim/2));
var yPos2Go = Math.round(yPos + (hDim/2) - (ihDim/2));
$(clickedImageSelector).css({
'position': 'absolute',
'left': xPos2Go,
'top': yPos2Go
});
}
});
}
})( jQuery );