Initial commit
Some checks failed
定时更新GitHub源插件 / 自动更新GitHub插件 (push) Has been cancelled

This commit is contained in:
chorblack
2026-03-07 11:19:25 +08:00
commit e75f275ef4
4484 changed files with 645480 additions and 0 deletions

362
IQapTcha/Plugin.php Normal file
View File

@@ -0,0 +1,362 @@
<?php
/**
* IQapTcha 评论滑动解锁
*
* @package IQapTcha
* @author Byends
* @version 1.1.2
* @link http://www.byends.com
*/
class IQapTcha_Plugin implements Typecho_Plugin_Interface
{
/**
* 激活插件方法,如果激活失败,直接抛出异常
*
* @access public
* @return void
* @throws Typecho_Plugin_Exception
*/
public static function activate()
{
Typecho_Plugin::factory('Widget_Feedback')->comment = array('IQapTcha_Plugin', 'filter');
Typecho_Plugin::factory('Widget_Archive')->header = array('IQapTcha_Plugin', 'headerScript');
Typecho_Plugin::factory('Widget_Archive')->footer = array('IQapTcha_Plugin', 'footerScript');
Helper::addRoute('iQapTcha4tyepcho', '/'.self::getCheckStr('slug'), 'IQapTcha_Plugin');
return _t('评论滑动解锁插件启用成功,请配置 评论滑动解锁 相关项');
}
/**
* 禁用插件方法,如果禁用失败,直接抛出异常
*
* @static
* @access public
* @return void
* @throws Typecho_Plugin_Exception
*/
public static function deactivate()
{
Helper::removeRoute('iQapTcha4tyepcho');
}
public static function execute()
{
$aResponse['code'] = false;
$options = Helper::options();
$iQapTchaOpt = $options->plugin('IQapTcha');
$action = self::getCheckStr('action');
$req = Typecho_Request::getInstance();
if ( !$req->isAjax() || !$req->isPost() || !$req->is('action='.$action) ) {
$msg = $iQapTchaOpt->opt_lock_txt;
$aResponse['msg'] = $msg;
}
else{
$aResponse['code'] = true;
$msg = $iQapTchaOpt->opt_unlock_txt;
$aResponse['msg'] = $msg;
@session_start();
$_SESSION[$action] = $req->get('iQaptcha');
}
echo json_encode($aResponse);
exit;
}
/**
* 获取插件配置面板
*
* @access public
* @param Typecho_Widget_Helper_Form $form 配置面板
* @return void
*/
public static function config(Typecho_Widget_Helper_Form $form)
{
//jquery 设置
$opt_jq_set = new Typecho_Widget_Helper_Form_Element_Radio('opt_jq_set', array('0'=> '自己处理', '1'=> '自动载入'), 1, 'jQuery 来源', '如果选择 【自动载入】会从开放静态文件CDN自动载入 jQurey 1.8.3 到 header<br />http://cdn.staticfile.org/jquery/1.8.3/jquery.min.js');
$form->addInput($opt_jq_set);
//IQapTcha 配置
$opt_admin_unlock = new Typecho_Widget_Helper_Form_Element_Radio('opt_admin_unlock', array("true" => "", "false" => ""), "false", _t('博主是否无须上锁'), '若选择 【是】,博主登录后 IQapTcha 将不再显示,且不进行相应的 session 检验');
$form->addInput($opt_admin_unlock);
$opt_autoSubmit = new Typecho_Widget_Helper_Form_Element_Radio('opt_autoSubmit', array("true" => "", "false" => ""), "false", _t('解锁后立即提交评论'), '若选择 【是】IQapTcha 解锁后将立即自动提交评论');
$form->addInput($opt_autoSubmit);
$opt_lock_txt = new Typecho_Widget_Helper_Form_Element_Text('opt_lock_txt', NULL, '发表评论前,请滑动滚动条解锁', _t('IQapTcha 解锁前的提示'));
$form->addInput( $opt_lock_txt->addRule('required', _t('IQapTcha 解锁前的提示不能为空')) );
$opt_unlock_txt = new Typecho_Widget_Helper_Form_Element_Text('opt_unlock_txt', NULL, '已解锁,可以发表评论了', _t('IQapTcha 解锁后的提示'));
$form->addInput( $opt_unlock_txt->addRule('required', _t('IQapTcha 解锁后的提示不能为空')) );
$opt_autoRevert = new Typecho_Widget_Helper_Form_Element_Radio('opt_autoRevert', array("true" => "", "false" => ""), "true", _t('滚动条是否自动回滚'), '在拖动 IQapTcha 滚动条中途释放时,滚动条是否自动回滚');
$form->addInput($opt_autoRevert);
$opt_disabledSubmit = new Typecho_Widget_Helper_Form_Element_Radio('opt_disabledSubmit', array("false" => "", "true" => ""), "false", _t('提交按钮是否可用'), 'IQapTcha 未解锁时,评论提交按钮是否可用');
$form->addInput($opt_disabledSubmit);
//屏蔽IP操作
$opt_ip = new Typecho_Widget_Helper_Form_Element_Radio('opt_ip', array("none" => "无动作", "waiting" => "标记为待审核", "spam" => "标记为垃圾", "abandon" => "评论失败"), "abandon",
_t('屏蔽IP操作'), "如果评论发布者的IP在屏蔽IP段将执行该操作");
$form->addInput($opt_ip);
$opt_ip_txt = new Typecho_Widget_Helper_Form_Element_Text('opt_ip_txt', NULL, '你的IP已被管理员屏蔽', _t('屏蔽IP后的提示'), _t('仅当屏蔽IP操作为 【评论失败】 时有效'));
$form->addInput( $opt_ip_txt->addRule('required', _t('屏蔽IP后的提示不能为空')) );
$wordsIp = new Typecho_Widget_Helper_Form_Element_Textarea('words_ip', NULL, "0.0.0.0",
_t('屏蔽IP'), _t('多条IP请用换行符隔开<br />支持用*号匹配IP段192.168.*.*'));
$form->addInput($wordsIp);
//非中文评论操作
$opt_nocn = new Typecho_Widget_Helper_Form_Element_Radio('opt_nocn', array("none" => "无动作", "waiting" => "标记为待审核", "spam" => "标记为垃圾", "abandon" => "评论失败"), "abandon",
_t('非中文评论操作'), "如果评论中不包含中文,则强行按该操作执行");
$form->addInput($opt_nocn);
$opt_nocn_txt = new Typecho_Widget_Helper_Form_Element_Text('opt_nocn_txt', NULL, '评论内容不能少于2个汉字', _t('非中文评论的提示'), _t('仅当非中文评论操作为 【评论失败】 时有效'));
$form->addInput( $opt_nocn_txt->addRule('required', _t('非中文评论的提示不能为空')) );
//禁止词汇操作
$opt_ban = new Typecho_Widget_Helper_Form_Element_Radio('opt_ban', array("none" => "无动作", "waiting" => "标记为待审核", "spam" => "标记为垃圾", "abandon" => "评论失败"), "abandon",
_t('禁止词汇操作'), "如果评论中包含禁止词汇列表中的词汇,将执行该操作");
$form->addInput($opt_ban);
$opt_ban_txt = new Typecho_Widget_Helper_Form_Element_Text('opt_ban_txt', NULL, '评论内容包含禁止词汇!', _t('禁止词汇后的提示'), _t('仅当禁止词汇操作为 【评论失败】 时有效'));
$form->addInput( $opt_ban_txt->addRule('required', _t('禁止词汇后的提示不能为空')) );
$words_ban = new Typecho_Widget_Helper_Form_Element_Textarea('words_ban', NULL, "fuck\n操你妈\n[url\n[/url]",
_t('禁止词汇'), _t('多条词汇请用换行符隔开'));
$form->addInput($words_ban);
//感词汇操作
$opt_chk = new Typecho_Widget_Helper_Form_Element_Radio('opt_chk', array("none" => "无动作", "waiting" => "标记为待审核", "spam" => "标记为垃圾", "abandon" => "评论失败"), "abandon",
_t('敏感词汇操作'), "如果评论中包含敏感词汇列表中的词汇,将执行该操作");
$form->addInput($opt_chk);
$opt_chk_txt = new Typecho_Widget_Helper_Form_Element_Text('opt_chk_txt', NULL, '评论内容包含敏感词汇!', _t('包含敏感词汇的提示'), _t('仅当敏感词汇操作为 【评论失败】 时有效'));
$form->addInput( $opt_chk_txt->addRule('required', _t('包含敏感词汇的提示不能为空')) );
$words_chk = new Typecho_Widget_Helper_Form_Element_Textarea('words_chk', NULL, "http://",
_t('敏感词汇'), _t('多条词汇请用换行符隔开<br />注意:如果词汇同时出现于禁止词汇,则执行禁止词汇操作'));
$form->addInput($words_chk);
}
/**
* 个人用户的配置面板
*
* @access public
* @param Typecho_Widget_Helper_Form $form
* @return void
*/
public static function personalConfig(Typecho_Widget_Helper_Form $form){}
/**
* 自定义 header
*
* @access public
* @return void
*/
public static function headerScript()
{
if (Typecho_Widget::widget('Widget_Archive')->is('single')) {
$user = Typecho_Widget::widget('Widget_User');
$options = Helper::options();
$iQapTchaOpt = $options->plugin('IQapTcha');
if( $iQapTchaOpt->opt_admin_unlock == 'false' || !($user->hasLogin() && $user->pass('administrator'))) {
$pluginUrl = Typecho_Common::url('IQapTcha/static', $options->pluginUrl);
if ($iQapTchaOpt->opt_jq_set == 1) {
echo "<script type=\"text/javascript\" src=\"http://cdn.staticfile.org/jquery/1.8.3/jquery.min.js\"></script>\n";
}
echo '<link rel="stylesheet" type="text/css" media="all" href="'.$pluginUrl.'/QapTcha.jquery.css" />'."\n";
}
}
}
/**
* 自定义 footer
*
* @access public
* @return void
*/
public static function footerScript()
{
if (Typecho_Widget::widget('Widget_Archive')->is('single')) {
$user = Typecho_Widget::widget('Widget_User');
$options = Helper::options();
$iQapTchaOpt = $options->plugin('IQapTcha');
if( $iQapTchaOpt->opt_admin_unlock == 'false' || !($user->hasLogin() && $user->pass('administrator'))) {
$pluginUrl = Typecho_Common::url('IQapTcha/static', $options->pluginUrl);
$url = Typecho_Router::url('iQapTcha4tyepcho', array(), $options->index);
$action = self::getCheckStr('action');
$script = <<<EOT
\n<script type="text/javascript" src="{$pluginUrl}/jquery-ui.js"></script>
<script type="text/javascript" src="{$pluginUrl}/jquery.ui.touch.js"></script>
<script type="text/javascript" src="{$pluginUrl}/QapTcha.jquery.js"></script>
<script>
(function($){
$(document).ready(function() {
if ( ! $('#QapTcha').is('div')) {
$('textarea').parent().before('<div id="QapTcha"></div>\\n');
}
$('#QapTcha').QapTcha({
txtLock : '{$iQapTchaOpt->opt_lock_txt}',
txtUnlock : '{$iQapTchaOpt->opt_unlock_txt}',
disabledSubmit : {$iQapTchaOpt->opt_disabledSubmit},
autoRevert: {$iQapTchaOpt->opt_autoRevert},
autoSubmit : {$iQapTchaOpt->opt_autoSubmit},
url : '{$url}',
action : '{$action}'
});
});
})(jQuery)
</script>
EOT;
echo $script;
}
}
}
/**
* 评论过滤器
*
*/
public static function filter($comment, $post)
{
$user = Typecho_Widget::widget('Widget_User');
$options = Helper::options();
$iQapTchaOpt = $options->plugin('IQapTcha');
$opt = 'none';
$error = '';
$action = self::getCheckStr('action');
$req = Typecho_Request::getInstance();
//登录检测session 检测
if( $iQapTchaOpt->opt_admin_unlock == 'false' || !($user->hasLogin() && $user->pass('administrator'))) {
@session_start();
if (!isset($_SESSION[$action]) || !$_SESSION[$action] || !$req->is('iQapTcha='.$_SESSION[$action]) ) {
throw new Typecho_Widget_Exception($iQapTchaOpt->opt_lock_txt);
}
}
//屏蔽IP段处理
if ($iQapTchaOpt->opt_ip != "none") {
if (IQapTcha_Plugin::checkIp($iQapTchaOpt->words_ip, $comment['ip'])) {
$error = $iQapTchaOpt->opt_ip_txt;
$opt = $iQapTchaOpt->opt_ip;
}
}
//纯英文评论处理
if ($iQapTchaOpt->opt_nocn != "none") {
$result = preg_match_all("/[\x{4e00}-\x{9fa5}]/u", $comment['text'], $txt);
if ($result == false || $result < 2) {
$error = $iQapTchaOpt->opt_nocn_txt;
$opt = $iQapTchaOpt->opt_nocn;
}
}
//检查禁止词汇
if ($iQapTchaOpt->opt_ban != "none") {
if (IQapTcha_Plugin::checkIn($iQapTchaOpt->words_ban, $comment['text'])) {
$error = $iQapTchaOpt->opt_ban_txt;
$opt = $iQapTchaOpt->opt_ban;
}
}
//检查敏感词汇
if ($iQapTchaOpt->opt_chk != "none") {
if (IQapTcha_Plugin::checkIn($iQapTchaOpt->words_chk, $comment['text'])) {
$error = $iQapTchaOpt->opt_chk_txt;
$opt = $iQapTchaOpt->opt_chk;
}
}
//执行操作
if ($opt == "abandon") {
Typecho_Cookie::set('__typecho_remember_text', $comment['text']);
throw new Typecho_Widget_Exception($error);
}
else if ($opt == "spam") {
$comment['status'] = 'spam';
}
else if ($opt == "waiting") {
$comment['status'] = 'waiting';
}
Typecho_Cookie::delete('__typecho_remember_text');
return $comment;
}
/**
* 获取 安全检测 字符串
* @param string $type
* @return string
*/
private static function getCheckStr($type)
{
$options = Helper::options();
switch ($type) {
case 'slug':
$chkStr = md5($options->siteUrl.'_slug_iQapTcha4tyepcho_admin_');
break;
case 'action':
$chkStr = md5($options->siteUrl.'_action_iQapTcha4tyepcho_');
break;
default:
$chkStr = '';
break;
}
return $chkStr;
}
/**
* 检查$str中是否含有$wordsStr中的词汇
*
*/
private static function checkIn($wordsStr, $str)
{
$words = explode("\n", $wordsStr);
if (empty($words)) {
return false;
}
foreach ($words as $word) {
if (false !== strpos($str, trim($word))) {
return true;
}
}
return false;
}
/**
* 检查$ip中是否在$wordsIp的IP段中
*
*/
private static function checkIp($wordsIp, $ip)
{
$words = explode("\n", $wordsIp);
if (empty($words)) {
return false;
}
foreach ($words as $word) {
$word = trim($word);
if (false !== strpos($word, '*')) {
$word = "/^".str_replace('*', '\d{1,3}', $word)."$/";
if (preg_match($word, $ip)) {
return true;
}
} else {
if (false !== strpos($ip, $word)) {
return true;
}
}
}
return false;
}
}

