This commit is contained in:
362
IQapTcha/Plugin.php
Normal file
362
IQapTcha/Plugin.php
Normal 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
29
IQapTcha/README.md
Normal 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,能有效防止垃圾评论 和 机器人
|
||||
- 滑动解锁代替验证码,方便且大气
|
||||
45
IQapTcha/static/QapTcha.jquery.css
Normal file
45
IQapTcha/static/QapTcha.jquery.css
Normal 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
|
||||
}
|
||||
96
IQapTcha/static/QapTcha.jquery.js
Normal file
96
IQapTcha/static/QapTcha.jquery.js
Normal 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
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
222
IQapTcha/static/jquery.ui.touch.js
vendored
Normal 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
BIN
IQapTcha/static/sprites.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.1 KiB |
Reference in New Issue
Block a user