29
IQapTcha/README.md Normal file
View File

@@ -0,0 +1,29 @@
Typecho滑动解锁插件IQapTcha
========
### 说明
- IQapTcha 基于jquery 插件 QapTcha 进行开发但相应JS有较大的更改。
- 集成了 Hanny 评论过滤器 CommentFilter 的所有功能并进行了优化比如评论内容必须包含至少2个中文所以安装此插件后无须再安装 CommentFilter 插件。
- 这个插件通过 session 来控制是否能够评论,应该能大幅度 防止垃圾评论和机器人。
- 安装时,如果想要调整滚动条的位置,请把`<div id="QapTcha"></div>`放在你想放的任意位置。如果没有指定,则会自动出现在评论框上方。
### 安装说明
- 下载插件
- 将插件上传到 `/usr/plugins/` 这个目录下
- 登陆后台,在“控制台”下拉菜单中进入“插件管理”
- 启用插件并进行相应配置
### 升级日志
> 修正jquery-ui.js报错和textarea自动选取问题。
#### 1.1.0 at 2012-07-12
- 增加博主可以不需要解锁的功能,可以自由设置
- 增加解锁后立即自动提交评论的功能,可以自由设置
- 后台设置区增加一些必要的检验
**安装时请先禁用之前的版本,然后上传覆盖激活**
#### 1.0.0 at 2012-07-10
- 使用Ajax操作 Session能有效防止垃圾评论 和 机器人
- 滑动解锁代替验证码,方便且大气

View File

@@ -0,0 +1,45 @@
/*****************/
/** QapTcha CSS **/
/*****************/
#QapTcha {
margin-top:10px;
}
#QapTcha .clr{ clear:both}
#QapTcha #bgSlider {
width:202px;
height:22px;
background:transparent url('sprites.png') no-repeat 0 -22px;
float:left
}
#QapTcha #Slider {
width:48px;
height:22px;
background:transparent url('sprites.png') no-repeat -32px 0;
cursor:e-resize
}
#QapTcha #Icons {
float:left;
width:16px;
height:16px;
background:transparent url('sprites.png') no-repeat 0 0;
margin-top:3px;
margin-left:10px;
}
#QapTcha #TxtStatus {
float:left;
width:202px;
margin:4px 0 0 5px;
font-size:12px;
}
/** States **/
#QapTcha .dropSuccess {
color:#67A611
}
#QapTcha .dropError {
color:#bb2828
}

View File

@@ -0,0 +1,96 @@
/************************************************************************
*************************************************************************
@Name : QapTcha - jQuery Plugin
@Revison : 2.5
@Date : 26/01/2011
@Author: Surrel Mickael (www.myjqueryplugins.com - www.msconcept.fr)
@License : Open Source - MIT License : http://www.opensource.org/licenses/mit-license.php
**************************************************************************
*************************************************************************/
jQuery.QapTcha = {
build : function(options)
{
var defaults = {
txtLock : '发表评论前,请滑动滚动条解锁',
txtUnlock : '已解锁,可以发表评论了',
disabledSubmit : false,
autoRevert : true,
autoSubmit : false,
url : '/iQapTcha4tyepcho',
action : 'iQapTcha4tyepcho'
};
if(this.length>0)
return jQuery(this).each(function(i) {
/** Vars **/
var
opts = $.extend(defaults, options),
$this = $(this),
form = $('form').has($this),
Clr = jQuery('<div>',{'class':'clr'}),
bgSlider = jQuery('<div>',{id:'bgSlider'}),
Slider = jQuery('<div>',{id:'Slider'}),
Icons = jQuery('<div>',{id:'Icons'}),
TxtStatus = jQuery('<div>',{id:'TxtStatus','class':'dropError',text:opts.txtLock}),
inputQapTcha = jQuery('<input>',{name:'iQapTcha',value:'',type:'hidden'});
/** Disabled submit button **/
if(opts.disabledSubmit) form.find('input[type=\'submit\']').attr('disabled','disabled');
/** Construct DOM **/
bgSlider.appendTo($this);
Icons.insertAfter(bgSlider);
TxtStatus.insertAfter(Icons);
Clr.insertAfter(TxtStatus);
inputQapTcha.appendTo($this);
Slider.appendTo(bgSlider);
$this.show();
Slider.draggable({
revert: function(){
if(opts.autoRevert)
{
if(parseInt(Slider.css("left")) > 150) return false;
else return true;
}
},
containment: bgSlider,
axis:'x',
stop: function(event,ui){
if(ui.position.left > 150)
{
inputQapTcha.val(generatePass(32));
$.post(opts.url,{
action : opts.action,
iQaptcha : inputQapTcha.val()
},
function(data) {
if(data.code)
{
Slider.draggable('disable').css('cursor', 'default');
TxtStatus.text(opts.txtUnlock).addClass('dropSuccess').removeClass('dropError');
Icons.css('background-position', '-16px 0');
form.find('input[type=\'submit\']').removeAttr('disabled');
if(opts.autoSubmit) form.find('input[type=\'submit\']').trigger('click');
}
},'json');
}
}
});
function generatePass(nb) {
var chars = 'azertyupqsdfghjkmwxcvbn23456789AZERTYUPQSDFGHJKMWXCVBN_-#@';
var pass = '';
for(i=0;i<nb;i++){
var wpos = Math.round(Math.random()*chars.length);
pass += chars.substring(wpos,wpos+1);
}
return pass;
}
});
}
}; jQuery.fn.QapTcha = jQuery.QapTcha.build;

25
IQapTcha/static/jquery-ui.js vendored Normal file

File diff suppressed because one or more lines are too long

222
IQapTcha/static/jquery.ui.touch.js vendored Normal file
View File

@@ -0,0 +1,222 @@
/**
* jQuery.UI.iPad plugin
* Copyright (c) 2010 Stephen von Takach
* licensed under MIT.
* Date: 27/8/2010
*
* Project Home:
* http://code.google.com/p/jquery-ui-for-ipad-and-iphone/
*/
$(function() {
//
// Extend jQuery feature detection
//
$.extend($.support, {
touch: "ontouchend" in document
});
//
// Hook up touch events
//
if ($.support.touch) {
var obj = document.getElementsByClassName('QapTcha');
for(i=0; i<obj.length;i++){
obj[i].addEventListener("touchstart", iPadTouchHandler, false);
obj[i].addEventListener("touchmove", iPadTouchHandler, false);
obj[i].addEventListener("touchend", iPadTouchHandler, false);
obj[i].addEventListener("touchcancel", iPadTouchHandler, false);
}}
});
var lastTap = null; // Holds last tapped element (so we can compare for double tap)
var tapValid = false; // Are we still in the .6 second window where a double tap can occur
var tapTimeout = null; // The timeout reference
function cancelTap() {
tapValid = false;
}
var rightClickPending = false; // Is a right click still feasible
var rightClickEvent = null; // the original event
var holdTimeout = null; // timeout reference
var cancelMouseUp = false; // prevents a click from occuring as we want the context menu
function cancelHold() {
if (rightClickPending) {
window.clearTimeout(holdTimeout);
rightClickPending = false;
rightClickEvent = null;
}
}
function startHold(event) {
if (rightClickPending)
return;
rightClickPending = true; // We could be performing a right click
rightClickEvent = (event.changedTouches)[0];
holdTimeout = window.setTimeout("doRightClick();", 800);
}
function doRightClick() {
rightClickPending = false;
//
// We need to mouse up (as we were down)
//
var first = rightClickEvent,
simulatedEvent = document.createEvent("MouseEvent");
simulatedEvent.initMouseEvent("mouseup", true, true, window, 1, first.screenX, first.screenY, first.clientX, first.clientY,
false, false, false, false, 0, null);
first.target.dispatchEvent(simulatedEvent);
//
// emulate a right click
//
simulatedEvent = document.createEvent("MouseEvent");
simulatedEvent.initMouseEvent("mousedown", true, true, window, 1, first.screenX, first.screenY, first.clientX, first.clientY,
false, false, false, false, 2, null);
first.target.dispatchEvent(simulatedEvent);
//
// Show a context menu
//
simulatedEvent = document.createEvent("MouseEvent");
simulatedEvent.initMouseEvent("contextmenu", true, true, window, 1, first.screenX + 50, first.screenY + 5, first.clientX + 50, first.clientY + 5,
false, false, false, false, 2, null);
first.target.dispatchEvent(simulatedEvent);
//
// Note:: I don't mouse up the right click here however feel free to add if required
//
cancelMouseUp = true;
rightClickEvent = null; // Release memory
}
//
// mouse over event then mouse down
//
function iPadTouchStart(event) {
var touches = event.changedTouches,
first = touches[0],
type = "mouseover",
simulatedEvent = document.createEvent("MouseEvent");
//
// Mouse over first - I have live events attached on mouse over
//
simulatedEvent.initMouseEvent(type, true, true, window, 1, first.screenX, first.screenY, first.clientX, first.clientY,
false, false, false, false, 0, null);
first.target.dispatchEvent(simulatedEvent);
type = "mousedown";
simulatedEvent = document.createEvent("MouseEvent");
simulatedEvent.initMouseEvent(type, true, true, window, 1, first.screenX, first.screenY, first.clientX, first.clientY,
false, false, false, false, 0, null);
first.target.dispatchEvent(simulatedEvent);
if (!tapValid) {
lastTap = first.target;
tapValid = true;
tapTimeout = window.setTimeout("cancelTap();", 600);
startHold(event);
}
else {
window.clearTimeout(tapTimeout);
//
// If a double tap is still a possibility and the elements are the same
// Then perform a double click
//
if (first.target == lastTap) {
lastTap = null;
tapValid = false;
type = "click";
simulatedEvent = document.createEvent("MouseEvent");
simulatedEvent.initMouseEvent(type, true, true, window, 1, first.screenX, first.screenY, first.clientX, first.clientY,
false, false, false, false, 0/*left*/, null);
first.target.dispatchEvent(simulatedEvent);
type = "dblclick";
simulatedEvent = document.createEvent("MouseEvent");
simulatedEvent.initMouseEvent(type, true, true, window, 1, first.screenX, first.screenY, first.clientX, first.clientY,
false, false, false, false, 0/*left*/, null);
first.target.dispatchEvent(simulatedEvent);
}
else {
lastTap = first.target;
tapValid = true;
tapTimeout = window.setTimeout("cancelTap();", 600);
startHold(event);
}
}
}
function iPadTouchHandler(event) {
var type = "",
button = 0; /*left*/
if (event.touches.length > 1)
return;
switch (event.type) {
case "touchstart":
if ($(event.changedTouches[0].target).is("select")) {
return;
}
iPadTouchStart(event); /*We need to trigger two events here to support one touch drag and drop*/
event.preventDefault();
return false;
break;
case "touchmove":
cancelHold();
type = "mousemove";
event.preventDefault();
break;
case "touchend":
if (cancelMouseUp) {
cancelMouseUp = false;
event.preventDefault();
return false;
}
cancelHold();
type = "mouseup";
break;
default:
return;
}
var touches = event.changedTouches,
first = touches[0],
simulatedEvent = document.createEvent("MouseEvent");
simulatedEvent.initMouseEvent(type, true, true, window, 1, first.screenX, first.screenY, first.clientX, first.clientY,
false, false, false, false, button, null);
first.target.dispatchEvent(simulatedEvent);
if (type == "mouseup" && tapValid && first.target == lastTap) { // This actually emulates the ipads default behaviour (which we prevented)
simulatedEvent = document.createEvent("MouseEvent"); // This check avoids click being emulated on a double tap
simulatedEvent.initMouseEvent("click", true, true, window, 1, first.screenX, first.screenY, first.clientX, first.clientY,
false, false, false, false, button, null);
first.target.dispatchEvent(simulatedEvent);
}
}

BIN
IQapTcha/static/sprites.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB