From 19e77d03c8be2c4e40320ddc56213c271bc3721e Mon Sep 17 00:00:00 2001 From: zorlan <285909184@qq.com> Date: Wed, 25 Sep 2019 15:45:58 +0800 Subject: [PATCH] 2.3 --- .htaccess | 22 +- SkycaijiApp/admin/behavior/Init.php | 54 +- SkycaijiApp/admin/command/Collect.php | 104 +- SkycaijiApp/admin/common.php | 66 +- SkycaijiApp/admin/controller/Api.php | 12 +- SkycaijiApp/admin/controller/App.php | 3 - SkycaijiApp/admin/controller/Backstage.php | 75 +- SkycaijiApp/admin/controller/Collected.php | 135 +- SkycaijiApp/admin/controller/Collector.php | 12 +- SkycaijiApp/admin/controller/Cpattern.php | 137 +- SkycaijiApp/admin/controller/Develop.php | 102 +- SkycaijiApp/admin/controller/Index.php | 163 +- SkycaijiApp/admin/controller/Mystore.php | 246 +- SkycaijiApp/admin/controller/Provider.php | 6 +- SkycaijiApp/admin/controller/Proxy.php | 250 ++ SkycaijiApp/admin/controller/Release.php | 4 +- SkycaijiApp/admin/controller/Setting.php | 245 +- SkycaijiApp/admin/controller/Store.php | 16 +- SkycaijiApp/admin/controller/Task.php | 64 +- SkycaijiApp/admin/controller/Taskgroup.php | 12 +- SkycaijiApp/admin/controller/Tool.php | 30 +- SkycaijiApp/admin/controller/Upgrade.php | 29 +- SkycaijiApp/admin/controller/User.php | 32 +- SkycaijiApp/admin/event/Collector.php | 17 +- SkycaijiApp/admin/event/Cpattern.php | 92 +- SkycaijiApp/admin/event/CpatternBase.php | 434 ++- SkycaijiApp/admin/event/Rapi.php | 1 - SkycaijiApp/admin/event/Rcms.php | 1 + SkycaijiApp/admin/event/Rdb.php | 1 - SkycaijiApp/admin/event/ReleaseBase.php | 81 +- SkycaijiApp/admin/event/Rfile.php | 1 - SkycaijiApp/admin/event/Rtoapi.php | 137 + SkycaijiApp/admin/lang/zh-cn.php | 9 +- SkycaijiApp/admin/model/App.php | 2 + SkycaijiApp/admin/model/CacheModel.php | 1 + SkycaijiApp/admin/model/Collector.php | 105 +- SkycaijiApp/admin/model/Config.php | 123 + SkycaijiApp/admin/model/DbCommon.php | 2 +- SkycaijiApp/admin/model/FuncApp.php | 219 ++ SkycaijiApp/admin/model/Provider.php | 5 +- SkycaijiApp/admin/model/Proxyip.php | 163 +- SkycaijiApp/admin/model/Task.php | 20 +- SkycaijiApp/admin/model/User.php | 2 +- SkycaijiApp/admin/model/Usergroup.php | 2 +- SkycaijiApp/admin/view/backstage/index.html | 37 +- SkycaijiApp/admin/view/collected/chart.html | 64 + SkycaijiApp/admin/view/collected/list.html | 6 +- SkycaijiApp/admin/view/collector/set.html | 4 + .../admin/view/common/browser_is_old.html | 2 +- .../admin/view/common/header_public.html | 3 +- SkycaijiApp/admin/view/common/main.html | 28 +- SkycaijiApp/admin/view/cpattern/browser.html | 46 + .../admin/view/cpattern/browser_json.html | 34 + SkycaijiApp/admin/view/cpattern/easymode.html | 31 + SkycaijiApp/admin/view/cpattern/field.html | 10 +- SkycaijiApp/admin/view/cpattern/process.html | 1 + .../admin/view/cpattern/process_module.html | 204 +- SkycaijiApp/admin/view/cpattern/set.html | 98 +- SkycaijiApp/admin/view/cpattern/source.html | 2 +- .../view/cpattern/test_cont_url_ajax.html | 21 +- .../admin/view/cpattern/test_source_urls.html | 70 +- SkycaijiApp/admin/view/develop/func.html | 83 + .../admin/view/index/find_password.html | 2 +- SkycaijiApp/admin/view/index/index.html | 2 +- SkycaijiApp/admin/view/mystore/app.html | 2 +- SkycaijiApp/admin/view/mystore/func.html | 94 + .../admin/view/mystore/releaseApp.html | 16 +- SkycaijiApp/admin/view/mystore/rules.html | 16 +- SkycaijiApp/admin/view/provider/list.html | 2 +- SkycaijiApp/admin/view/proxy/add.html | 37 + SkycaijiApp/admin/view/proxy/batch.html | 49 + SkycaijiApp/admin/view/proxy/list.html | 121 + SkycaijiApp/admin/view/proxy/testApi.html | 4 + SkycaijiApp/admin/view/release/set.html | 99 +- SkycaijiApp/admin/view/setting/caiji.html | 113 +- SkycaijiApp/admin/view/setting/caiji_nav.html | 8 + .../admin/view/setting/download_img.html | 109 + .../admin/view/setting/page_render.html | 7 +- SkycaijiApp/admin/view/setting/proxy.html | 155 +- SkycaijiApp/admin/view/setting/site.html | 18 +- SkycaijiApp/admin/view/setting/translate.html | 82 +- SkycaijiApp/admin/view/store/index.html | 2 +- SkycaijiApp/admin/view/task/list_list.html | 10 +- SkycaijiApp/admin/view/taskgroup/list.html | 4 +- SkycaijiApp/admin/view/tool/json_tree.html | 1 + SkycaijiApp/admin/view/user/add.html | 2 +- SkycaijiApp/admin/view/user/edit.html | 2 +- SkycaijiApp/admin/view/user/list.html | 2 +- SkycaijiApp/common.php | 71 +- SkycaijiApp/common/behavior/Init.php | 10 +- SkycaijiApp/config.php | 6 +- SkycaijiApp/extend/util/ChromeSocket.php | 2 +- SkycaijiApp/extend/util/Curl.php | 141 + SkycaijiApp/extend/util/Log.php | 3 +- SkycaijiApp/extend/util/Translator.php | 222 +- SkycaijiApp/install/controller/Index.php | 55 +- SkycaijiApp/install/controller/Upgrade.php | 4 +- SkycaijiApp/install/data/check_db | 2 +- SkycaijiApp/install/data/check_file | 2 +- SkycaijiApp/install/data/install_table | 2 +- SkycaijiApp/install/event/LocSystem.php | 62 + SkycaijiApp/install/event/UpgradeDb.php | 75 +- SkycaijiApp/install/view/common/header.html | 1 - SkycaijiApp/lang/zh-cn.php | 2 +- SkycaijiApp/public/func_app/class.tpl | 8 + app/app/config/index.html | 0 app/app/index.html | 0 app/index.html | 0 composer.json | 1 - composer.lock | 53 +- data/app/index.html | 0 data/images/index.html | 0 data/index.html | 0 data/program/backup/index.html | 0 data/program/index.html | 0 data/program/upgrade/index.html | 0 plugin/func/index.html | 0 plugin/func/process/index.html | 0 plugin/func/processIf/index.html | 0 plugin/release/cms/index.html | 1 - public/static/css/admin.css | 28 +- public/static/css/cpattern_browser.css | 113 + public/static/css/cpattern_easy.css | 28 + public/static/css/introjs.css | 506 ++++ public/static/css/store.css | 2 +- public/static/js/admin.js | 3 +- public/static/js/admin/app.js | 2 +- public/static/js/admin/collected.js | 6 +- public/static/js/admin/collector.js | 49 +- public/static/js/admin/cpattern_browser.js | 48 + public/static/js/admin/cpattern_easy.js | 33 + public/static/js/admin/develop.js | 3 +- public/static/js/admin/index.js | 7 +- public/static/js/admin/json_tree.js | 2 +- public/static/js/admin/mystore.js | 10 +- public/static/js/admin/proxy.js | 21 +- public/static/js/admin/release.js | 9 +- public/static/js/admin/store.js | 7 +- public/static/js/chart.min.js | 10 + public/static/js/common.js | 3 +- public/static/js/intro.js | 2532 +++++++++++++++++ public/static/js/langs/zh-cn.js | 2 +- runtime/index.html | 0 vendor/composer/autoload_namespaces.php | 1 - vendor/composer/autoload_static.php | 7 - vendor/composer/installed.json | 51 - 146 files changed, 7812 insertions(+), 1334 deletions(-) create mode 100644 SkycaijiApp/admin/controller/Proxy.php create mode 100644 SkycaijiApp/admin/event/Rtoapi.php create mode 100644 SkycaijiApp/admin/model/FuncApp.php create mode 100644 SkycaijiApp/admin/view/collected/chart.html create mode 100644 SkycaijiApp/admin/view/cpattern/browser.html create mode 100644 SkycaijiApp/admin/view/cpattern/browser_json.html create mode 100644 SkycaijiApp/admin/view/cpattern/easymode.html create mode 100644 SkycaijiApp/admin/view/develop/func.html create mode 100644 SkycaijiApp/admin/view/mystore/func.html create mode 100644 SkycaijiApp/admin/view/proxy/add.html create mode 100644 SkycaijiApp/admin/view/proxy/batch.html create mode 100644 SkycaijiApp/admin/view/proxy/list.html create mode 100644 SkycaijiApp/admin/view/proxy/testApi.html create mode 100644 SkycaijiApp/admin/view/setting/caiji_nav.html create mode 100644 SkycaijiApp/admin/view/setting/download_img.html create mode 100644 SkycaijiApp/extend/util/Curl.php create mode 100644 SkycaijiApp/install/event/LocSystem.php create mode 100644 SkycaijiApp/public/func_app/class.tpl create mode 100644 app/app/config/index.html create mode 100644 app/app/index.html create mode 100644 app/index.html create mode 100644 data/app/index.html create mode 100644 data/images/index.html create mode 100644 data/index.html create mode 100644 data/program/backup/index.html create mode 100644 data/program/index.html create mode 100644 data/program/upgrade/index.html create mode 100644 plugin/func/index.html create mode 100644 plugin/func/process/index.html create mode 100644 plugin/func/processIf/index.html create mode 100644 public/static/css/cpattern_browser.css create mode 100644 public/static/css/cpattern_easy.css create mode 100644 public/static/css/introjs.css create mode 100644 public/static/js/admin/cpattern_browser.js create mode 100644 public/static/js/admin/cpattern_easy.js create mode 100644 public/static/js/chart.min.js create mode 100644 public/static/js/intro.js create mode 100644 runtime/index.html diff --git a/.htaccess b/.htaccess index 33d32f7..99842f7 100644 --- a/.htaccess +++ b/.htaccess @@ -1,12 +1,12 @@ - - Options +FollowSymlinks -Multiviews - RewriteEngine on - - RewriteCond %{REQUEST_FILENAME} !-d - RewriteCond %{REQUEST_FILENAME} !-f - RewriteRule ^app/(\w+)/(.*)$ app/$1/index.php [QSA,PT,L,E=PATH_INFO:$2] - - RewriteCond %{REQUEST_FILENAME} !-d - RewriteCond %{REQUEST_FILENAME} !-f - RewriteRule ^(.*)$ index.php [QSA,PT,L,E=PATH_INFO:$1] + + Options +FollowSymlinks -Multiviews + RewriteEngine on + + RewriteCond %{REQUEST_FILENAME} !-d + RewriteCond %{REQUEST_FILENAME} !-f + RewriteRule ^app/(\w+)/(.*)$ app/$1/index.php [QSA,PT,L,E=PATH_INFO:$2] + + RewriteCond %{REQUEST_FILENAME} !-d + RewriteCond %{REQUEST_FILENAME} !-f + RewriteRule ^(.*)$ index.php [QSA,PT,L,E=PATH_INFO:$1] \ No newline at end of file diff --git a/SkycaijiApp/admin/behavior/Init.php b/SkycaijiApp/admin/behavior/Init.php index 43386a4..81d72c3 100644 --- a/SkycaijiApp/admin/behavior/Init.php +++ b/SkycaijiApp/admin/behavior/Init.php @@ -66,13 +66,13 @@ class Init{ } if($s_userid>0){ - $GLOBALS['user']=$muser->getByUid($s_userid); - if(!empty($GLOBALS['user'])){ - $GLOBALS['user']=$GLOBALS['user']->toArray(); - $GLOBALS['user']['group']=model('Usergroup')->getById($GLOBALS['user']['groupid']); - if(!empty($GLOBALS['user']['group'])){ - $GLOBALS['user']['group']=$GLOBALS['user']['group']->toArray(); - if(model('Usergroup')->is_admin($GLOBALS['user']['group'])){ + $GLOBALS['_sc']['user']=$muser->getByUid($s_userid); + if(!empty($GLOBALS['_sc']['user'])){ + $GLOBALS['_sc']['user']=$GLOBALS['_sc']['user']->toArray(); + $GLOBALS['_sc']['user']['group']=model('Usergroup')->getById($GLOBALS['_sc']['user']['groupid']); + if(!empty($GLOBALS['_sc']['user']['group'])){ + $GLOBALS['_sc']['user']['group']=$GLOBALS['_sc']['user']['group']->toArray(); + if(model('Usergroup')->is_admin($GLOBALS['_sc']['user']['group'])){ session('is_admin',true); }else{ session('is_admin',null); @@ -81,7 +81,7 @@ class Init{ } } - if(!empty($GLOBALS['user'])&&session('is_admin')){ + if(!empty($GLOBALS['_sc']['user'])&&session('is_admin')){ /*是管理员,进行下列操作*/ if('index'==$curController&&'index'==strtolower(request()->action())){ @@ -108,38 +108,32 @@ class Init{ } /*通用操作,全局变量*/ $mconfig=model('Config'); - $latestDate=$mconfig->max('dateline'); - $keyConfig='cache_config_all'; - $cacheConfig=cache($keyConfig); - $configList=array(); - if(empty($cacheConfig)||$cacheConfig['update_time']!=$latestDate){ - - $configDbList=$mconfig->column('*'); - $configDbList=empty($configDbList)?array():$configDbList; - foreach ($configDbList as $configItem){ - $configItem=$mconfig->convertData($configItem); - $configList[$configItem['cname']]=$configItem['data']; - } - cache($keyConfig,array('update_time'=>$latestDate,'list'=>$configList)); - }else{ - $configList=$cacheConfig['list']; + $configList=$mconfig->getConfigList(); + if(empty($configList)){ + $mconfig->cacheConfigList(); + $configList=$mconfig->getConfigList(); } - $GLOBALS['config']=$configList; + $GLOBALS['_sc']['c']=$configList; - if(!empty($GLOBALS['config']['site']['closelog'])){ + if(!empty($GLOBALS['_sc']['c']['site']['closelog'])){ \think\Log::init(array('type'=>'test','level'=>array())); } - if(!empty($GLOBALS['config']['site']['dblong'])){ + if(!empty($GLOBALS['_sc']['c']['site']['dblong'])){ $dbParams=config('database.params'); $dbParams[\PDO::ATTR_PERSISTENT]=true; config('database.params',$dbParams); } - $GLOBALS['clientinfo']=clientinfo(); - if(!empty($GLOBALS['clientinfo'])){ - $GLOBALS['clientinfo']=base64_encode(json_encode($GLOBALS['clientinfo'])); + + if(empty($GLOBALS['_sc']['c']['download_img'])){ + $GLOBALS['_sc']['c']['download_img']=$mconfig->get_img_config_from_caiji($GLOBALS['_sc']['c']['caiji']); + } + + $GLOBALS['_sc']['clientinfo']=clientinfo(); + if(!empty($GLOBALS['_sc']['clientinfo'])){ + $GLOBALS['_sc']['clientinfo']=base64_encode(json_encode($GLOBALS['_sc']['clientinfo'])); } @@ -150,7 +144,7 @@ class Init{ $usertoken=md5($usertoken); session('usertoken',$usertoken); } - $GLOBALS['usertoken']=$usertoken; + $GLOBALS['_sc']['usertoken']=$usertoken; } } diff --git a/SkycaijiApp/admin/command/Collect.php b/SkycaijiApp/admin/command/Collect.php index ebcb3a3..4c1704d 100644 --- a/SkycaijiApp/admin/command/Collect.php +++ b/SkycaijiApp/admin/command/Collect.php @@ -34,22 +34,37 @@ class Collect extends Command{ if(is_array($cacheConfig)){ \think\Config::set($cacheConfig); } - if ($input->hasOption('cli_user')){ - - $cliUser=$input->getOption('cli_user'); - $cliUser=base64_decode($cliUser); - $cliUser=explode('_', $cliUser); - $muser=new \skycaiji\admin\model\User(); - $user=$muser->where('username',$cliUser[0])->find(); - if(!empty($user)){ - $user['username']=strtolower($user['username']); + $op=$input->getArgument('op'); + + static $loginOps=array('task','batch'); + + if(in_array($op, $loginOps)){ + + if ($input->hasOption('cli_user')){ - if($user['username']==$cliUser[0]&&$cliUser[1]==md5($user['username'].$user['password'])){ - session('user_id',$user['uid']); + $cliUser=$input->getOption('cli_user'); + $cliUser=base64_decode($cliUser); + $cliUser=explode('_', $cliUser); + if(!empty($cliUser[0])){ + + $muser=new \skycaiji\admin\model\User(); + $user=$muser->where('username',$cliUser[0])->find(); + if(!empty($user)){ + $user['username']=strtolower($user['username']); + + if($user['username']==$cliUser[0]&&$cliUser[1]==md5($user['username'].$user['password'])){ + session('user_id',$user['uid']); + } + } } } + + if(!session('?user_id')){ + $this->error_msg('抱歉,必须传入账号信息!'); + } } - $op=$input->getArgument('op'); + + $rootUrl=\think\Config::get('root_website').'/index.php?s='; if('task'==$op){ @@ -59,29 +74,74 @@ class Collect extends Command{ $taskId=intval($taskId); } - $curUrl=\think\Config::get('root_website').'/admin/task/collect?backstage=1&id='.urlencode($taskId); + $curUrl=$rootUrl.'/admin/task/collect&backstage=1&id='.urlencode($taskId); \think\Request::create($curUrl); define('BIND_MODULE', "admin/task/collect"); - }elseif('auto'==$op){ - - $curUrl=\think\Config::get('root_website').'/admin/api/collect?backstage=1'; - \think\Request::create($curUrl); - - define('BIND_MODULE', "admin/api/collect"); + \think\App::run()->send(); }elseif('batch'==$op){ $taskIds=''; if ($input->hasOption('task_ids')){ $taskIds=$input->getOption('task_ids'); } - $curUrl=\think\Config::get('root_website').'/admin/task/collectBatch?backstage=1&ids='.urlencode($taskIds); + $curUrl=$rootUrl.'/admin/task/collectBatch&backstage=1&ids='.urlencode($taskIds); \think\Request::create($curUrl); define('BIND_MODULE', "admin/task/collectBatch"); + \think\App::run()->send(); + }elseif('backstage'==$op){ + + set_time_limit(0); + $curKey=CacheModel::getInstance()->getCache('admin_index_backstage_key', 'data'); + do{ + + $cacheKey=CacheModel::getInstance()->getCache('admin_index_backstage_key', 'data'); + if(empty($curKey)||$curKey!=$cacheKey){ + + + $this->error_msg('密钥错误,请在后台运行'); + } + + $mconfig=new \skycaiji\admin\model\Config(); + $caijiConfig=$mconfig->getConfig('caiji','data'); + + if($caijiConfig['server']!='cli'){ + $this->error_msg('不是cli命令行模式'); + } + if(empty($caijiConfig['auto'])){ + $this->error_msg('未开启自动采集'); + } + if($caijiConfig['run']!='backstage'){ + $this->error_msg('不是后台运行方式'); + } + + $url=$rootUrl.'/admin/api/collect&backstage=1'; + + try{ + + \util\Curl::get($url,null,array('timeout'=>3)); + }catch(\Exception $ex){ + + } + + $waitTime=$caijiConfig['interval']*60; + $waitTime=$waitTime>0?$waitTime:60; + sleep($waitTime); + + }while(1==1); + }elseif('auto'==$op){ + + $curUrl=$rootUrl.'/admin/api/collect&backstage=1'; + \think\Request::create($curUrl); + + define('BIND_MODULE', "admin/api/collect"); + \think\App::run()->send(); } - - \think\App::run()->send(); + } + + protected function error_msg($msg){ + exit($msg); } } \ No newline at end of file diff --git a/SkycaijiApp/admin/common.php b/SkycaijiApp/admin/common.php index 86d37ae..f311855 100644 --- a/SkycaijiApp/admin/common.php +++ b/SkycaijiApp/admin/common.php @@ -66,7 +66,7 @@ function program_filemd5_list($path,&$md5FileList){ } /*验证用户token*/ function check_usertoken(){ - if($GLOBALS['usertoken']!=input('_usertoken_')){ + if($GLOBALS['_sc']['usertoken']!=input('_usertoken_')){ return false; }else{ return true; @@ -75,7 +75,7 @@ function check_usertoken(){ /*输出用户token*/ function html_usertoken(){ - return ''; + return ''; } /*判断正在执行采集任务*/ @@ -106,35 +106,24 @@ function cli_command_exec($paramStr){ } } - static $php_ext_path=null; - - if(!isset($php_ext_path)){ - $ini_all=ini_get_all(); - $php_ext_path=$ini_all['extension_dir']['local_value']; + $phpExeFile=$GLOBALS['_sc']['c']['caiji']['server_php']; + if(empty($phpExeFile)){ + + $phpExeFile=model('Config')->detect_php_exe(); } - - if($php_ext_path){ - $phpPath=preg_replace('/[\/\\\]ext[\/\\\]*$/i', '', $php_ext_path); - if(IS_WIN){ - - $phpPath.=DIRECTORY_SEPARATOR.'php.exe'; - } - }else{ - $phpPath='php'; - } - - $commandStr=$phpPath; + $commandStr=$phpExeFile; if(IS_WIN){ + $commandStr='"'.$commandStr.'"'; } - $cliUser=strtolower($GLOBALS['user']['username']); - $cliUser=$cliUser.'_'.md5($cliUser.$GLOBALS['user']['password']); + $cliUser=strtolower($GLOBALS['_sc']['user']['username']); + $cliUser=$cliUser.'_'.md5($cliUser.$GLOBALS['_sc']['user']['password']); $paramStr.=' --cli_user '.base64_encode($cliUser); $commandStr.=' '.config('root_path').DIRECTORY_SEPARATOR.'caiji '.$paramStr; - + $descriptorspec = array( 0 => array('pipe', 'r'), 1 => array('pipe', 'w'), @@ -156,4 +145,37 @@ function is_official_url($url){ }else{ return false; } +} + + +function convert_html2json($html,$returnStr=false){ + static $jsonpRegExp='/^(\s*[\$\w\-]+\s*[\{\(])+(?P[\s\S]+)(?P[\}\]])\s*\)\s*[\;]{0,1}/i'; + $json=json_decode($html,true); + if(!empty($json)){ + + if($returnStr){ + + $json=$html; + } + }elseif(preg_match($jsonpRegExp,$html,$json)){ + + $json=trim($json['json']).$json['end']; + if(!$returnStr){ + + $json=json_decode($json,true); + } + } + return $json?$json:null; +} + +function array_filter_keep0($list){ + if(is_array($list)){ + foreach ($list as $k=>$v){ + if(empty($v)&&$v!==0&&$v!=='0'){ + + unset($list[$k]); + } + } + } + return $list; } \ No newline at end of file diff --git a/SkycaijiApp/admin/controller/Api.php b/SkycaijiApp/admin/controller/Api.php index 16d0236..53f67ec 100644 --- a/SkycaijiApp/admin/controller/Api.php +++ b/SkycaijiApp/admin/controller/Api.php @@ -33,7 +33,7 @@ class Api extends BaseController{ if(!IS_CLI){ ignore_user_abort(true); - if($GLOBALS['config']['caiji']['server']=='cli'){ + if($GLOBALS['_sc']['c']['caiji']['server']=='cli'){ cli_command_exec('collect auto'); exit(); @@ -55,19 +55,19 @@ class Api extends BaseController{ } ignore_user_abort(true); - if($GLOBALS['config']['caiji']['timeout']>0){ - set_time_limit(60*$GLOBALS['config']['caiji']['timeout']); + if($GLOBALS['_sc']['c']['caiji']['timeout']>0){ + set_time_limit(60*$GLOBALS['_sc']['c']['caiji']['timeout']); }else{ set_time_limit(0); } - if(empty($GLOBALS['config']['caiji']['auto'])){ + if(empty($GLOBALS['_sc']['c']['caiji']['auto'])){ $this->error('请先开启自动采集','Admin/Setting/caiji'); } $lastCollectTime=cache('last_collect_time'); - if($GLOBALS['config']['caiji']['interval']>0){ + if($GLOBALS['_sc']['c']['caiji']['interval']>0){ - $waitTime=(60*$GLOBALS['config']['caiji']['interval'])-abs(time()-$lastCollectTime); + $waitTime=(60*$GLOBALS['_sc']['c']['caiji']['interval'])-abs(time()-$lastCollectTime); if($waitTime>0){ $this->error('再次采集需等待'.(($waitTime<60)?($waitTime.'秒'):(sprintf("%.2f", $waitTime/60).'分钟')),'Admin/Api/collect',null,$waitTime); } diff --git a/SkycaijiApp/admin/controller/App.php b/SkycaijiApp/admin/controller/App.php index bc9f445..c69db67 100644 --- a/SkycaijiApp/admin/controller/App.php +++ b/SkycaijiApp/admin/controller/App.php @@ -61,9 +61,6 @@ class App extends BaseController { $appData['app_class']=$mapp->get_class_vars($appClass); } - - - $this->assign('app',$app); $this->assign('appUrl',$appUrl); $this->assign('navid',$navid); diff --git a/SkycaijiApp/admin/controller/Backstage.php b/SkycaijiApp/admin/controller/Backstage.php index 1d8ee55..946c14d 100644 --- a/SkycaijiApp/admin/controller/Backstage.php +++ b/SkycaijiApp/admin/controller/Backstage.php @@ -30,7 +30,7 @@ class Backstage extends BaseController{ 'os'=>php_uname('s').' '.php_uname('r'), 'php'=>PHP_VERSION, 'db'=>config('database.type'), - 'version'=>$GLOBALS['config']['version']?$GLOBALS['config']['version']:constant("SKYCAIJI_VERSION"), + 'version'=>$GLOBALS['_sc']['c']['version']?$GLOBALS['_sc']['c']['version']:constant("SKYCAIJI_VERSION"), 'server'=>$_SERVER["SERVER_SOFTWARE"], 'upload_max'=>ini_get('upload_max_filesize') ); @@ -42,7 +42,7 @@ class Backstage extends BaseController{ $runInfo['auto_status']='良好'; /*设置采集状态*/ - if($GLOBALS['config']['caiji']['auto']){ + if($GLOBALS['_sc']['c']['caiji']['auto']){ $lastTime=cache('last_collect_time'); $taskAutoCount=model('Task')->where('auto',1)->count(); @@ -55,9 +55,9 @@ class Backstage extends BaseController{ if($lastTime>0){ $runInfo['auto_status']='运行良好'; $serverData['caiji']='最近采集:'.date('Y-m-d H:i:s',$lastTime).'  '; - if($GLOBALS['config']['caiji']['run']=='backstage'){ + if($GLOBALS['_sc']['c']['caiji']['run']=='backstage'){ - if(NOW_TIME-$lastTime>60*($GLOBALS['config']['caiji']['interval']+15)){ + if(NOW_TIME-$lastTime>60*($GLOBALS['_sc']['c']['caiji']['interval']+15)){ $serverData['caiji'].='

自动采集似乎停止了,请重新保存设置以便激活采集

'; @@ -78,6 +78,45 @@ class Backstage extends BaseController{ $upgradeDb=true; } + $LocSystem=new \skycaiji\install\event\LocSystem(); + $systemData=$LocSystem->environment(); + + $systemWarning=array('php'=>array(),'path_write'=>array(),'path_read'=>array()); + if(is_array($systemData['php'])){ + foreach ($systemData['php'] as $k=>$v){ + if(empty($v[1])){ + + $systemWarning['php'][$v[0]]=$v[0]; + } + } + } + if(is_array($systemData['path'])){ + foreach ($systemData['path'] as $k=>$v){ + if(empty($v[1])){ + + $systemWarning['path_write'][$v[0]]=$v[0]; + } + if(empty($v[2])){ + + $systemWarning['path_read'][$v[0]]=$v[0]; + } + } + } + + $hasSystemWarning=false; + foreach ($systemWarning as $k=>$v){ + if(!empty($v)){ + $hasSystemWarning=true; + } + } + if(!$hasSystemWarning){ + $systemWarning=null; + } + + + + $adminIndexData=cache('backstage_admin_index'); + $timeout=NOW_TIME-(3600*24*30); $mcacheSource=CacheModel::getInstance('source_url'); @@ -89,12 +128,14 @@ class Backstage extends BaseController{ $mcacheCont=CacheModel::getInstance('cont_url'); $mcacheCont->db()->where('dateline','<',$timeout)->delete(); - $GLOBALS['content_header']='后台管理'; - $GLOBALS['breadcrumb']=breadcrumb(array('首页')); + $GLOBALS['_sc']['p_name']='后台管理'; + $GLOBALS['_sc']['p_nav']=breadcrumb(array(array('url'=>url('Backstage/index'),'title'=>'首页'))); $this->assign('runInfo',$runInfo); $this->assign('serverData',$serverData); $this->assign('upgradeDb',$upgradeDb); + $this->assign('systemWarning',$systemWarning); + $this->assign('adminIndexData',$adminIndexData); return $this->fetch('backstage/index'); } @@ -105,10 +146,21 @@ class Backstage extends BaseController{ } /*获取推送消息*/ public function adminIndexAction(){ - $callback=input('?'.config('var_jsonp_handler'))?input(config('var_jsonp_handler')):config('default_jsonp_handler'); - $html=get_html('https://www.skycaiji.com/store/client/adminIndex?v='.SKYCAIJI_VERSION.'&'.config('var_jsonp_handler').'='.rawurlencode($callback),null,null,'utf-8'); - header('Content-Type:application/json;charset=utf-8'); - exit($html); + $refresh=input('refresh'); + $data=cache('backstage_admin_index'); + $data=is_array($data)?$data:array(); + if($refresh||empty($data['html'])){ + + $data=get_html('https://www.skycaiji.com/store/client/adminIndex?v='.SKYCAIJI_VERSION,null,null,'utf-8'); + $data=json_decode($data,true); + + $data=array( + 'ver'=>$data['ver'], + 'html'=>$data['html'] + ); + cache('backstage_admin_index',$data); + } + return json($data); } /*后台任务操作*/ public function backstageTaskAction(){ @@ -221,7 +273,7 @@ class Backstage extends BaseController{ } } - /*生成js语言包文件*/ + public function createJsLangAction(){ $langs=array(); $langs['zh-cn']='zh-cn'; @@ -259,4 +311,5 @@ class Backstage extends BaseController{ } print_r ( $repeatList ); } + } \ No newline at end of file diff --git a/SkycaijiApp/admin/controller/Collected.php b/SkycaijiApp/admin/controller/Collected.php index 2638594..3e1763b 100644 --- a/SkycaijiApp/admin/controller/Collected.php +++ b/SkycaijiApp/admin/controller/Collected.php @@ -76,8 +76,8 @@ class Collected extends BaseController { $taskList=model('Task')->where(array('id'=>array('in',$taskIds)))->column('name','id'); } } - $GLOBALS['content_header']=lang('collected_list'); - $GLOBALS['breadcrumb']=breadcrumb(array(array('url'=>url('Collected/list'),'title'=>lang('collected_list')))); + $GLOBALS['_sc']['p_name']=lang('collected_list'); + $GLOBALS['_sc']['p_nav']=breadcrumb(array(array('url'=>url('Collected/list'),'title'=>'已采集数据'),array('url'=>url('Collected/list'),'title'=>'数据列表'))); } $this->assign('search',$search); $this->assign('dataList',$dataList); @@ -122,4 +122,135 @@ class Collected extends BaseController { $this->success(lang('op_success'),'list'); } } + /*图表显示*/ + public function chartAction(){ + $GLOBALS['_sc']['p_name']='已采集数据:统计图表'; + $GLOBALS['_sc']['p_nav']=breadcrumb(array(array('url'=>url('Collected/list'),'title'=>'已采集数据'),array('url'=>url('Collected/chart'),'title'=>'统计图表'))); + + return $this->fetch(); + } + + public function chartOpAction(){ + $op=input('op'); + $mcollected=model('Collected'); + $nowTime=time(); + $nowYear=intval(date('Y',$nowTime)); + $nowMonth=intval(date('m',$nowTime)); + $nowDay=intval(date('d',$nowTime)); + if(in_array($op,array('today','this_month','this_year','years'))){ + $dataList = array ( + 'name'=>array(), + 'success' => array (), + 'failed' => array () + ); + $dateList=array(); + if($op=='today'){ + + for($i=0;$i<24;$i++){ + $start=$nowYear.'-'.$nowMonth.'-'.$nowDay.' '.$i.':00'; + $end=strtotime($start.' +1 hour')-1; + $start=strtotime($start); + $dateList[$i+1]=array('name'=>($i+1).'点','start'=>$start,'end'=>$end); + } + }if($op=='this_month'){ + + $endDay=date('d',strtotime("{$nowYear}-{$nowMonth}-1 +1 month -1 day")); + $endDay=intval($endDay); + for($i=1;$i<=$endDay;$i++){ + $start=$nowYear.'-'.$nowMonth.'-'.$i; + $end=strtotime($start.' +1 day')-1; + $start=strtotime($start); + $dateList[$i]=array('name'=>$i.'号','start'=>$start,'end'=>$end); + } + }elseif($op=='this_year'){ + + for($i=1;$i<=12;$i++){ + $start=$nowYear.'-'.$i.'-1'; + $end=strtotime($start.' +1 month')-1; + $start=strtotime($start); + $dateList[$i]=array('name'=>$i.'月','start'=>$start,'end'=>$end); + + } + }elseif($op=='years'){ + + $minTime=$mcollected->min('addtime'); + $minYear=intval(date('Y',$minTime)); + for($i=$nowYear;$i>=$minYear;$i--){ + $start=$i.'-1-1'; + $end=strtotime($start.' +1 year')-1; + $start=strtotime($start); + $dateList[$i]=array('name'=>$i.'年','start'=>$start,'end'=>$end); + } + } + foreach ($dateList as $k=>$v){ + $dataList['name'][$k]=$v['name']; + + $dataList['success'][$k]=$mcollected->where(array( + 'addtime'=>array('between',array($v['start'],$v['end'])), + 'target'=>array('<>','') + ))->count(); + + + $dataList['failed'][$k]=$mcollected->where(array( + 'addtime'=>array('between',array($v['start'],$v['end'])), + 'error'=>array('<>','') + ))->count(); + } + + $this->success('',null,$dataList); + }elseif($op=='release'){ + + $dataList = array ( + 'name'=>array(), + 'success' => array (), + 'failed' => array (), + ); + + foreach(config('release_modules') as $module){ + if($module=='api'){ + + continue; + } + $dataList['name'][$module]=lang('rele_module_'.$module); + + $dataList['success'][$module]=$mcollected->where(array( + 'release'=>$module, + 'target'=>array('<>','') + ))->count(); + + + $dataList['failed'][$module]=$mcollected->where(array( + 'release'=>$module, + 'error'=>array('<>','') + ))->count(); + } + $this->success('',null,$dataList); + }elseif($op=='task'){ + + $dataList = array ( + 'name'=>array(), + 'total' => array (), + ); + + + $list=$mcollected->field('task_id,count(id) as ct')->group('task_id')->having('count(id)>0')->select(); + $taskIds=array(); + foreach($list as $v){ + $taskIds[$v['task_id']]=$v['task_id']; + } + if($taskIds){ + $taskList=model('Task')->where('id','in',$taskIds)->column('name','id'); + } + + foreach($list as $v){ + if(isset($taskList[$v['task_id']])){ + + $dataList['name'][$v['task_id']]=$taskList[$v['task_id']]; + $dataList['total'][$v['task_id']]=$v['ct']; + } + } + + $this->success('',null,$dataList); + } + } } \ No newline at end of file diff --git a/SkycaijiApp/admin/controller/Collector.php b/SkycaijiApp/admin/controller/Collector.php index 2bf6067..8ab0476 100644 --- a/SkycaijiApp/admin/controller/Collector.php +++ b/SkycaijiApp/admin/controller/Collector.php @@ -60,7 +60,7 @@ class Collector extends BaseController { } if($collId>0){ $tab_link=trim(input('tab_link'),'#'); - $this->success(lang('op_success'),'Collector/set?task_id='.$taskId.($tab_link?'&tab_link='.$tab_link:'')); + $this->success(lang('op_success'),'Collector/set?task_id='.$taskId.($tab_link?'&tab_link='.$tab_link:'').(input('?easymode')?'&easymode=1':'')); }else{ $this->error(lang('op_failed')); } @@ -68,8 +68,14 @@ class Collector extends BaseController { if(!empty($collData)){ $collData['config']=unserialize($collData['config']); } - $GLOBALS['content_header']=lang('coll_set').lang('separator').lang('task_module_'.$taskData['module']); - $GLOBALS['breadcrumb']=breadcrumb(array(array('url'=>url('Task/edit?id='.$taskData['id']),'title'=>lang('task').lang('separator').$taskData['name']),lang('coll_set'))); + $GLOBALS['_sc']['p_name']=lang('coll_set').lang('separator').lang('task_module_'.$taskData['module']); + if(input('?easymode')){ + $GLOBALS['_sc']['p_name'].=' 普通模式'; + }else{ + $GLOBALS['_sc']['p_name'].=' 简单模式'; + } + + $GLOBALS['_sc']['p_nav']=breadcrumb(array(array('url'=>url('Task/edit?id='.$taskData['id']),'title'=>lang('task').lang('separator').$taskData['name']),array('url'=>url('Collector/set?task_id='.$taskData['id']),'title'=>lang('coll_set')))); $this->assign('collData',$collData); $this->assign('taskData',$taskData); return $this->fetch(); diff --git a/SkycaijiApp/admin/controller/Cpattern.php b/SkycaijiApp/admin/controller/Cpattern.php index ce70722..4b4c135 100644 --- a/SkycaijiApp/admin/controller/Cpattern.php +++ b/SkycaijiApp/admin/controller/Cpattern.php @@ -11,6 +11,7 @@ namespace skycaiji\admin\controller; +use skycaiji\admin\model\FuncApp; /*采集器:规则采集*/ class Cpattern extends BaseController { /** @@ -178,7 +179,7 @@ class Cpattern extends BaseController { $field['num_end']=intval($field['num_end']); $field['num_end'] = max ( $field['num_start'], $field ['num_end'] ); break; - case 'words':if(empty($field['words']))$this->error('固定文字不能为空!');break; + case 'list':if(empty($field['list']))$this->error('随机抽取不能为空!');break; case 'extract':if(empty($field['extract']))$this->error('请选择字段!');break; case 'merge':if(empty($field['merge']))$this->error('字段组合不能为空!');break; @@ -225,6 +226,16 @@ class Cpattern extends BaseController { $this->assign('type',$type); $op=input('op'); + + + $transApiLangs=null; + if(!empty($GLOBALS['_sc']['c']['translate'])&&!empty($GLOBALS['_sc']['c']['translate']['open'])){ + + $transApiLangs=\util\Translator::get_api_langs($GLOBALS['_sc']['c']['translate']['api']); + $transApiLangs=$transApiLangs?$transApiLangs:null; + } + $this->assign('transApiLangs',$transApiLangs); + if(empty($type)){ if(empty($op)){ @@ -263,6 +274,39 @@ class Cpattern extends BaseController { } } } + /*获取函数插件列表*/ + public function process_funcAction(){ + $module=input('module'); + if(empty($module)){ + $this->error('模块错误'); + } + $mfuncApp=new FuncApp(); + + $cacheName='cpattern_process_func_methods_'.$module; + $cacheFuncs=cache($cacheName); + + $enableApps=$mfuncApp->where(array('module'=>$module,'enable'=>1))->column('uptime','app'); + $enableApps=md5(serialize($enableApps)); + + $apps=array(); + if(empty($cacheFuncs)||$enableApps!=$cacheFuncs['key']||abs(time()-$cacheFuncs['time'])>600){ + + $appList=$mfuncApp->where(array('module'=>$module,'enable'=>1))->column('uptime','app'); + $apps=array(); + if(!empty($appList)){ + foreach ($appList as $k=>$v){ + $appClass=$mfuncApp->get_app_class($module, $k); + if(!empty($appClass['methods'])){ + $apps[$k]=$appClass; + } + } + } + cache($cacheName,array('list'=>$apps,'time'=>time(),'key'=>md5(serialize($appList)))); + }else{ + $apps=$cacheFuncs['list']; + } + $this->success('',null,$apps); + } /** * 内容分页 * 添加分页字段 @@ -351,7 +395,7 @@ class Cpattern extends BaseController { model('Task')->loadConfig($taskData['config']); - $GLOBALS['breadcrumb']=breadcrumb(array(array('url'=>url('Collector/set?task_id='.$taskData['id']),'title'=>lang('task').lang('separator').$taskData['name']),'测试')); + $GLOBALS['_sc']['p_nav']=breadcrumb(array(array('url'=>url('Collector/set?task_id='.$taskData['id']),'title'=>lang('task').lang('separator').$taskData['name']),array('url'=>url('Cpattern/test&op='.$op.'&coll_id='.$coll_id),'title'=>'测试'))); if('source_urls'==$op){ @@ -388,7 +432,7 @@ class Cpattern extends BaseController { $eCpattern->success('',null,array('urls'=>$levelData['urls'],'levelName'=>$levelData['levelName'],'nextLevel'=>$levelData['nextLevel'])); }elseif('cont_url'==$op){ - $GLOBALS['content_header']='测试抓取'; + $GLOBALS['_sc']['p_name']='测试抓取'; $cont_url=input('cont_url','','trim'); $test=input('test'); @@ -463,7 +507,7 @@ class Cpattern extends BaseController { $num=0; foreach ($eCpattern->exclude_cont_urls[$md5Url] as $k=>$v){ - $num+=count($v); + $num+=count((array)$v); } $msg='通过数据处理排除了'.$num.'条数据'; } @@ -505,7 +549,7 @@ class Cpattern extends BaseController { } }elseif('match'==$op){ - $GLOBALS['content_header']='模拟匹配'; + $GLOBALS['_sc']['p_name']='模拟匹配'; if(request()->isPost()){ $type=strtolower(input('type')); $content=input('content','','trim'); @@ -563,42 +607,61 @@ class Cpattern extends BaseController { }elseif('elements'==$op){ $cont_url=input('cont_url','','trim'); - if(!preg_match('/^\w+\:\/\//',$cont_url)){ - - $cont_url='http://'.$cont_url; - } - $html=$eCpattern->get_html($cont_url,false,$eCpattern->config['url_post']); - $jsonData=null; - if(preg_match('/^\{[\s\S]*\}$/',$html)){ - - $jsonData=json_decode($html,true); - $jsonData=$jsonData?$jsonData:null; - } + $this->redirect('Cpattern/browser?coll_id='.$coll_id.'&url='.rawurlencode($cont_url)); + } + } + /*编辑规则:简单模式*/ + public function easymodeAction(){ + $taskId=input('task_id/d',0); + $collId=model('Collector')->where('task_id',$taskId)->value('id'); + $this->assign('taskId',$taskId); + $this->assign('collId',$collId); + return $this->fetch(); + } + /*浏览器*/ + public function browserAction(){ + + $coll_id=input('coll_id/d',0); + $mcoll=model('Collector'); + $collData=$mcoll->where(array('id'=>$coll_id))->find(); + if(empty($collData)){ + $collData=array(); + }else{ + $collData=$collData->toArray(); + } + + + $eCpattern=controller('admin/Cpattern','event'); + $eCpattern->init($collData); + + $url=input('url','','trim'); + if(!preg_match('/^\w+\:\/\//',$url)){ + $url='http://'.$url; + } + $html=$eCpattern->get_html($url,false,$eCpattern->config['url_post']); + + $jsonHtml=convert_html2json($html,true); + + $config=$eCpattern->config; + $config=is_array($config)?$config:array(); + + + if(empty($jsonHtml)){ - $publicUrl=config('root_website').'/public'; - $jscss="\r\n\r\n".'' - ."\r\n".'' - ."\r\n".''; - $jscss=sprintf($jscss,$publicUrl,config('html_v'),$publicUrl,config('html_v'),$publicUrl,config('html_v')); + $html=preg_replace('/]*?>[\s\S]*?<\/script>/i', '', $html); + $html=preg_replace('/]*charset[^<>]*?>/i', '', $html); + header("Content-type:text/html;charset=utf-8"); + $eCpattern->assign('html',$html); + $eCpattern->assign('config',$config); - if(empty($jsonData)){ - - $html=preg_replace('/]*?>[\s\S]*?<\/script>/i', '', $html); - $html=preg_replace('/]*charset[^<>]*?>/i', '', $html); - $html.=$jscss."\r\n".''; - ob_clean(); - header("Content-type:text/html;charset=utf-8"); - exit($html); - }else{ - - $GLOBALS['content_header']='分析元素'; - - $eCpattern->assign('html',$html); - $eCpattern->assign('jscss',$jscss); - return $eCpattern->fetch('cpattern:test_elements'); - } + return $eCpattern->fetch('cpattern:browser'); + }else{ + + $GLOBALS['_sc']['p_name']='分析网页'; + $eCpattern->assign('jsonHtml',$jsonHtml); + return $eCpattern->fetch('cpattern:browser_json'); } } /*名称命名规范*/ diff --git a/SkycaijiApp/admin/controller/Develop.php b/SkycaijiApp/admin/controller/Develop.php index 385f100..85f7d64 100644 --- a/SkycaijiApp/admin/controller/Develop.php +++ b/SkycaijiApp/admin/controller/Develop.php @@ -12,6 +12,7 @@ namespace skycaiji\admin\controller; use plugin; +use skycaiji\admin\model\FuncApp; class Develop extends BaseController { public static $typeList = array ( 'number' => '数字(number)', @@ -162,8 +163,9 @@ class Develop extends BaseController { } } - $GLOBALS['content_header']='开发CMS发布插件 '; - $GLOBALS['breadcrumb']=breadcrumb(array('开发工具','开发CMS发布插件')); + $GLOBALS['_sc']['p_name']='开发CMS发布插件 '; + $GLOBALS['_sc']['p_nav']=breadcrumb(array(array('url'=>url('Mystore/ReleaseApp'),'title'=>'CMS发布插件'),array('url'=>url('Develop/releaseCms'),'title'=>'开发CMS发布插件'))); + $this->assign('config',$config); $this->assign('is_old_plugin',$is_old_plugin); return $this->fetch('releaseCms'); @@ -537,8 +539,6 @@ EOF; $codeAppPhp=preg_replace($matchVar, 'public $'.$reVar.'='.$reCont.';', $codeAppPhp); }else{ - preg_match_all($matchVar,$codeAppPhp,$asd); - $codeAppPhp=preg_replace($matchVar, 'public $'.$reVar."='".addslashes($reCont)."';", $codeAppPhp); } } @@ -561,12 +561,12 @@ EOF; $this->success('修改成功','Develop/app?app='.$app); } }else{ - $GLOBALS['content_header']='开发应用程序 '; + $GLOBALS['_sc']['p_name']='开发应用程序 '; if($appData){ - $GLOBALS['breadcrumb']=breadcrumb(array(array('url'=>url('App/manage?app='.$appData['app']),'title'=>$appData['config']['name']),'开发应用')); + $GLOBALS['_sc']['p_nav']=breadcrumb(array(array('url'=>url('App/manage?app='.$appData['app']),'title'=>$appData['config']['name']),array('url'=>url('Develop/app?app='.$appData['app']),'title'=>'开发应用'))); }else{ - $GLOBALS['breadcrumb']=breadcrumb(array('开发工具','应用程序')); + $GLOBALS['_sc']['p_nav']=breadcrumb(array(array('url'=>url('Mystore/app'),'title'=>'应用程序'),array('url'=>url('Develop/app'),'title'=>'开发应用'))); } $appClass=$mapp->app_class($app); @@ -743,6 +743,94 @@ EOF; $this->error(); } } + /*开发函数插件*/ + public function funcAction(){ + $mfuncApp=new FuncApp(); + if(request()->isPost()){ + if(input('?edit')){ + + $app=input('app'); + $name=input('name'); + $name=strip_tags($name); + $funcData=$mfuncApp->where('app',$app)->find(); + if(empty($funcData)){ + $this->error('插件不存在'); + } + $mfuncApp->where('id',$funcData['id'])->update(array('name'=>$name,'uptime'=>time())); + + $this->success('修改成功','Develop/func?app='.$app); + }else{ + + $module=input('module'); + $copyright=input('copyright'); + $identifier=input('identifier'); + $name=input('name'); + $methods=input('methods/a'); + + if(empty($module)){ + $this->error('请选择类型'); + } + + $module=$mfuncApp->format_module($module); + $copyright=$mfuncApp->format_copyright($copyright); + $identifier=$mfuncApp->format_identifier($identifier); + + if(!$mfuncApp->right_module($module)){ + $this->error('类型错误'); + } + if(!$mfuncApp->right_identifier($identifier)){ + $this->error('功能标识只能由字母或数字组成,且首个字符必须是字母!'); + } + if(!$mfuncApp->right_copyright($copyright)){ + $this->error('作者版权只能由字母或数字组成,且首个字符必须是字母!'); + } + + $newMethods=array(); + foreach ($methods['method'] as $k=>$v){ + if(preg_match('/^[a-z\_]\w*/',$v)){ + + foreach ($methods as $mk=>$mv){ + + $newMethods[$mk][$k]=$mv[$k]; + } + } + } + $methods=$newMethods; + unset($newMethods); + + if(empty($methods['method'])){ + $this->error('请添加方法!'); + } + + $app=$mfuncApp->app_name($copyright,$identifier); + + $id=$mfuncApp->createApp($module,$app,array('name'=>$name,'methods'=>$methods)); + + if($id>0){ + $this->success('创建成功','Develop/func?app='.$app); + }else{ + $this->error('创建失败'); + } + } + }else{ + $GLOBALS['_sc']['p_name']='开发函数插件 '; + $GLOBALS['_sc']['p_nav']=breadcrumb(array(array('url'=>url('Mystore/funcApp'),'title'=>'函数插件'),array('url'=>url('Develop/func'),'title'=>'开发函数插件'))); + + if(input('?app')){ + + $funcData=$mfuncApp->where('app',input('app'))->find(); + if(!empty($funcData)){ + $funcClass=$mfuncApp->get_app_class($funcData['module'],$funcData['app']); + $funcClass['name']=$funcData['name']; + + $this->assign('funcClass',$funcClass); + } + } + + $this->assign('modules',$mfuncApp->funcModules); + return $this->fetch(); + } + } public function _format_array($arr,$headStr=''){ if(is_array($arr)){ diff --git a/SkycaijiApp/admin/controller/Index.php b/SkycaijiApp/admin/controller/Index.php index eeabeca..6b9689e 100644 --- a/SkycaijiApp/admin/controller/Index.php +++ b/SkycaijiApp/admin/controller/Index.php @@ -18,116 +18,107 @@ class Index extends BaseController{ } /*后台运行采集,会无限运行下去*/ public function backstageAction(){ - if(empty($GLOBALS['config']['caiji']['auto'])){ - $this->error('请先开启自动采集'); - } - if(!input('?autorun')){ + $key=input('key'); + $cacheKey=CacheModel::getInstance()->getCache('admin_index_backstage_key', 'data'); + if(empty($key)||$key!=$cacheKey){ - $this->error('参数错误'); + + $this->error('密钥错误,请在后台运行'); } - $runtime=input('runtime/d'); - if(empty($runtime)){ - - $runtime=time(); - cache('caiji_auto_backstage_runtime',$runtime); - }else{ - - $cache_runtime=cache('caiji_auto_backstage_runtime'); - $cache_runtime=intval($cache_runtime); - if($runtime<$cache_runtime){ - - $this->error('终止旧进程'); - } - } - - $curlCname='caiji_auto_curltime_'.$runtime; - if(input('?curltime')){ - - $cacheCurl=cache($curlCname); - if(!empty($cacheCurl)&&$cacheCurl>input('curltime')){ - - $this->error('终止过期进程'); - } - cache($curlCname,input('curltime')); - } - - ignore_user_abort(true); - set_time_limit(0); - $mconfig=new \skycaiji\admin\model\Config(); - $caijiConfig=$mconfig->where('cname','caiji')->find(); - $caijiConfig=$mconfig->convertData($caijiConfig); + $caijiConfig=$mconfig->getConfig('caiji','data'); - if(empty($caijiConfig['data']['auto'])||$caijiConfig['data']['run']!='backstage'){ - - $this->error('自动采集已停止'); + if(empty($caijiConfig['auto'])){ + $this->error('请先开启自动采集'); } - try{ - - $ch = curl_init (); - curl_setopt ( $ch, CURLOPT_URL, url('Admin/Api/collect?backstage=1',null,false,true) ); - curl_setopt ( $ch, CURLOPT_TIMEOUT, 3 ); - curl_setopt ( $ch, CURLOPT_RETURNTRANSFER, 1 ); - curl_setopt ( $ch, CURLOPT_FOLLOWLOCATION, 1 ); - curl_setopt ( $ch, CURLOPT_HEADER, 1 ); - curl_setopt ( $ch, CURLOPT_NOBODY, 1 ); - curl_setopt ( $ch, CURLOPT_SSL_VERIFYPEER, FALSE ); - curl_setopt ( $ch, CURLOPT_SSL_VERIFYHOST, FALSE ); - curl_exec ( $ch ); - curl_close ( $ch ); - }catch(\Exception $ex){ - + if($caijiConfig['run']!='backstage'){ + $this->error('不是后台运行方式'); } - sleep(15); - - if($GLOBALS['config']['caiji']['auto']){ + if($caijiConfig['server']=='cli'){ + + cli_command_exec('collect backstage'); + }else{ + + $curlCname='caiji_auto_curltime_'.$key; + if(input('?curltime')){ + + $cacheCurl=cache($curlCname); + if(!empty($cacheCurl)&&$cacheCurl>input('curltime')){ + + $this->error('终止过期进程'); + } + cache($curlCname,input('curltime')); + } + + ignore_user_abort(true); + set_time_limit(0); try{ - + get_html(url('Admin/Api/collect?backstage=1',null,false,true),null,array('timeout'=>3)); + }catch(\Exception $ex){ + + } + + sleep(15); + + + try{ + $maxTimes=0; do { + + + $cacheKey=CacheModel::getInstance()->getCache('admin_index_backstage_key', 'data'); + if(empty($key)||$key!=$cacheKey){ + + + $this->error('密钥错误,终止进程'); + } + + $caijiConfig=$mconfig->getConfig('caiji','data'); + + if(empty($caijiConfig['auto'])){ + $this->error('请先开启自动采集'); + } + if($caijiConfig['run']!='backstage'){ + $this->error('不是后台运行方式'); + } + + + $curltime=time(); - $ch = curl_init (); - curl_setopt ( $ch, CURLOPT_URL, url('Admin/Index/backstage?autorun=1&runtime='.$runtime.'&curltime='.$curltime,null,false,true) ); - curl_setopt ( $ch, CURLOPT_TIMEOUT, 2 ); - curl_setopt ( $ch, CURLOPT_RETURNTRANSFER, 1 ); - curl_setopt ( $ch, CURLOPT_FOLLOWLOCATION, 1 ); - curl_setopt ( $ch, CURLOPT_HEADER, 1 ); - curl_setopt ( $ch, CURLOPT_NOBODY, 1 ); - curl_setopt ( $ch, CURLOPT_SSL_VERIFYPEER, FALSE ); - curl_setopt ( $ch, CURLOPT_SSL_VERIFYHOST, FALSE ); - curl_exec ( $ch ); - curl_close ( $ch ); - - sleep(1); + @get_html(url('Admin/Index/backstage?key='.$key.'&curltime='.$curltime,null,false,true),null,array('timeout'=>2)); + + sleep(5); $cacheCurl=cache($curlCname); $continue=false; - if(empty($cacheCurl)||$cacheCurl<$curltime){ + $maxTimes++; + if((empty($cacheCurl)||$cacheCurl<$curltime)&&$maxTimes<=3){ $continue=true; } }while($continue); }catch(\Exception $ex){ - + } + exit(); + } - exit(); - } /*访问执行采集*/ public function caijiAction(){ - if(empty($GLOBALS['config']['caiji']['auto'])){ + if(empty($GLOBALS['_sc']['c']['caiji']['auto'])){ $this->error('请先开启自动采集','Admin/Setting/caiji'); } @get_html(url('Admin/Api/collect?backstage=1',null,false,true),null,array('timeout'=>3)); - $waitTime=$GLOBALS['config']['caiji']['interval']*60; - $waitTime=$waitTime>0?$waitTime:3; + $waitTime=$GLOBALS['_sc']['c']['caiji']['interval']*60; + $waitTime=$waitTime>0?$waitTime:60; $this->success('正在采集...','Admin/Index/caiji',null,$waitTime); } /*任务api发布*/ @@ -138,11 +129,11 @@ class Index extends BaseController{ public function loginAction(){ if(request()->isPost()){ if(!check_usertoken()){ - $this->error(lang('usertoken_error')); + $this->error(lang('usertoken_error'),'Admin/Index/login'); } $mcacheLogin=CacheModel::getInstance('login'); - $config_login=$GLOBALS['config']['site']['login']; + $config_login=$GLOBALS['_sc']['c']['site']['login']; $clientIpMd5=md5(request()->ip()); if(!empty($config_login['limit'])){ @@ -155,7 +146,7 @@ class Index extends BaseController{ if(input('post.sublogin')){ $username=strtolower(trim(input('post.username'))); $pwd=trim(input('post.password')); - if($GLOBALS['config']['site']['verifycode']){ + if($GLOBALS['_sc']['c']['site']['verifycode']){ $verifycode=trim(input('post.verifycode')); $check=check_verify($verifycode); @@ -241,7 +232,7 @@ class Index extends BaseController{ /*退出*/ public function logoutAction(){ \think\Cookie::delete('login_history'); - unset($GLOBALS['user']); + unset($GLOBALS['_sc']['user']); session('user_id',null); session('is_admin',null); $this->success(lang('op_success'),'Admin/Index/index'); @@ -291,9 +282,9 @@ class Index extends BaseController{ if($step===1){ if(!check_usertoken()){ - $this->error(lang('usertoken_error')); + $this->error(lang('usertoken_error'),'Admin/Index/find_password'); } - if($GLOBALS['config']['site']['verifycode']){ + if($GLOBALS['_sc']['c']['site']['verifycode']){ $verifycode=trim(input('verifycode')); $check=check_verify($verifycode); @@ -370,7 +361,7 @@ class Index extends BaseController{ }else{ if($step===2){ $emailStatus=array('success'=>false,'msg'=>''); - if(empty($GLOBALS['config']['email'])){ + if(empty($GLOBALS['_sc']['c']['email'])){ $emailStatus['msg']=lang('config_error_none_email'); }else{ $waitTime=60; @@ -384,7 +375,7 @@ class Index extends BaseController{ $minutes=floor($expire/60); $yzm=mt_rand(100000,999999); session($waitSname,NOW_TIME); - $mailReturn=send_mail($GLOBALS['config']['email'], $stepSession['user']['email'], $stepSession['user']['username'],lang('find_pwd_email_subject'),lang('find_pwd_email_body',array('yzm'=>$yzm,'minutes'=>$minutes))); + $mailReturn=send_mail($GLOBALS['_sc']['c']['email'], $stepSession['user']['email'], $stepSession['user']['username'],lang('find_pwd_email_subject'),lang('find_pwd_email_body',array('yzm'=>$yzm,'minutes'=>$minutes))); if($mailReturn===true){ $yzmSname='send_yzm.'.md5($username); session(array('name'=>$yzmSname,'expire'=>$expire)); diff --git a/SkycaijiApp/admin/controller/Mystore.php b/SkycaijiApp/admin/controller/Mystore.php index 8bedc7a..422fe7c 100644 --- a/SkycaijiApp/admin/controller/Mystore.php +++ b/SkycaijiApp/admin/controller/Mystore.php @@ -11,17 +11,9 @@ namespace skycaiji\admin\controller; +use skycaiji\admin\model\FuncApp; class Mystore extends BaseController { public function indexAction(){ - - - - - - - - - $this->redirect('Mystore/rule'); } public function ruleAction(){ @@ -66,8 +58,8 @@ class Mystore extends BaseController { } } - $GLOBALS['content_header']='已下载'; - $GLOBALS['breadcrumb']=breadcrumb(array(array('url'=>url('Mystore/index'),'title'=>'已下载'),lang('rule_'.$type))); + $GLOBALS['_sc']['p_name']='已下载'; + $GLOBALS['_sc']['p_nav']=breadcrumb(array(array('url'=>url('Mystore/rule'),'title'=>'已下载:'.lang('rule_'.$type)))); $this->assign('ruleList',$ruleList); @@ -103,9 +95,9 @@ class Mystore extends BaseController { $auto=input('auto/d',0); model('Config')->setConfig('store_auto_check_rule',$auto); if($auto){ - $this->success('设置为自动检测更新'); + $this->success('规则设置为自动检测更新'); }else{ - $this->error('设置为手动检测更新'); + $this->error('规则设置为手动检测更新'); } }elseif($op=='check_store_update'){ @@ -213,8 +205,8 @@ class Mystore extends BaseController { } } - $GLOBALS['content_header']='已下载'; - $GLOBALS['breadcrumb']=breadcrumb(array(array('url'=>url('Mystore/index'),'title'=>'已下载'),'发布插件')); + $GLOBALS['_sc']['p_name']='已下载'; + $GLOBALS['_sc']['p_nav']=breadcrumb(array(array('url'=>url('Mystore/releaseApp'),'title'=>'已下载:发布插件'))); $this->assign('appList',$appList); return $this->fetch('releaseApp'); @@ -242,65 +234,13 @@ class Mystore extends BaseController { $this->success(lang('op_success'),'Mystore/ReleaseApp'); }elseif($op=='auto_check'){ - $auto=input('auto/d'); - model('Config')->setConfig('store_auto_check_plugin',$auto); - if($auto){ - $this->success('设置为自动检测更新'); - }else{ - $this->error('设置为手动检测更新'); - } + $this->_auto_check_plugin(); }elseif($op=='check_store_update'){ $ids=input('ids/a'); $appList=model('ReleaseApp')->where(array('module'=>'cms','id'=>array('in',$ids)))->column('*','app'); - - $appList1=array(); - foreach ($appList as $k=>$v){ - $appList1[$v['app'].'_'.$v['provider_id']]=$v; - } - $appList=$appList1; - unset($appList1); - - $uptimeList=array(); - if(!empty($appList)){ - $provList=array(); - $provApps=array(); - foreach ($appList as $v){ - $provList[$v['provider_id']]=$v['provider_id']; - $provApps[$v['provider_id']][$v['app']]=$v['app']; - } - if(!empty($provList)){ - $provList=model('Provider')->where('id','in',$provList)->column('*','id'); - }else{ - $provList=array(); - } - - foreach ($provApps as $provId=>$apps){ - $apps=implode(',',$apps); - $apps=rawurlencode($apps); - $url='https://www.skycaiji.com'; - if(!empty($provId)&&!empty($provList[$provId])){ - - $url=$provList[$provId]['url']; - } - $url.='/client/plugin/update?apps='.$apps; - - $uptimeList=get_html($url,null,array('timeout'=>2)); - $uptimeList=json_decode($uptimeList,true); - - if(!empty($uptimeList)){ - - foreach ($uptimeList as $app=>$uptime){ - if($uptime>0&&$uptime>$appList[$app.'_'.$provId]['uptime']){ - - $updateList[]=$appList[$app.'_'.$provId]['id']; - } - } - } - } - } - + $updateList=$this->_check_store_plugin_update($appList); if(!empty($updateList)){ $this->success('',null,$updateList); }else{ @@ -392,8 +332,8 @@ class Mystore extends BaseController { } } - $GLOBALS['content_header']='应用程序'; - $GLOBALS['breadcrumb']=breadcrumb(array('应用程序')); + $GLOBALS['_sc']['p_name']='应用程序'; + $GLOBALS['_sc']['p_nav']=breadcrumb(array(array('url'=>url('Mystore/app'),'title'=>'应用程序'))); $this->assign('pagenav',$pagenav); $this->assign('dbApps',$dbApps); @@ -411,9 +351,9 @@ class Mystore extends BaseController { $auto=input('auto/d'); model('Config')->setConfig('store_auto_check_app',$auto); if($auto){ - $this->success('设置为自动检测更新'); + $this->success('应用设置为自动检测更新'); }else{ - $this->error('设置为手动检测更新'); + $this->error('应用设置为手动检测更新'); } }elseif($op=='check_store_update'){ @@ -480,4 +420,164 @@ class Mystore extends BaseController { } } } + /*函数插件*/ + public function funcAppAction(){ + $page=max(1,input('p/d',0)); + $cond=array(); + + $sortBy=input('sort','desc'); + $sortBy=($sortBy=='asc')?'asc':'desc'; + $orderKey=input('order'); + + $this->assign('sortBy',$sortBy); + $this->assign('orderKey',$orderKey); + $orderBy=!empty($orderKey)?($orderKey.' '.$sortBy):'id desc'; + $mfuncApp=model('FuncApp'); + $limit=20; + $count=$mfuncApp->where($cond)->count(); + $appList=$mfuncApp->where($cond)->order($orderBy)->paginate($limit,false,paginate_auto_config()); + + $pagenav = $appList->render(); + $this->assign('pagenav',$pagenav); + $appList=$appList->all(); + + if(!empty($appList)){ + $provList=array(); + foreach ($appList as $k=>$v){ + if(!empty($v['provider_id'])){ + + $provList[$v['provider_id']]=$v['provider_id']; + } + } + $provList=model('Provider')->where('id','in',$provList)->column('*','id'); + + foreach ($appList as $k=>$v){ + $url='https://www.skycaiji.com'; + if(!empty($v['provider_id'])&&!empty($provList[$v['provider_id']])){ + + $url=$provList[$v['provider_id']]['url']; + } + $appList[$k]['store_url']=$url.'/client/plugin/detail?app='.$v['app']; + } + } + + $GLOBALS['_sc']['p_name']='已下载'; + $GLOBALS['_sc']['p_nav']=breadcrumb(array(array('url'=>url('Mystore/funcApp'),'title'=>'已下载:函数插件'))); + + $this->assign('appList',$appList); + $this->assign('modules',$mfuncApp->funcModules); + return $this->fetch('func'); + } + + public function funcAppOpAction(){ + $op=input('op'); + $id=input('id'); + + $ops=array('item'=>array('delete','enable','detail'),'list'=>array('deleteall','check_store_update'),'else'=>array('auto_check')); + if(!in_array($op,$ops['item'])&&!in_array($op,$ops['list'])&&!in_array($op,$ops['else'])){ + + $this->error(lang('invalid_op')); + } + + $mfuncApp=new FuncApp(); + $appData=$mfuncApp->where('id',$id)->find(); + if($op=='detail'){ + $appClass=empty($appData)?array():$mfuncApp->get_app_class($appData['module'], $appData['app']); + $this->success('',null,$appClass); + }elseif($op=='enable'){ + $enable=input('enable/d'); + $mfuncApp->where('id',$appData['id'])->update(array('enable'=>$enable)); + $this->success(); + }elseif($op=='delete'){ + if(!empty($appData['module'])&&!empty($appData['app'])){ + $filename=$mfuncApp->filename($appData['module'], $appData['app']); + if(file_exists($filename)){ + unlink($filename); + } + } + $mfuncApp->where('id',$appData['id'])->delete(); + $this->success('删除成功'); + }elseif($op=='deleteall'){ + + $ids=input('ids/a'); + if(is_array($ids)&&count($ids)>0){ + $mfuncApp->where(array('id'=>array('in',$ids)))->delete(); + } + $this->success(lang('op_success'),'Mystore/funcApp'); + }elseif($op=='auto_check'){ + + $this->_auto_check_plugin(); + }elseif($op=='check_store_update'){ + + $ids=input('ids/a'); + + $appList=model('FuncApp')->where(array('id'=>array('in',$ids)))->column('*','app'); + $updateList=$this->_check_store_plugin_update($appList); + if(!empty($updateList)){ + $this->success('',null,$updateList); + }else{ + $this->error(); + } + } + } + /*插件设置自动检测*/ + public function _auto_check_plugin(){ + $auto=input('auto/d'); + model('Config')->setConfig('store_auto_check_plugin',$auto); + if($auto){ + $this->success('插件设置为自动检测更新'); + }else{ + $this->error('插件设置为手动检测更新'); + } + } + /*检测插件云平台c插件更新*/ + public function _check_store_plugin_update($appList=array()){ + $appList1=array(); + foreach ($appList as $k=>$v){ + $appList1[$v['app'].'_'.$v['provider_id']]=$v; + } + $appList=$appList1; + unset($appList1); + + $uptimeList=array(); + $updateList=array(); + if(!empty($appList)){ + $provList=array(); + $provApps=array(); + foreach ($appList as $v){ + $provList[$v['provider_id']]=$v['provider_id']; + $provApps[$v['provider_id']][$v['app']]=$v['app']; + } + if(!empty($provList)){ + $provList=model('Provider')->where('id','in',$provList)->column('*','id'); + }else{ + $provList=array(); + } + + foreach ($provApps as $provId=>$apps){ + $apps=implode(',',$apps); + $apps=rawurlencode($apps); + $url='https://www.skycaiji.com'; + if(!empty($provId)&&!empty($provList[$provId])){ + + $url=$provList[$provId]['url']; + } + $url.='/client/plugin/update?apps='.$apps; + + $uptimeList=get_html($url,null,array('timeout'=>2)); + $uptimeList=json_decode($uptimeList,true); + + if(!empty($uptimeList)){ + + foreach ($uptimeList as $app=>$uptime){ + if($uptime>0&&$uptime>$appList[$app.'_'.$provId]['uptime']){ + + $updateList[]=$appList[$app.'_'.$provId]['id']; + } + } + } + } + } + return $updateList; + } } \ No newline at end of file diff --git a/SkycaijiApp/admin/controller/Provider.php b/SkycaijiApp/admin/controller/Provider.php index 0a03878..c73f0bc 100644 --- a/SkycaijiApp/admin/controller/Provider.php +++ b/SkycaijiApp/admin/controller/Provider.php @@ -31,8 +31,8 @@ class Provider extends BaseController { $pagenav=$list->render(); $list=$list->all(); - $GLOBALS['content_header']='第三方平台'; - $GLOBALS['breadcrumb']=breadcrumb(array('第三方平台')); + $GLOBALS['_sc']['p_name']='第三方平台'; + $GLOBALS['_sc']['p_nav']=breadcrumb(array(array('url'=>url('Provider/list'),'title'=>'第三方平台'))); $this->assign('list',$list); $this->assign('pagenav',$pagenav); @@ -76,7 +76,7 @@ class Provider extends BaseController { $sort=input('sort/d',0); $enable=input('enable/d',0); - $domain=\skycaiji\admin\model\Provider::matchDomain($url); + $domain=\skycaiji\admin\model\Provider::match_domain($url); if(empty($domain)){ $this->error('网址格式错误'); } diff --git a/SkycaijiApp/admin/controller/Proxy.php b/SkycaijiApp/admin/controller/Proxy.php new file mode 100644 index 0000000..131fb85 --- /dev/null +++ b/SkycaijiApp/admin/controller/Proxy.php @@ -0,0 +1,250 @@ +input('num/d',200), + 'ip'=>input('ip'), + 'user'=>input('user'), + 'pwd'=>input('pwd'), + 'type'=>input('?type')?input('type'):'all', + 'invalid'=>input('?invalid')?input('invalid'):'all', + ); + if(!empty($search['ip'])){ + + $cond['ip']=array('like',addslashes($search['ip']).'%'); + } + if($search['type']!='all'){ + + $cond['type']=$search['type']; + } + if($search['invalid']!='all'){ + + $cond['invalid']=$search['invalid']; + } + if(!empty($search['user'])){ + + $cond['user']=$search['user']; + } + if(!empty($search['pwd'])){ + + $cond['pwd']=$search['pwd']; + } + + $count=$mproxy->where($cond)->count(); + if($count>0){ + + $dataList=$mproxy->where($cond)->order('addtime desc,no desc')->paginate($search['num'],false,paginate_auto_config()); + + $pagenav=$dataList->render(); + $pagenav=str_replace('class="pagination', 'class="pagination pagination-sm no-margin pull-right', $pagenav); + + $this->assign('pagenav',$pagenav); + $dataList=$dataList->all(); + $dataList=empty($dataList)?array():$dataList; + + + foreach ($dataList as $k=>$v){ + $v=$v->toArray(); + foreach ($v as $vk=>$vv){ + $v[$vk]=htmlspecialchars($vv,ENT_QUOTES); + } + $dataList[$k]=$v; + } + $this->assign('dataList',$dataList); + } + + $urlParams=input('param.'); + $urlParams=http_build_query($urlParams); + + $this->assign('proxyTypes',$mproxy->proxy_types()); + $this->assign('search',$search); + $this->assign('urlParams',$urlParams); + return $this->fetch(); + } + + public function opAction(){ + $op=input('op'); + $listUrl=input('url_params','','trim'); + $listUrl='Proxy/list?'.ltrim($listUrl,'?'); + + $mproxy=model('Proxyip'); + if($op=='delete'){ + $ip=input('ip'); + $mproxy->where('ip',$ip)->delete(); + $this->success('删除成功',$listUrl); + }elseif($op=='delete_all'){ + + $ips=input('ips','','trim'); + $ips=empty($ips)?array():json_decode($ips,true); + $ips=array_map('trim', $ips); + + if(!empty($ips)){ + $mproxy->where('ip','in',$ips)->delete(); + } + $this->success('删除成功',$listUrl); + }elseif($op=='update_all'){ + + $ips=input('ips','','trim'); + $ip_list=input('ip_list','','trim'); + $user_list=input('user_list','','trim'); + $pwd_list=input('pwd_list','','trim'); + $type_list=input('type_list','','trim'); + + $ips=empty($ips)?array():json_decode($ips,true); + $ip_list=empty($ip_list)?array():json_decode($ip_list,true); + $user_list=empty($user_list)?array():json_decode($user_list,true); + $pwd_list=empty($pwd_list)?array():json_decode($pwd_list,true); + $type_list=empty($type_list)?array():json_decode($type_list,true); + + $ips=array_map('trim', $ips); + $ip_list=array_map('trim', $ip_list); + $user_list=array_map('trim', $user_list); + $pwd_list=array_map('trim', $pwd_list); + $type_list=array_map('trim', $type_list); + + for($i=0;$istrict(false)->where('ip',$ips[$i])->update(array( + 'ip'=>$ip_list[$i], + 'user'=>$user_list[$i], + 'pwd'=>$pwd_list[$i], + 'type'=>$type_list[$i], + )); + } + $this->success('修改成功',$listUrl); + } + } + public function addAction(){ + $mproxy=model('Proxyip'); + $proxyTypes=$mproxy->proxy_types(); + if(request()->isPost()){ + $ip_list=input('ip_list/a','','trim'); + $user_list=input('user_list/a','','trim'); + $pwd_list=input('pwd_list/a','','trim'); + $type_list=input('type_list/a','','trim'); + + if(!empty($ip_list)){ + foreach($ip_list as $k=>$v){ + $newData=array( + 'ip'=>$v, + 'user'=>$user_list[$k], + 'pwd'=>$pwd_list[$k], + 'type'=>$type_list[$k], + 'addtime'=>NOW_TIME + ); + $mproxy->db()->strict(false)->insert($newData,true); + } + $this->success('添加成功'); + }else{ + $this->error('请添加ip'); + } + }else{ + $this->assign('proxyTypes',$proxyTypes); + return $this->fetch(); + } + + } + + /*批量添加代理*/ + public function batchAction(){ + $mproxy=model('Proxyip'); + $proxyTypes=$mproxy->proxy_types(); + if(request()->isPost()){ + $type=input('type'); + $ips=input('ips','',null); + $fmt=input('format','','trim'); + $user=input('user','','trim'); + $pwd=input('pwd','','trim'); + + $ipList=array(); + if(!empty($fmt)&&preg_match_all('/[^\r\n]+/',$ips,$mips)){ + foreach ($mips[0] as $ip){ + $ip=model('Proxyip')->get_format_ips($ip,$fmt,false); + if(empty($ip)){ + continue; + } + $ipList[]=$ip; + } + } + + $ipList=$mproxy->ips_format2db($ipList,array( + 'type'=>$type, + 'user'=>$user, + 'pwd'=>$pwd + )); + + if(empty($ipList)){ + $this->error('没有匹配到数据'); + } + + if(input('is_test')){ + + $ips=''; + $proxyTypes=array_flip($proxyTypes); + foreach($ipList as $ip){ + $ips.=$ip['ip'].' - '.$proxyTypes[$ip['type']].($ip['user']?(' - '.$ip['user'].':'.$ip['pwd']):'').PHP_EOL; + } + $this->success($ips); + }else{ + + $mproxy->strict(false)->insertAll($ipList,true,500); + $this->success('批量添加成功'); + } + }else{ + $this->assign('proxyTypes',$proxyTypes); + return $this->fetch(); + } + } + + + public function clearInvalidAction(){ + $mproxy=model('Proxyip'); + $mproxy->where('invalid',1)->delete(); + $this->success('清理完成','Setting/proxy'); + } + + + public function testApiAction(){ + $config=input('config/a','','trim'); + $mproxy=model('Proxyip'); + + $html=get_html($config['api_url']); + $ips=$mproxy->get_format_ips($html,$config['api_format'],true); + $ips=$mproxy->ips_format2db ( $ips, array ( + 'type' => $config ['api_type'], + 'user' => $config ['api_user'], + 'pwd' => $config ['api_pwd'], + ) ); + + + $types=$mproxy->proxy_types(); + $types=array_flip($types); + + $ipsStr=''; + foreach ($ips as $ip){ + if(empty($ip)){ + continue; + } + $ipsStr.=$ip['ip'].' - '.$types[$ip['type']].($ip['user']?(' - '.$ip['user'].':'.$ip['pwd']):'').PHP_EOL; + } + $ips=$ipsStr; + unset($ipsStr); + + $this->assign('ips',$ips); + return $this->fetch('testApi'); + } +} \ No newline at end of file diff --git a/SkycaijiApp/admin/controller/Release.php b/SkycaijiApp/admin/controller/Release.php index 2d06a4a..e3744df 100644 --- a/SkycaijiApp/admin/controller/Release.php +++ b/SkycaijiApp/admin/controller/Release.php @@ -60,8 +60,8 @@ class Release extends BaseController{ $this->error(lang('op_failed')); } }else{ - $GLOBALS['content_header']=lang('rele_set'); - $GLOBALS['breadcrumb']=breadcrumb(array(array('url'=>url('Task/edit?id='.$taskData['id']),'title'=>lang('task').lang('separator').$taskData['name']),lang('rele_set'))); + $GLOBALS['_sc']['p_name']=lang('rele_set'); + $GLOBALS['_sc']['p_nav']=breadcrumb(array(array('url'=>url('Task/edit?id='.$taskData['id']),'title'=>lang('task').lang('separator').$taskData['name']),array('url'=>url('Release/set?task_id='.$taskData['id']),'title'=>lang('rele_set')))); $this->assign('taskData',$taskData); if(!empty($releData)){ diff --git a/SkycaijiApp/admin/controller/Setting.php b/SkycaijiApp/admin/controller/Setting.php index 86dffa3..8a44ff8 100644 --- a/SkycaijiApp/admin/controller/Setting.php +++ b/SkycaijiApp/admin/controller/Setting.php @@ -11,6 +11,7 @@ namespace skycaiji\admin\controller; +use skycaiji\admin\model\CacheModel; class Setting extends BaseController { /*站点设置*/ public function siteAction(){ @@ -35,8 +36,8 @@ class Setting extends BaseController { $mconfig->setConfig('site',$config); $this->success(lang('op_success'),'Setting/site'); }else{ - $GLOBALS['content_header']=lang('setting_site'); - $GLOBALS['breadcrumb']=breadcrumb(array(array('url'=>url('Setting/site'),'title'=>lang('setting_site')))); + $GLOBALS['_sc']['p_name']=lang('setting_site'); + $GLOBALS['_sc']['p_nav']=breadcrumb(array(array('url'=>url('Setting/site'),'title'=>lang('setting_site')))); $siteConfig=$mconfig->getConfig('site','data'); $this->assign('siteConfig',$siteConfig); } @@ -46,15 +47,68 @@ class Setting extends BaseController { public function caijiAction(){ $mconfig=model('Config'); if(request()->isPost()){ + + if($mconfig->where('cname','download_img')->count()<=0){ + + $caijiConfig=$mconfig->getConfig('caiji','data'); + $imgConfig=$mconfig->get_img_config_from_caiji($caijiConfig); + if(!empty($imgConfig)){ + + $mconfig->setConfig('download_img',$imgConfig); + } + } + $config=array(); + $config['robots']=input('robots/d',0); $config['auto']=input('auto/d',0); $config['run']=input('run'); $config['server']=input('server'); + $config['server_php']=input('server_php'); $config['num']=input('num/d',0); $config['interval']=input('interval/d',0); $config['timeout']=input('timeout/d',0); $config['html_interval']=input('html_interval/d',0); $config['real_time']=input('real_time/d',0); + + unset($config['download_img']); + + if($config['server']=='cli'){ + + if(!function_exists('proc_open')){ + $this->error('抱歉cli命令行模式需开启proc_open函数'); + } + } + + $mconfig->setConfig('caiji',$config); + if($config['auto']){ + + remove_auto_collecting(); + if($config['run']=='backstage'){ + + $runkey=md5(time().rand(1, 1000000)); + CacheModel::getInstance()->setCache('admin_index_backstage_key', $runkey); + @get_html(url('Admin/Index/backstage?key='.$runkey,null,false,true),null,array('timeout'=>3)); + } + } + $this->success(lang('op_success'),'Setting/caiji'); + }else{ + $GLOBALS['_sc']['p_name']=lang('setting_caiji'); + $GLOBALS['_sc']['p_nav']=breadcrumb(array(array('url'=>url('Setting/caiji'),'title'=>lang('setting_caiji')))); + $caijiConfig=$mconfig->getConfig('caiji','data'); + $caijiConfig=is_array($caijiConfig)?$caijiConfig:array(); + + $phpExeFile=$mconfig->detect_php_exe(); + + $this->assign('caijiConfig',$caijiConfig); + $this->assign('phpExeFile',$phpExeFile); + } + return $this->fetch(); + } + /*图片本地化设置*/ + public function download_imgAction(){ + $mconfig=model('Config'); + if(request()->isPost()){ + $config=array(); $config['download_img']=input('download_img/d',0); $config['img_path']=trim(input('img_path','')); $config['img_url']=input('img_url','','trim'); @@ -62,45 +116,54 @@ class Setting extends BaseController { $config['img_timeout']=input('img_timeout/d',0); $config['img_interval']=input('img_interval/d',0); $config['img_max']=input('img_max/d',0); - - if($config['server']=='cli'){ - - if(!function_exists('proc_open')){ - $this->error('抱歉cli命令行模式需开启proc_open函数'); - } - } - if(!empty($config['img_path'])){ - - $checkImgPath=$mconfig->check_img_path($config['img_path']); - if(!$checkImgPath['success']){ - $this->error($checkImgPath['msg']); - } - } - if(!empty($config['img_url'])){ - - $checkImgUrl=$mconfig->check_img_url($config['img_url']); - if(!$checkImgUrl['success']){ - $this->error($checkImgUrl['msg']); - } - } - $mconfig->setConfig('caiji',$config); - if($config['auto']){ - - remove_auto_collecting(); - if($config['run']=='backstage'){ - - @get_html(url('Admin/Index/backstage?autorun=1',null,false,true),null,array('timeout'=>3)); - } + $config['name_custom_path']=input('name_custom_path',''); + $config['name_custom_name']=input('name_custom_name',''); + if(!empty($config['img_path'])){ + + $checkImgPath=$mconfig->check_img_path($config['img_path']); + if(!$checkImgPath['success']){ + $this->error($checkImgPath['msg']); + } } - - $this->success(lang('op_success'),'Setting/caiji'); + if(!empty($config['img_url'])){ + + $checkImgUrl=$mconfig->check_img_url($config['img_url']); + if(!$checkImgUrl['success']){ + $this->error($checkImgUrl['msg']); + } + } + + $checkNamePath=$mconfig->check_img_name_path($config['name_custom_path']); + if($config['img_name']=='custom'){ + + if(empty($config['name_custom_path'])){ + $this->error('请输入图片名称自定义目录'); + } + if(!$checkNamePath['success']){ + $this->error($checkNamePath['msg']); + } + }else{ + + if(!$checkNamePath['success']){ + $config['name_custom_path']=''; + } + } + + $mconfig->setConfig('download_img',$config); + + $this->success(lang('op_success'),'Setting/download_img'); }else{ - $GLOBALS['content_header']=lang('setting_caiji'); - $GLOBALS['breadcrumb']=breadcrumb(array(array('url'=>url('Setting/caiji'),'title'=>lang('setting_caiji')))); - $caijiConfig=$mconfig->getConfig('caiji','data'); - $this->assign('caijiConfig',$caijiConfig); + $GLOBALS['_sc']['p_name']='图片本地化设置'; + $GLOBALS['_sc']['p_nav']=breadcrumb(array(array('url'=>url('Setting/caiji'),'title'=>lang('setting_caiji')),array('url'=>url('Setting/download_img'),'title'=>'图片本地化'))); + $imgConfig=$mconfig->getConfig('download_img','data'); + if(empty($imgConfig)){ + + $caijiConfig=$mconfig->getConfig('caiji','data'); + $imgConfig=$mconfig->get_img_config_from_caiji($caijiConfig); + } + $this->assign('imgConfig',$imgConfig); + return $this->fetch('download_img'); } - return $this->fetch(); } /*代理设置*/ public function proxyAction(){ @@ -111,10 +174,12 @@ class Setting extends BaseController { $ip_list=input('ip_list','','trim'); $user_list=input('user_list','','trim'); $pwd_list=input('pwd_list','','trim'); + $type_list=input('type_list','','trim'); - $ip_list=empty($ip_list)?null:json_decode($ip_list,true); - $user_list=empty($user_list)?null:json_decode($user_list,true); - $pwd_list=empty($pwd_list)?null:json_decode($pwd_list,true); + $ip_list=empty($ip_list)?array():json_decode($ip_list,true); + $user_list=empty($user_list)?array():json_decode($user_list,true); + $pwd_list=empty($pwd_list)?array():json_decode($pwd_list,true); + $type_list=empty($type_list)?array():json_decode($type_list,true); $config['open']=input('open/d',0); $config['failed']=input('failed/d',0); @@ -132,13 +197,14 @@ class Setting extends BaseController { if(!empty($ip_list)&&is_array($ip_list)){ - $mproxy->where(array('ip'=>array('not in',$ip_list)))->delete(); - $ip_list=array_map('trim', $ip_list); $user_list=array_map('trim', $user_list); $pwd_list=array_map('trim', $pwd_list); + $type_list=array_map('trim', $type_list); - foreach ($ip_list as $k=>$v){ + + for($k=count($ip_list);$k>=0;$k--){ + $v=$ip_list[$k]; if(empty($v)){ continue; @@ -147,10 +213,12 @@ class Setting extends BaseController { 'ip'=>$v, 'user'=>$user_list[$k], 'pwd'=>$pwd_list[$k], + 'type'=>$type_list[$k], 'invalid'=>0, 'failed'=>0, 'num'=>0, 'time'=>0, + 'addtime'=>NOW_TIME, ); if($mproxy->where(array('ip'=>$newData['ip']))->count()>0){ @@ -162,60 +230,44 @@ class Setting extends BaseController { $mproxy->db()->insert($newData,true); } } - }else{ - - $mproxy->where('1=1')->delete(); } + + + $config['api']=input('api/a','','trim'); + $config['apis']=input('apis/a','','trim'); + $config['apis']=is_array($config['apis'])?$config['apis']:array(); + foreach ($config['apis'] as $k=>$v){ + if(empty($v['api_url'])||!preg_match('/^\w+\:\/\//',$v['api_url'])){ + + unset($config['apis'][$k]); + } + } + $config['apis']=array_values($config['apis']); + $mconfig->setConfig('proxy',$config); $this->success(lang('op_success'),'Setting/Proxy'); }else{ - $GLOBALS['content_header']='代理设置'; - $GLOBALS['breadcrumb']=breadcrumb(array(array('url'=>url('Setting/Proxy'),'title'=>'代理设置'))); + $GLOBALS['_sc']['p_name']='代理设置'; + $GLOBALS['_sc']['p_nav']=breadcrumb(array(array('url'=>url('Setting/caiji'),'title'=>lang('setting_caiji')),array('url'=>url('Setting/Proxy'),'title'=>'代理'))); $proxyConfig=$mconfig->getConfig('proxy','data'); - $proxyConfig['ip_list']=$mproxy->column('*'); + + $proxyConfig['ip_count']=$mproxy->count(); $this->assign('proxyConfig',$proxyConfig); + $this->assign('proxyTypes',$mproxy->proxy_types()); } return $this->fetch(); } - /*批量添加代理*/ - public function proxyBatchAction(){ - if(request()->isPost()){ - $ips=input('ips'); - $fmt=input('format'); - - $fmt=str_replace(array('[ip]','[端口]','[用户名]','[密码]') - ,array('(?P(\d+\.)+\d+)','(?P\d+)','(?P[^\s]+)','(?P[^\s]+)') - ,$fmt); - $ipList=array(); - if(preg_match_all('/[^\r\n]+/',$ips,$m_ips)){ - foreach ($m_ips[0] as $ip){ - if(preg_match('/'.$fmt.'/',$ip,$ipInfo)){ - $ipList[]=array( - 'ip'=>$ipInfo['ip'].':'.$ipInfo['port'], - 'user'=>$ipInfo['user'], - 'pwd'=>$ipInfo['pwd'], - ); - } - } - } - if(empty($ipList)){ - $this->error('没有匹配到数据'); - }else{ - $this->success('',null,$ipList); - } - }else{ - return $this->fetch('proxyBatch'); - } - } /*翻译设置*/ public function translateAction(){ $mconfig=model('Config'); + $apiTypes=array('baidu','youdao','qq'); if(request()->isPost()){ $config=array(); $config['open']=input('open/d',0); $config['api']=input('api','','strtolower'); - $config['baidu']=input('baidu/a',null,'trim'); - $config['youdao']=input('youdao/a',null,'trim'); + foreach ($apiTypes as $v){ + $config[$v]=input($v.'/a',null,'trim'); + } if(!empty($config['api'])){ if(empty($config[$config['api']])){ @@ -231,18 +283,21 @@ class Setting extends BaseController { $mconfig->setConfig('translate',$config); $this->success(lang('op_success'),'Setting/translate'); }else{ - $GLOBALS['content_header']='翻译设置'; - $GLOBALS['breadcrumb']=breadcrumb(array(array('url'=>url('Setting/translate'),'title'=>'翻译设置'))); + $GLOBALS['_sc']['p_name']='翻译设置'; + $GLOBALS['_sc']['p_nav']=breadcrumb(array(array('url'=>url('Setting/caiji'),'title'=>lang('setting_caiji')),array('url'=>url('Setting/translate'),'title'=>'翻译'))); $transConfig=$mconfig->getConfig('translate','data'); - - foreach ($transConfig['baidu'] as $k=>$v){ - $transConfig['baidu'][$k]=htmlspecialchars($v,ENT_QUOTES); - } - foreach ($transConfig['youdao'] as $k=>$v){ - $transConfig['youdao'][$k]=htmlspecialchars($v,ENT_QUOTES); + $apiLangs=array(); + foreach ($apiTypes as $api){ + $transConfig[$api]=is_array($transConfig[$api])?$transConfig[$api]:array(); + foreach ($transConfig[$api] as $k=>$v){ + $transConfig[$api][$k]=htmlspecialchars($v,ENT_QUOTES); + } + $apiLangs[$api]=\util\Translator::get_api_langs($api); + $apiLangs[$api]=is_array($apiLangs[$api])?implode(', ',$apiLangs[$api]):''; } $this->assign('transConfig',$transConfig); + $this->assign('apiLangs',$apiLangs); return $this->fetch(); } } @@ -272,8 +327,8 @@ class Setting extends BaseController { $this->success(lang('op_success'),'Setting/email'); } }else{ - $GLOBALS['content_header']=lang('setting_email'); - $GLOBALS['breadcrumb']=breadcrumb(array(array('url'=>url('Setting/email'),'title'=>lang('setting_email')))); + $GLOBALS['_sc']['p_name']=lang('setting_email'); + $GLOBALS['_sc']['p_nav']=breadcrumb(array(array('url'=>url('Setting/email'),'title'=>lang('setting_email')))); $emailConfig=$mconfig->getConfig('email','data'); $this->assign('emailConfig',$emailConfig); } @@ -317,8 +372,8 @@ class Setting extends BaseController { } $this->success(lang('op_success'),'Setting/page_render'); }else{ - $GLOBALS['content_header']='页面渲染设置 '; - $GLOBALS['breadcrumb']=breadcrumb(array(array('url'=>url('Setting/page_render'),'title'=>'页面渲染设置'))); + $GLOBALS['_sc']['p_name']='页面渲染设置 '; + $GLOBALS['_sc']['p_nav']=breadcrumb(array(array('url'=>url('Setting/caiji'),'title'=>lang('setting_caiji')),array('url'=>url('Setting/page_render'),'title'=>'页面渲染'))); $config=$mconfig->getConfig('page_render','data'); $this->assign('config',$config); @@ -329,7 +384,7 @@ class Setting extends BaseController { $toolIsOpen=$chromeSoket->hostIsOpen(); $this->assign('toolIsOpen',$toolIsOpen); } - return $this->fetch(); + return $this->fetch('page_render'); } } /*清理缓存目录*/ diff --git a/SkycaijiApp/admin/controller/Store.php b/SkycaijiApp/admin/controller/Store.php index b5bd22b..599adf7 100644 --- a/SkycaijiApp/admin/controller/Store.php +++ b/SkycaijiApp/admin/controller/Store.php @@ -14,7 +14,7 @@ namespace skycaiji\admin\controller; class Store extends BaseController { public function isLoginAction(){ - if(empty($GLOBALS['user'])){ + if(empty($GLOBALS['_sc']['user'])){ $this->dispatchJump(false,lang('user_error_is_not_admin'),url('Admin/Index/index',null,null,true)); }else{ $this->dispatchJump(true); @@ -34,7 +34,7 @@ class Store extends BaseController { $url=$provData['url']; $url.=strpos($url, '?')===false?'?':'&'; - $url.='clientinfo='.urlencode($GLOBALS['clientinfo']); + $url.='clientinfo='.urlencode($GLOBALS['_sc']['clientinfo']); $this->assign('provData',$provData); } @@ -46,8 +46,8 @@ class Store extends BaseController { } - $GLOBALS['content_header']=lang('store'); - $GLOBALS['breadcrumb']=breadcrumb(array(lang('store'))); + $GLOBALS['_sc']['p_name']=lang('store'); + $GLOBALS['_sc']['p_nav']=breadcrumb(array(array('url'=>url('Store/index'),'title'=>lang('store')))); $this->assign('url',$url); return $this->fetch(); @@ -124,8 +124,14 @@ class Store extends BaseController { $newData['provider_id']=$this->_getStoreProvid($plugin['store_url']); if($plugin['type']=='release'){ + model('ReleaseApp')->addCms($newData,$plugin['code'],$plugin['tpl']); $this->dispatchJump(true); + }elseif($plugin['type']=='func'){ + + $newData['module']=$plugin['module']; + model('FuncApp')->addFunc($newData,$plugin['code']); + $this->dispatchJump(true); }else{ $this->dispatchJump(false); } @@ -245,6 +251,8 @@ class Store extends BaseController { $cond=array('app'=>array('in',$apps),'provider_id'=>$provId); if($type=='release'||$type=='cms'){ $list=model('ReleaseApp')->where($cond)->column('uptime','app'); + }elseif($type=='func'){ + $list=model('FuncApp')->where($cond)->column('uptime','app'); }elseif($type=='app'){ foreach ($apps as $app){ diff --git a/SkycaijiApp/admin/controller/Task.php b/SkycaijiApp/admin/controller/Task.php index a604d61..85bd9cb 100644 --- a/SkycaijiApp/admin/controller/Task.php +++ b/SkycaijiApp/admin/controller/Task.php @@ -121,8 +121,8 @@ class Task extends BaseController { $this->assign('pagenav',$pagenav); } $showChange=$show=='list'?'folder':'list'; - $GLOBALS['content_header']=lang('task_list').' '.lang('task_change_'.$showChange).''; - $GLOBALS['breadcrumb']=breadcrumb(array(array('url'=>url('Task/list'),'title'=>lang('task_list')))); + $GLOBALS['_sc']['p_name']=lang('task_list').' '.lang('task_change_'.$showChange).''; + $GLOBALS['_sc']['p_nav']=breadcrumb(array(array('url'=>url('Task/list'),'title'=>lang('task_list')))); return $this->fetch('list_'.$show); } /*任务列表,打开文件夹*/ @@ -221,8 +221,8 @@ class Task extends BaseController { $mtaskgroup=model('Taskgroup'); $tgSelect=$mtaskgroup->getLevelSelect(); - $GLOBALS['content_header']=lang('task_add'); - $GLOBALS['breadcrumb']=breadcrumb(array(array('url'=>url('Task/list'),'title'=>lang('task_list')),lang('task_add'))); + $GLOBALS['_sc']['p_name']=lang('task_add'); + $GLOBALS['_sc']['p_nav']=breadcrumb(array(array('url'=>url('Task/list'),'title'=>lang('task_list')),array('url'=>url('Task/add'),'title'=>lang('task_add')))); $this->assign('tgSelect',$tgSelect); @@ -287,8 +287,8 @@ class Task extends BaseController { $mtaskgroup=model('Taskgroup'); $tgSelect=$mtaskgroup->getLevelSelect(); - $GLOBALS['content_header']=lang('task_edit'); - $GLOBALS['breadcrumb']=breadcrumb(array(array('url'=>url('Task/list'),'title'=>lang('task_list')),lang('task_edit'))); + $GLOBALS['_sc']['p_name']=lang('task_edit').':'.$taskData['name']; + $GLOBALS['_sc']['p_nav']=breadcrumb(array(array('url'=>url('Task/list'),'title'=>lang('task_list')),array('url'=>url('Task/edit?id='.$taskData['id']),'title'=>$taskData['name']))); $this->assign('tgSelect',$tgSelect); $this->assign('taskData',$taskData); @@ -431,7 +431,7 @@ class Task extends BaseController { if(!IS_CLI){ ignore_user_abort(true); - if($GLOBALS['config']['caiji']['server']=='cli'){ + if($GLOBALS['_sc']['c']['caiji']['server']=='cli'){ cli_command_exec('collect task --task_id '.$taskId); exit(); @@ -460,7 +460,7 @@ class Task extends BaseController { if(!IS_CLI){ ignore_user_abort(true); - if($GLOBALS['config']['caiji']['server']=='cli'){ + if($GLOBALS['_sc']['c']['caiji']['server']=='cli'){ cli_command_exec('collect batch --task_ids '.implode(',',$taskIds)); exit(); @@ -472,8 +472,8 @@ class Task extends BaseController { ignore_user_abort(false); } - if($GLOBALS['config']['caiji']['timeout']>0){ - set_time_limit(60*$GLOBALS['config']['caiji']['timeout']); + if($GLOBALS['_sc']['c']['caiji']['timeout']>0){ + set_time_limit(60*$GLOBALS['_sc']['c']['caiji']['timeout']); }else{ set_time_limit(0); } @@ -498,30 +498,30 @@ class Task extends BaseController { } /*将任务标记为后台运行*/ public function _backstage_task($taskId){ - $GLOBALS['backstage_task_runtime']=time(); + $GLOBALS['_sc']['backstage_task_runtime']=time(); if(model('Task')->where('id',$taskId)->count()>0){ $mcache=CacheModel::getInstance('backstage_task'); $mcache->db()->strict(false)->insert(array( 'cname'=>$taskId, - 'dateline'=>$GLOBALS['backstage_task_runtime'], + 'dateline'=>$GLOBALS['_sc']['backstage_task_runtime'], 'ctype'=>0, 'data'=>'' ),true); - if(!isset($GLOBALS['backstage_task_ids'])){ - $GLOBALS['backstage_task_ids']=array(); + if(!isset($GLOBALS['_sc']['backstage_task_ids'])){ + $GLOBALS['_sc']['backstage_task_ids']=array(); } - $GLOBALS['backstage_task_ids'][$taskId]=$taskId; + $GLOBALS['_sc']['backstage_task_ids'][$taskId]=$taskId; static $registered=false; if(!$registered){ register_shutdown_function(function(){ - if(!empty($GLOBALS['backstage_task_ids'])&&is_array($GLOBALS['backstage_task_ids'])){ + if(!empty($GLOBALS['_sc']['backstage_task_ids'])&&is_array($GLOBALS['_sc']['backstage_task_ids'])){ $mcache=\skycaiji\admin\model\CacheModel::getInstance('backstage_task'); - $mcache->db()->strict(false)->where('cname','in',$GLOBALS['backstage_task_ids'])->update(array('ctype'=>1,'data'=>time())); + $mcache->db()->strict(false)->where('cname','in',$GLOBALS['_sc']['backstage_task_ids'])->update(array('ctype'=>1,'data'=>time())); } }); $registered=true; @@ -532,8 +532,8 @@ class Task extends BaseController { public function _collect($taskId){ static $setted_timeout=null; if(!isset($setted_timeout)){ - if($GLOBALS['config']['caiji']['timeout']>0){ - set_time_limit(60*$GLOBALS['config']['caiji']['timeout']); + if($GLOBALS['_sc']['c']['caiji']['timeout']>0){ + set_time_limit(60*$GLOBALS['_sc']['c']['caiji']['timeout']); }else{ set_time_limit(0); } @@ -579,11 +579,11 @@ class Task extends BaseController { $acoll->init($collData); $arele=controller('admin/R'.strtolower($releData['module']),'event'); $arele->init($releData); - $GLOBALS['real_time_release']=&$arele; + $GLOBALS['_sc']['real_time_release']=&$arele; if('api'==$releData['module']){ - $GLOBALS['config']['caiji']['real_time']=0; + $GLOBALS['_sc']['c']['caiji']['real_time']=0; $cacheApiData=$arele->get_cache_fields(); @@ -598,7 +598,7 @@ class Task extends BaseController { $all_field_list=array(); - $caijiNum=intval($GLOBALS['config']['caiji']['num']); + $caijiNum=intval($GLOBALS['_sc']['c']['caiji']['num']); $taskNum=intval($taskData['config']['num']); if($taskNum<=0||($caijiNum>0&&$taskNum>$caijiNum)){ @@ -620,10 +620,10 @@ class Task extends BaseController { }elseif(is_array($field_list)&&!empty($field_list)){ $all_field_list=array_merge($all_field_list,$field_list); - $taskNum-=count($field_list); + $taskNum-=count((array)$field_list); } if($taskNum>0){ - $this->echo_msg('采集到'.count($field_list).'条数据,还差'.$taskNum.'条','orange'); + $this->echo_msg('采集到'.count((array)$field_list).'条数据,还差'.$taskNum.'条','orange'); } } }else{ @@ -640,8 +640,8 @@ class Task extends BaseController { if(empty($all_field_list)){ $this->echo_msg('没有采集到数据','orange'); }else{ - $this->echo_msg('采集到'.count($all_field_list).'条数据','green'); - if(empty($GLOBALS['config']['caiji']['real_time'])){ + $this->echo_msg('采集到'.count((array)$all_field_list).'条数据','green'); + if(empty($GLOBALS['_sc']['c']['caiji']['real_time'])){ $addedNum=$arele->export($all_field_list); $this->echo_msg('成功发布'.$addedNum.'条数据','green'); @@ -653,7 +653,7 @@ class Task extends BaseController { $mtask=model('Task'); $mcoll=model('Collector'); $mrele=model('Release'); - $caijiNum=intval($GLOBALS['config']['caiji']['num']); + $caijiNum=intval($GLOBALS['_sc']['c']['caiji']['num']); $caijiLimit=false; if($caijiNum>0){ $caijiLimit=true; @@ -690,7 +690,7 @@ class Task extends BaseController { $arele='\\skycaiji\\admin\\event\\R'.strtolower($releData['module']); $arele=new $arele(); $arele->init($releData); - $GLOBALS['real_time_release']=&$arele; + $GLOBALS['_sc']['real_time_release']=&$arele; $this->echo_msg('
正在执行任务:'.$taskData['name'].'
','black'); $all_field_list=array(); @@ -711,8 +711,8 @@ class Task extends BaseController { }elseif(is_array($field_list)&&!empty($field_list)){ $all_field_list=array_merge($all_field_list,$field_list); - $taskNum-=count($field_list); - $caijiNum-=count($field_list); + $taskNum-=count((array)$field_list); + $caijiNum-=count((array)$field_list); } } }else{ @@ -728,9 +728,9 @@ class Task extends BaseController { if(empty($all_field_list)){ $this->echo_msg('任务:'.$taskData['name'].' 没有采集到数据','orange'); }else{ - $this->echo_msg('任务:'.$taskData['name'].' 采集到'.count($all_field_list).'条数据','green'); + $this->echo_msg('任务:'.$taskData['name'].' 采集到'.count((array)$all_field_list).'条数据','green'); - if(empty($GLOBALS['config']['caiji']['real_time'])){ + if(empty($GLOBALS['_sc']['c']['caiji']['real_time'])){ $addedNum=$arele->export($all_field_list); $this->echo_msg('成功发布'.$addedNum.'条数据','green'); diff --git a/SkycaijiApp/admin/controller/Taskgroup.php b/SkycaijiApp/admin/controller/Taskgroup.php index 8152950..87075f5 100644 --- a/SkycaijiApp/admin/controller/Taskgroup.php +++ b/SkycaijiApp/admin/controller/Taskgroup.php @@ -74,8 +74,8 @@ class Taskgroup extends BaseController { $parentTgList=$mtaskgroup->where(array('parent_id'=>0))->order('sort desc')->column('name','id'); $this->assign('parentTgList',$parentTgList); - $GLOBALS['content_header']=lang('taskgroup_list'); - $GLOBALS['breadcrumb']=breadcrumb(array(array('url'=>url('Taskgroup/list'),'title'=>lang('taskgroup_list')))); + $GLOBALS['_sc']['p_name']=lang('taskgroup_list'); + $GLOBALS['_sc']['p_nav']=breadcrumb(array(array('url'=>url('Taskgroup/list'),'title'=>lang('taskgroup_list')))); return $this->fetch(); } /** @@ -102,8 +102,8 @@ class Taskgroup extends BaseController { $parentTgList=$mtaskgroup->where(array('parent_id'=>0))->order('sort desc')->column('name','id'); $this->assign('parentTgList',$parentTgList); - $GLOBALS['content_header']=lang('taskgroup_add'); - $GLOBALS['breadcrumb']=breadcrumb(array(array('url'=>url('Taskgroup/list'),'title'=>lang('taskgroup_list')),lang('taskgroup_add'))); + $GLOBALS['_sc']['p_name']=lang('taskgroup_add'); + $GLOBALS['_sc']['p_nav']=breadcrumb(array(array('url'=>url('Taskgroup/list'),'title'=>lang('taskgroup_list')),array('url'=>url('Taskgroup/add'),'title'=>lang('taskgroup_add')))); if(request()->isAjax()){ return view('add_ajax'); @@ -160,8 +160,8 @@ class Taskgroup extends BaseController { $this->assign('parentTgList',$parentTgList); $this->assign('tgData',$tgData); - $GLOBALS['content_header']=lang('taskgroup_edit'); - $GLOBALS['breadcrumb']=breadcrumb(array(array('url'=>url('Taskgroup/list'),'title'=>lang('taskgroup_list')),lang('taskgroup_edit'))); + $GLOBALS['_sc']['p_name']=lang('taskgroup_edit').':'.$tgData['name']; + $GLOBALS['_sc']['p_nav']=breadcrumb(array(array('url'=>url('Taskgroup/list'),'title'=>lang('taskgroup_list')),array('url'=>url('Taskgroup/edit?id='.$tgData['id']),'title'=>$tgData['name']))); if(request()->isAjax()){ return view('add_ajax'); diff --git a/SkycaijiApp/admin/controller/Tool.php b/SkycaijiApp/admin/controller/Tool.php index d6e526e..c61d3f7 100644 --- a/SkycaijiApp/admin/controller/Tool.php +++ b/SkycaijiApp/admin/controller/Tool.php @@ -14,8 +14,8 @@ use skycaiji\admin\model\DbCommon; class Tool extends BaseController { /*文件管理*/ public function fileManagerAction(){ - $GLOBALS['content_header']='文件管理'; - $GLOBALS['breadcrumb']=breadcrumb(array('文件管理')); + $GLOBALS['_sc']['p_name']='文件管理'; + $GLOBALS['_sc']['p_nav']=breadcrumb(array(array('url'=>url('Tool/fileManager'),'title'=>'文件管理'))); return $this->fetch('fileManager'); } /*elfinder文件管理器*/ @@ -86,8 +86,8 @@ class Tool extends BaseController { } } } - $GLOBALS['content_header']='错误日志'; - $GLOBALS['breadcrumb']=breadcrumb(array('错误日志')); + $GLOBALS['_sc']['p_name']='错误日志'; + $GLOBALS['_sc']['p_nav']=breadcrumb(array(array('url'=>url('Tool/logs'),'title'=>'错误日志'))); $this->assign('logList',$logList); return $this->fetch(); } @@ -156,8 +156,8 @@ class Tool extends BaseController { $this->error('',null,array('files'=>$error_files)); } }else{ - $GLOBALS['content_header']='校验文件'; - $GLOBALS['breadcrumb']=breadcrumb(array('校验文件')); + $GLOBALS['_sc']['p_name']='校验文件'; + $GLOBALS['_sc']['p_nav']=breadcrumb(array(array('url'=>url('Tool/checkfile'),'title'=>'校验文件'))); return $this->fetch(); } } @@ -207,7 +207,7 @@ class Tool extends BaseController { $this->error('没有获取到表'); } - if(!version_compare($check_db['version'],$GLOBALS['config']['version'],'=')){ + if(!version_compare($check_db['version'],$GLOBALS['_sc']['c']['version'],'=')){ $this->error('校验文件版本与数据库版本不一致'); } @@ -417,8 +417,8 @@ class Tool extends BaseController { } } }else{ - $GLOBALS['content_header']='校验数据库'; - $GLOBALS['breadcrumb']=breadcrumb(array('校验数据库')); + $GLOBALS['_sc']['p_name']='校验数据库'; + $GLOBALS['_sc']['p_nav']=breadcrumb(array(array('url'=>url('Tool/checkdb'),'title'=>'校验数据库'))); return $this->fetch(); } } @@ -433,18 +433,14 @@ class Tool extends BaseController { $html=get_html($url); } if(!empty($html)){ - - if(preg_match($eCpattern::$jsonpRegExp,$html,$json)){ - - $json=trim($json['json']).'}'; - } + $json=convert_html2json($html,true); } $this->success('','',array('json'=>$json)); }else{ - $GLOBALS['content_header']='JSON解析'; - $GLOBALS['breadcrumb']=breadcrumb(array('JSON解析')); - return $this->fetch(); + $GLOBALS['_sc']['p_name']='JSON解析'; + $GLOBALS['_sc']['p_nav']=breadcrumb(array(array('url'=>url('Tool/json_tree'),'title'=>'JSON解析'))); + return $this->fetch('json_tree'); } } } \ No newline at end of file diff --git a/SkycaijiApp/admin/controller/Upgrade.php b/SkycaijiApp/admin/controller/Upgrade.php index 79e7932..553e280 100644 --- a/SkycaijiApp/admin/controller/Upgrade.php +++ b/SkycaijiApp/admin/controller/Upgrade.php @@ -17,21 +17,29 @@ class Upgrade extends BaseController{ public function __construct(){ parent::__construct(); set_time_limit(3600); - $this->oldFilePath=config('root_path').'/data/program/backup/skycaiji'.$GLOBALS['config']['version']; - $this->newFilePath=config('root_path').'/data/program/upgrade/skycaiji'.$GLOBALS['config']['version']; + $this->oldFilePath=config('root_path').'/data/program/backup/skycaiji'.$GLOBALS['_sc']['c']['version']; + $this->newFilePath=config('root_path').'/data/program/upgrade/skycaiji'.$GLOBALS['_sc']['c']['version']; } /*检测更新*/ public function newVersionAction(){ $version=get_html('https://www.skycaiji.com/upgrade/program/version?v='.SKYCAIJI_VERSION,null,null,'utf-8'); $version=json_decode($version,true); + $version=is_array($version)?$version:array(); $new_version=trim($version['new_version']); - $cur_version=$GLOBALS['config']['version']; + $cur_version=$GLOBALS['_sc']['c']['version']; - if(version_compare($new_version,$cur_version)>=1){ - $this->success('',null,$version); - }else{ - $this->error(); + + if(version_compare($new_version,$cur_version,'>')){ + + $version['is_new_version']=true; } + + $cacheIx=cache('backstage_admin_index'); + if(empty($cacheIx)||$cacheIx['ver']!=$version['admin_index_ver']){ + $version['is_new_admin_index']=true; + } + + $this->success('',null,$version); } /* * 下载的文件完整性检测 @@ -89,10 +97,11 @@ class Upgrade extends BaseController{ } } $fileUrl='https://www.skycaiji.com/upgrade/program/getFile?filename='.rawurlencode(base64_encode($fileName)); - $result=\Requests::get($fileUrl,array(),array('timeout'=>100,'verify'=>false)); - if(200==$result->status_code){ + + $curl=\util\Curl::get($fileUrl,null,array('timeout'=>100)); + if($curl->isOk){ - $newFile=$result->body; + $newFile=$curl->body; $oldFile=file_get_contents(config('root_path').$fileName); if(!empty($oldFile)){ diff --git a/SkycaijiApp/admin/controller/User.php b/SkycaijiApp/admin/controller/User.php index 2ac353b..814681e 100644 --- a/SkycaijiApp/admin/controller/User.php +++ b/SkycaijiApp/admin/controller/User.php @@ -24,8 +24,8 @@ class User extends BaseController { $this->assign('pagenav',$pagenav); $userList=$userList->all(); - $GLOBALS['content_header']=lang('user_list'); - $GLOBALS['breadcrumb']=breadcrumb(array(array('url'=>url('User/list'),'title'=>lang('user_list')))); + $GLOBALS['_sc']['p_name']=lang('user_list'); + $GLOBALS['_sc']['p_nav']=breadcrumb(array(array('url'=>url('User/list'),'title'=>lang('user_list')))); $groupList=model('Usergroup')->column('*','id'); $this->assign('userList',$userList); @@ -38,10 +38,10 @@ class User extends BaseController { $musergroup=model('Usergroup'); if(request()->isPost()){ if(!check_usertoken()){ - $this->error(lang('usertoken_error')); + $this->error(lang('usertoken_error'),'Admin/User/add'); } - if($GLOBALS['config']['site']['verifycode']){ + if($GLOBALS['_sc']['c']['site']['verifycode']){ $verifycode=trim(input('verifycode')); $check=check_verify($verifycode); @@ -65,7 +65,7 @@ class User extends BaseController { $newData['password']=\skycaiji\admin\model\User::pwd_encrypt($newData['password'],$newData['salt']); $newGroup=$musergroup->getById($newData['groupid']); if($musergroup->user_level_limit($newGroup['level'])){ - $this->error('您不能添加“'.$GLOBALS['user']['group']['name'].'”用户组'); + $this->error('您不能添加“'.$GLOBALS['_sc']['user']['group']['name'].'”用户组'); } $newData['regtime']=NOW_TIME; $muser->isUpdate(false)->allowField(true)->save($newData); @@ -75,9 +75,9 @@ class User extends BaseController { $this->error(lang('op_failed')); } }else{ - $subGroupList=$musergroup->get_sub_level($GLOBALS['user']['groupid']); - $GLOBALS['content_header']=lang('user_add'); - $GLOBALS['breadcrumb']=breadcrumb(array(array('url'=>url('User/list'),'title'=>lang('user_list')),lang('user_add'))); + $subGroupList=$musergroup->get_sub_level($GLOBALS['_sc']['user']['groupid']); + $GLOBALS['_sc']['p_name']=lang('user_add'); + $GLOBALS['_sc']['p_nav']=breadcrumb(array(array('url'=>url('User/list'),'title'=>lang('user_list')),array('url'=>url('User/add'),'title'=>lang('user_add')))); $this->assign('subGroupList',$subGroupList); return $this->fetch(); } @@ -95,16 +95,16 @@ class User extends BaseController { } $userData['group']=$musergroup->getById($userData['groupid']); - $isOwner=($GLOBALS['user']['uid']==$userData['uid'])?true:false; + $isOwner=($GLOBALS['_sc']['user']['uid']==$userData['uid'])?true:false; if(!$isOwner&&$musergroup->user_level_limit($userData['group']['level'])){ $this->error('您不能编辑“'.$userData['group']['name'].'”组的用户'); } if(request()->isPost()){ if(!check_usertoken()){ - $this->error(lang('usertoken_error')); + $this->error(lang('usertoken_error'),'Admin/User/edit?uid='.$userData['uid']); } - if($GLOBALS['config']['site']['verifycode']){ + if($GLOBALS['_sc']['c']['site']['verifycode']){ $verifycode=trim(input('verifycode')); $check=check_verify($verifycode); @@ -135,7 +135,7 @@ class User extends BaseController { } $newGroup=$musergroup->getById($newData['groupid']); if($musergroup->user_level_limit($newGroup['level'])){ - $this->error('您不能改为“'.$GLOBALS['user']['group']['name'].'”用户组'); + $this->error('您不能改为“'.$GLOBALS['_sc']['user']['group']['name'].'”用户组'); } if($isOwner||empty($newData['groupid'])){ @@ -147,11 +147,11 @@ class User extends BaseController { }else{ $this->assign('userData',$userData); - $subGroupList=$musergroup->get_sub_level($GLOBALS['user']['groupid']); + $subGroupList=$musergroup->get_sub_level($GLOBALS['_sc']['user']['groupid']); $this->assign('subGroupList',$subGroupList); $this->assign('isOwner',$isOwner); - $GLOBALS['content_header']=lang('user_edit'); - $GLOBALS['breadcrumb']=breadcrumb(array(array('url'=>url('User/list'),'title'=>lang('user_list')),lang('user_edit'))); + $GLOBALS['_sc']['p_name']=lang('user_edit').':'.$userData['username']; + $GLOBALS['_sc']['p_nav']=breadcrumb(array(array('url'=>url('User/list'),'title'=>lang('user_list')),array('url'=>url('User/edit?uid='.$userData['uid']),'title'=>$userData['username']))); return $this->fetch(); } } @@ -166,7 +166,7 @@ class User extends BaseController { if(empty($userData)){ $this->error(lang('user_error_empty_user')); } - if($userData['uid']==$GLOBALS['user']['uid']){ + if($userData['uid']==$GLOBALS['_sc']['user']['uid']){ $this->error('不能删除自己'); } diff --git a/SkycaijiApp/admin/event/Collector.php b/SkycaijiApp/admin/event/Collector.php index 5987bcd..b3b0647 100644 --- a/SkycaijiApp/admin/event/Collector.php +++ b/SkycaijiApp/admin/event/Collector.php @@ -17,7 +17,7 @@ abstract class Collector extends \skycaiji\admin\controller\BaseController { public function error($msg = '', $url = null, $data = array(), $wait = 3, array $header = []){ if(is_collecting()){ - parent::echo_msg($msg,'red'); + $this->echo_msg($msg,'red'); return null; }else{ parent::error($msg,$url,$data,$wait,$header); @@ -26,6 +26,17 @@ abstract class Collector extends \skycaiji\admin\controller\BaseController { /*采集器的输出内容需要重写,只有正在采集时才输出内容*/ public function echo_msg($str,$color='red',$echo=true,$end_str=''){ if(is_collecting()){ + static $pause_session=null; + if(!isset($pause_session)){ + + if(session_status()!==2){ + session_start(); + } + \think\Session::pause(); + + $pause_session=true; + } + parent::echo_msg($str,$color,$echo,$end_str); } } @@ -56,9 +67,9 @@ abstract class Collector extends \skycaiji\admin\controller\BaseController { public function set_html_interval(){ if(is_collecting()){ - if($GLOBALS['config']['caiji']['html_interval']>0){ + if($GLOBALS['_sc']['c']['caiji']['html_interval']>0){ - sleep($GLOBALS['config']['caiji']['html_interval']); + sleep($GLOBALS['_sc']['c']['caiji']['html_interval']); return true; diff --git a/SkycaijiApp/admin/event/Cpattern.php b/SkycaijiApp/admin/event/Cpattern.php index 6902486..ce49a1a 100644 --- a/SkycaijiApp/admin/event/Cpattern.php +++ b/SkycaijiApp/admin/event/Cpattern.php @@ -17,20 +17,25 @@ class Cpattern extends CpatternBase{ if(!defined('IS_COLLECTING')){ define('IS_COLLECTING', 1); } - @session_start(); - \think\Session::pause(); if(!$this->show_opened_tools){ $opened_tools=array(); + + if($GLOBALS['_sc']['c']['caiji']['robots']){ + $opened_tools[]='遵守robots协议'; + } if($this->config['page_render']){ $opened_tools[]='页面渲染'; } - if($GLOBALS['config']['caiji']['download_img']){ + if($GLOBALS['_sc']['c']['download_img']['download_img']){ $opened_tools[]='图片本地化'; } - if($GLOBALS['config']['proxy']['open']){ + if($GLOBALS['_sc']['c']['proxy']['open']){ $opened_tools[]='代理'; } + if($GLOBALS['_sc']['c']['translate']['open']){ + $opened_tools[]='翻译'; + } if(!empty($opened_tools)){ $this->echo_msg('开启功能:'.implode('、', $opened_tools),'black'); } @@ -80,7 +85,7 @@ class Cpattern extends CpatternBase{ } } - $source_interval=$GLOBALS['config']['caiji']['interval']*60; + $source_interval=$GLOBALS['_sc']['c']['caiji']['interval']*60; $time_interval_list=array(); $source_urls=array(); @@ -311,6 +316,37 @@ class Cpattern extends CpatternBase{ }else{ $config=$cacheConfig['config']; } + + if($config['request_headers']['open']){ + + $taskHeaders=array(); + if(!empty($config['request_headers']['useragent'])){ + $taskHeaders['useragent']=$config['request_headers']['useragent']; + } + if(!empty($config['request_headers']['cookie'])){ + $taskHeaders['cookie']=$config['request_headers']['cookie']; + } + if(!empty($config['request_headers']['referer'])){ + $taskHeaders['referer']=$config['request_headers']['referer']; + } + + if(!empty($config['request_headers']['custom_names'])){ + foreach ($config['request_headers']['custom_names'] as $k=>$v){ + if(!empty($v)){ + $taskHeaders[$v]=$config['request_headers']['custom_vals'][$k]; + } + } + } + + $GLOBALS['_sc']['task_request_headers']=array( + 'request_headers'=>$config['request_headers'], + 'headers'=>$taskHeaders + ); + }else{ + + $GLOBALS['_sc']['task_request_headers']=null; + } + $this->config=$config; } @@ -976,21 +1012,15 @@ class Cpattern extends CpatternBase{ } }elseif(in_array($module,$fieldArr1)){ - if($module=='words'){ + if(empty($this->first_loop_field)){ $val=$this->$field_func($field_params); }else{ - if(empty($this->first_loop_field)){ - - $val=$this->$field_func($field_params); - }else{ - - $val=array(); - - foreach ($this->field_val_list[$this->first_loop_field]['values'][$cur_url_md5] as $v_k=>$v_v){ - $val[$v_k]=$this->$field_func($field_params); - } + $val=array(); + + foreach ($this->field_val_list[$this->first_loop_field]['values'][$cur_url_md5] as $v_k=>$v_v){ + $val[$v_k]=$this->$field_func($field_params); } } }elseif(in_array($module,$fieldArr2)){ @@ -1090,7 +1120,7 @@ class Cpattern extends CpatternBase{ $this->field_val_list[$field_name]['values'][$cur_url_md5]=$val; } - if(!empty($GLOBALS['config']['caiji']['download_img'])&&!empty($val)){ + if(!empty($GLOBALS['_sc']['c']['download_img']['download_img'])&&!empty($val)){ $valImgs=array(); if(preg_match_all('/]*\bsrc=[\'\"]*(\w+\:\/\/[^\'\"\s]+)[\'\"]*/i',$val,$imgUrls)){ @@ -1141,7 +1171,7 @@ class Cpattern extends CpatternBase{ if(!preg_match('/^\w+\:\/\//',$page_url)){ return $this->error($page_url.'网址不完整'); } - if(empty($this->config['paging']['max'])||(count($this->used_paging_urls[$contMd5])<$this->config['paging']['max'])){ + if(empty($this->config['paging']['max'])||(count((array)$this->used_paging_urls[$contMd5])<$this->config['paging']['max'])){ $this->set_html_interval(); $this->echo_msg("——采集分页:{$page_url}",'black'); @@ -1323,9 +1353,9 @@ class Cpattern extends CpatternBase{ if(empty($this->first_loop_field)){ foreach ($this->field_val_list as $fieldName=>$fieldVal){ - $val_values=array_filter($fieldVal['values']); + $val_values=array_filter_keep0($fieldVal['values']); $val_values=implode($this->config['new_paging_fields'][$fieldName]['delimiter'], $val_values); - + $val_imgs=array(); if(!empty($fieldVal['imgs'])){ foreach ($fieldVal['imgs'] as $v){ @@ -1518,7 +1548,7 @@ class Cpattern extends CpatternBase{ /*执行采集返回未使用的网址*/ public function _collect_unused_cont_urls($cont_urls=array(),$echo_str=''){ $mcollected=model('Collected'); - $count_conts=count($cont_urls); + $count_conts=count((array)$cont_urls); if($this->config['url_repeat']){ $db_cont_urls=array(); @@ -1567,7 +1597,7 @@ class Cpattern extends CpatternBase{ $this->echo_msg('','',true,'
'); $level_data=$this->collLevelUrls($source_url,$level); - $this->echo_msg($level_str.'抓取到'.$level.'级“'.$this->config['level_urls'][$level-1]['name'].'”网址'.count($level_data['urls']).'条','black'); + $this->echo_msg($level_str.'抓取到'.$level.'级“'.$this->config['level_urls'][$level-1]['name'].'”网址'.count((array)$level_data['urls']).'条','black'); $mcollected=model('Collected'); $mcacheLevel=CacheModel::getInstance('level_url'); @@ -1579,7 +1609,7 @@ class Cpattern extends CpatternBase{ $level_urls["level_{$level}:{$level_url}"]=$level_url; } - $level_interval=$GLOBALS['config']['caiji']['interval']*60; + $level_interval=$GLOBALS['_sc']['c']['caiji']['interval']*60; $time_interval_list=array(); $cacheLevels=$mcacheLevel->db()->where(array('cname'=>array('in',array_map('md5', array_keys($level_urls)))))->column('dateline','cname'); @@ -1651,7 +1681,7 @@ class Cpattern extends CpatternBase{ if(count($this->collected_field_list)>=$this->collect_num){ - if($cur_level_idb()->field('dateline')->where('cname',$this->collector['task_id'])->find(); - if(empty($backstageDate)||$GLOBALS['backstage_task_runtime']<$backstageDate['dateline']){ + if(empty($backstageDate)||$GLOBALS['_sc']['backstage_task_runtime']<$backstageDate['dateline']){ - unset($GLOBALS['backstage_task_ids'][$this->collector['task_id']]); + unset($GLOBALS['_sc']['backstage_task_ids'][$this->collector['task_id']]); exit('终止进程'); } } @@ -1750,7 +1780,7 @@ class Cpattern extends CpatternBase{ $is_loop=empty($this->first_loop_field)?false:true; if(!empty($field_vals_list)){ $is_real_time=false; - if(!empty($GLOBALS['config']['caiji']['real_time'])&&!empty($GLOBALS['real_time_release'])){ + if(!empty($GLOBALS['_sc']['c']['caiji']['real_time'])&&!empty($GLOBALS['_sc']['real_time_release'])){ $is_real_time=true; } @@ -1775,7 +1805,7 @@ class Cpattern extends CpatternBase{ unset($field_vals_list[$k]); } } - $this->echo_msg($echo_str.'已过滤'.count($loop_exists_urls).'条重复数据','black'); + $this->echo_msg($echo_str.'已过滤'.count((array)$loop_exists_urls).'条重复数据','black'); } } if(isset($this->exclude_cont_urls[$md5_cont_url])){ @@ -1783,7 +1813,7 @@ class Cpattern extends CpatternBase{ $excludeNum=0; foreach($this->exclude_cont_urls[$md5_cont_url] as $k=>$v){ - $excludeNum+=count($v); + $excludeNum+=count((array)$v); } $this->echo_msg($echo_str.'通过数据处理排除了'.$excludeNum.'条数据','black'); @@ -1823,7 +1853,7 @@ class Cpattern extends CpatternBase{ if($is_real_time){ - $GLOBALS['real_time_release']->export(array($collected_data)); + $GLOBALS['_sc']['real_time_release']->export(array($collected_data)); unset($collected_data['fields']); unset($collected_data['title']); @@ -1889,7 +1919,7 @@ class Cpattern extends CpatternBase{ $this->used_source_urls[$cont_key]=1; } } - + if($this->collect_num>0&&count($this->collected_field_list)>=$this->collect_num){ break; } diff --git a/SkycaijiApp/admin/event/CpatternBase.php b/SkycaijiApp/admin/event/CpatternBase.php index d820433..a3c5847 100644 --- a/SkycaijiApp/admin/event/CpatternBase.php +++ b/SkycaijiApp/admin/event/CpatternBase.php @@ -108,6 +108,8 @@ class CpatternBase extends Collector{ $cont_urls[$k]=$v['match']; } } + }else{ + $cont_urls=array(); } }else{ @@ -127,6 +129,8 @@ class CpatternBase extends Collector{ ); } } + }else{ + $cont_urls=array(); } } } @@ -462,23 +466,18 @@ class CpatternBase extends Collector{ } return $val; } + /** * json提取,方法可调用,$field_params传入规则参数 * @param array $field_params * @param string $html * @return string */ - public static $jsonpRegExp='/^(\s*[\$\w\-]+\s*[\{\(])+(?P[\s\S]+)\}\s*\)\s*[\;]{0,1}/i'; public function field_module_json($field_params,$html,$cur_url=''){ static $jsonList=array(); $jsonKey=!empty($cur_url)?md5($cur_url):md5($html); if(!isset($jsonList[$jsonKey])){ - $jsonList[$jsonKey]=json_decode($html,true); - if(empty($jsonList[$jsonKey])&&preg_match(self::$jsonpRegExp, $html,$json)){ - - $json=trim($json['json']).'}'; - $jsonList[$jsonKey]=json_decode($json,true); - } + $jsonList[$jsonKey]=convert_html2json($html); } $jsonArrType=$field_params['json_arr']; if($field_params['json_loop']){ @@ -508,12 +507,7 @@ class CpatternBase extends Collector{ $jsonArr=&$jsonArrOrStr; }else{ - $jsonArr=json_decode($jsonArrOrStr,true); - if(empty($jsonArr)&&preg_match(self::$jsonpRegExp,$jsonArrOrStr,$jsonArrOrStr)){ - - $jsonArr=trim($jsonArrOrStr['json']).'}'; - $jsonArr=json_decode($jsonArr,true); - } + $jsonArr=convert_html2json($jsonArrOrStr); unset($jsonArrOrStr); } $val=''; @@ -652,7 +646,7 @@ class CpatternBase extends Collector{ return $fieldVal; } public function process_f_replace($fieldVal,$params){ - return preg_replace('/'.$params['replace_from'].'/i',$params['replace_to'], $fieldVal); + return preg_replace('/'.$params['replace_from'].'/ui',$params['replace_to'], $fieldVal); } public function process_f_tool($fieldVal,$params){ @@ -667,16 +661,16 @@ class CpatternBase extends Collector{ } if(in_array('is_img', $params['tool_list'])){ - if(!empty($GLOBALS['config']['caiji']['download_img'])){ + if(!empty($GLOBALS['_sc']['c']['download_img']['download_img'])){ - $fieldVal=preg_replace('/(\bhttp[s]{0,1}\:\/\/[^\s]+)/i','{[img]}'."$1".'{[/img]}',$fieldVal); + $fieldVal=preg_replace('/(?]+)(?![\'\"])/i','{[img]}'."$1".'{[/img]}',$fieldVal); } } return $fieldVal; } public function process_f_translate($fieldVal,$params){ - if(!empty($GLOBALS['config']['translate'])&&!empty($GLOBALS['config']['translate']['open'])){ + if(!empty($GLOBALS['_sc']['c']['translate'])&&!empty($GLOBALS['_sc']['c']['translate']['open'])){ $fieldVal=\util\Translator::translate($fieldVal, $params['translate_from'], $params['translate_to']); } @@ -698,8 +692,8 @@ class CpatternBase extends Collector{ $batch_re=$batch_list[$listMd5][0]; $batch_to=$batch_list[$listMd5][1]; } - $batch_re=is_array($batch_re)?$batch_re:null; - $batch_to=is_array($batch_to)?$batch_to:null; + $batch_re=is_array($batch_re)?$batch_re:array(); + $batch_to=is_array($batch_to)?$batch_to:array(); if(!empty($batch_re)&&count($batch_re)==count($batch_to)){ $fieldVal=str_replace($batch_re, $batch_to, $fieldVal); @@ -717,45 +711,31 @@ class CpatternBase extends Collector{ } return $fieldVal; } - public function process_f_func($fieldVal,$params){ + public function process_f_func($fieldVal,$params,$curUrlMd5,$loopIndex,$contUrlMd5){ - if(!empty($params['func_name'])){ - if(!function_exists($params['func_name'])){ + $field_val_list=null; + if(preg_match('/\[\x{5b57}\x{6bb5}\:(.+?)\]/u',$params['func_param'])){ + + if(empty($this->first_loop_field)){ - $this->error('数据处理》无效的函数:'.$params['func_name']); + $field_val_list=array(); + foreach ($this->field_val_list as $k=>$v){ + $field_val_list[$k]=$v['values'][$curUrlMd5]; + } }else{ - if(array_key_exists($params['func_name'], config('allow_process_func'))||array_key_exists($params['func_name'], config('EXTEND_PROCESS_FUNC'))){ - - static $func_param_list=array(); - $funcParam=null; - if(empty($params['func_param'])){ - - $funcParam=array($fieldVal); - }else{ - $fparamMd5=md5($params['func_param']); - if(!isset($func_param_list[$fparamMd5])){ - if(preg_match_all('/[^\r\n]+/', $params['func_param'],$mfuncParam)){ - $func_param_list[$fparamMd5]=$mfuncParam[0]; - } - } - $funcParam=$func_param_list[$fparamMd5]; - foreach ($funcParam as $k=>$v){ - $funcParam[$k]=str_replace('###', $fieldVal, $v); - } - } - if(!empty($funcParam)&&is_array($funcParam)){ - try { - $fieldVal=call_user_func_array($params['func_name'], $funcParam); - }catch (\Exception $ex){ - - } - } - }else{ - $this->error('数据处理》未配置函数:'.$params['func_name']); + $field_val_list=array(); + + foreach ($this->field_val_list as $k=>$v){ + $field_val_list[$k]=is_array($v['values'][$curUrlMd5])?$v['values'][$curUrlMd5][$loopIndex]:$v['values'][$curUrlMd5]; } } } + + $result=$this->execute_plugin_func('process', $params['func_name'], $fieldVal, $params['func_param'], $field_val_list); + if(isset($result)){ + $fieldVal=$result; + } return $fieldVal; } public function process_f_filter($fieldVal,$params,$curUrlMd5,$loopIndex,$contUrlMd5){ @@ -874,59 +854,16 @@ class CpatternBase extends Collector{ } break; case 'func': - if(!empty($ifVal)){ - - $funcMd5=md5($ifVal); - if(!isset($func_list[$funcMd5])){ - if(preg_match_all('/[^\r\n]+/',$ifVal,$funcParam)){ - - $funcParam=$funcParam[0]; - }else{ - - $funcParam=array($ifVal); - } - $func_list[$funcMd5]=$funcParam; - }else{ - $funcParam=$func_list[$funcMd5]; - } - $funcName=$funcParam[0]; - $isTurn=false; - if(strpos($funcName,'!')===0){ - - $funcName=substr($funcName, 1); - $isTurn=true; - } - unset($funcParam[0]); - if(empty($funcParam)){ - - $funcParam=array($fieldVal); - }else{ - foreach($funcParam as $k=>$v){ - $funcParam[$k]=str_replace('###', $fieldVal, $v); - } - } - - if(!function_exists($funcName)){ - - $this->error('数据处理》条件判断》无效的函数:'.$funcName); - }else{ - if(array_key_exists($funcName, config('allow_process_if'))||array_key_exists($funcName, config('EXTEND_PROCESS_IF'))){ - - try { - $result=call_user_func_array($funcName, $funcParam); - if($isTurn){ - - $result=$result?false:true; - } - }catch (\Exception $ex){ - - $this->error('数据处理》条件判断》函数'.$funcName.'运行错误,'.$ex->getMessage()); - } - }else{ - $this->error('数据处理》条件判断》未配置函数:'.$funcName); - } - } + $funcName=$params['if_addon']['func'][$ifk]; + $isTurn=$params['if_addon']['turn'][$ifk]; + $isTurn=$isTurn?true:false; + + $result=$this->execute_plugin_func('processIf', $funcName, $fieldVal, $ifVal); + $result=$result?true:false; + if($isTurn){ + $result=$result?false:true; } + break; case 'has':$result=stripos($fieldVal,$ifVal)!==false?true:false;break; case 'nhas':$result=stripos($fieldVal,$ifVal)===false?true:false;break; @@ -1023,12 +960,66 @@ class CpatternBase extends Collector{ } return $fieldVal; } + /*调用接口*/ + public function process_f_api($fieldVal,$params){ + $url=$params['api_url']; + $result=null; + if(!empty($url)){ + $isLoc=false; + if(!preg_match('/^\w+\:\/\//', $url)&&strpos($url, '/')===0){ + + $isLoc=true; + $url=config('root_website').$url; + } + if(preg_match('/^\w+\:\/\//', $url)){ + + $postData=array(); + if(is_array($params['api_params'])){ + foreach ($params['api_params']['name'] as $k=>$v){ + if(empty($v)){ + continue; + } + $val=$params['api_params']['val'][$k]; + $addon=$params['api_params']['addon'][$k]; + switch ($val){ + case 'field':$val=$fieldVal;break; + case 'timestamp':$val=time();break; + case 'time':$addon=$addon?$addon:'Y-m-d H:i:s';$val=date($addon,time());break; + case 'custom':$val=$addon;break; + } + $postData[$v]=$val; + } + } + if($params['api_type']=='post'){ + + $postData=empty($postData)?true:$postData; + }else{ + + if($postData){ + $url.=(strpos($url,'?')===false?'?':'&').http_build_query($postData); + } + $postData=null; + } + if($isLoc){ + + $result=get_html($url,null,array(),'utf-8',$postData); + }else{ + + $result=$this->get_html($url,false,$postData); + } + if(isset($result)){ + $fieldVal=$this->rule_module_json_data(array('json'=>$params['api_json'],'json_arr'=>$params['api_json_arr'],'json_arr_implode'=>$params['api_json_implode']),$result); + } + } + } + return $fieldVal; + } /*数据处理*/ public function process_field($fieldVal,$process,$curUrlMd5,$loopIndex,$contUrlMd5){ if(empty($process)){ return $fieldVal; } - static $funcs=array('filter','if'); + static $funcs=array('func','filter','if'); foreach ($process as $params){ if(empty($this->first_loop_field)){ @@ -1164,6 +1155,8 @@ class CpatternBase extends Collector{ $urls=array_unique($urls[0]); $urls=array_values($urls); + }else{ + $urls=array(); } return $urls; }else{ @@ -1269,15 +1262,159 @@ class CpatternBase extends Collector{ } return $tags; } - /*获取源码*/ - public function get_html($url,$open_cache=false,$is_post=false){ - if($open_cache&&!empty($this->html_cache_list[$url])){ - - return $this->html_cache_list[$url]; + /** + * 执行数据处理》使用函数 + * @param string $module 模块 + * @param string $funcName 函数/方法 + * @param string $fieldVal 字段值 + * @param string $paramsStr 输入的参数(有换行符) + * @param array $fieldValList 所有字段值(调用字段时使用) + */ + public function execute_plugin_func($module,$funcName,$fieldVal,$paramsStr,$fieldValList=null){ + + static $func_class_list=array('process'=>array(),'processIf'=>array()); + $class_list=&$func_class_list[$module]; + + $options = array ( + 'process' => array ( + 'name' => '使用函数', + 'config'=>'allow_process_func', + 'extend'=>'EXTEND_PROCESS_FUNC', + ), + 'processIf' => array ( + 'name' => '条件判断》使用函数', + 'config'=>'allow_process_if', + 'extend'=>'EXTEND_PROCESS_IF', + ) + ); + $options=$options[$module]; + $result=null; + if(!empty($funcName)){ + $success=false; + if(strpos($funcName, ':')!==false){ + $funcName=explode(':', $funcName); + } + if(!is_array($funcName)){ + + if(!function_exists($funcName)){ + + $this->error('数据处理》'.$options['name'].'》无效的函数:'.$funcName); + }elseif(!array_key_exists($funcName, config($options['config']))&&!array_key_exists($funcName, config($options['extend']))){ + + $this->error('数据处理》'.$options['name'].'》未配置函数:'.$funcName); + }else{ + $success=true; + } + }else{ + + $className=$funcName[0]; + $methodName=$funcName[1]; + if(!isset($class_list[$className])){ + + $enable=model('FuncApp')->field('enable')->where(array('app'=>$className,'module'=>$module))->value('enable'); + if($enable){ + + $class=model('FuncApp')->app_classname($module,$className); + if(!class_exists($class)){ + + $class_list[$className]=1; + }else{ + $class=new $class(); + $class_list[$className]=$class; + } + }else{ + $class_list[$className]=2; + } + } + if(is_object($class_list[$className])){ + + if(!method_exists($class_list[$className], $methodName)){ + $this->error('数据处理》'.$options['name'].'》不存在方法:'.$className.'->'.$methodName); + }else{ + $success=true; + } + }else{ + $msg='数据处理》'.$options['name'].'》'; + if($class_list[$className]==1){ + $msg.='不存在插件:'; + }elseif($class_list[$className]==2){ + $msg.='已禁用插件:'; + }else{ + $msg.='无效的插件:'; + } + $this->error($msg.$className); + } + } + + if($success){ + static $func_param_list=array(); + $funcParam=null; + if(empty($paramsStr)){ + + $funcParam=array($fieldVal); + }else{ + $fparamMd5=md5($paramsStr); + if(!isset($func_param_list[$fparamMd5])){ + if(preg_match_all('/[^\r\n]+/',$paramsStr,$mfuncParam)){ + $func_param_list[$fparamMd5]=$mfuncParam[0]; + } + } + $funcParam=$func_param_list[$fparamMd5]; + if($funcParam){ + foreach ($funcParam as $k=>$v){ + $v=str_replace('###', $fieldVal, $v); + + if($fieldValList&&preg_match_all('/\[\x{5b57}\x{6bb5}\:(.+?)\]/u',$v,$match_fields)){ + for($i=0;$iabide_by_robots($url)){ + $this->error('robots拒绝访问的网址:'.$url); + return null; + } + } + + $urlMd5=md5($url); + if($openCache&&!empty($this->html_cache_list[$urlMd5])){ + + return $this->html_cache_list[$urlMd5]; + } + $is_post=$postData?true:false; + $pageRenderTool=null; if($this->config['page_render']){ - $pageRenderTool=$GLOBALS['config']['page_render']['tool']; + $pageRenderTool=$GLOBALS['_sc']['c']['page_render']['tool']; if(empty($pageRenderTool)){ $this->error('页面渲染未设置,请检查渲染设置','Setting/page_render'); @@ -1288,33 +1425,23 @@ class CpatternBase extends Collector{ $html=null; $headers=array(); $options=array(); - if($this->config['request_headers']['open']){ + + if(!empty($GLOBALS['_sc']['task_request_headers'])){ - if(!empty($this->config['request_headers']['useragent'])){ + $headers=$GLOBALS['_sc']['task_request_headers']['headers']; + if(!empty($headers['useragent'])){ - $options['useragent']=$this->config['request_headers']['useragent']; - } - if(!empty($this->config['request_headers']['cookie'])){ - $headers['cookie']=$this->config['request_headers']['cookie']; - } - if(!empty($this->config['request_headers']['referer'])){ - $headers['referer']=$this->config['request_headers']['referer']; - } - - if(!empty($this->config['request_headers']['custom_names'])){ - foreach ($this->config['request_headers']['custom_names'] as $k=>$v){ - if(!empty($v)){ - $headers[$v]=$this->config['request_headers']['custom_vals'][$k]; - } - } + $options['useragent']=$headers['useragent']; } + unset($headers['useragent']); } + $mproxy=model('Proxyip'); - $proxy_ip=null; - if(!empty($GLOBALS['config']['proxy']['open'])){ + $proxyDbIp=null; + if(!empty($GLOBALS['_sc']['c']['proxy']['open'])){ - $proxy_ip=$mproxy->get_usable_ip(); - $proxyIp=$mproxy->to_proxy_ip($proxy_ip); + $proxyDbIp=$mproxy->get_usable_ip(); + $proxyIp=$mproxy->to_proxy_ip($proxyDbIp); if(!empty($proxyIp)){ @@ -1331,8 +1458,30 @@ class CpatternBase extends Collector{ }else{ $urlPost=''; } + if(is_string($postData)){ + + if($urlPost){ + $urlPost.=$postData?('&'.$postData):''; + }else{ + $urlPost=$postData; + } + }elseif(is_array($postData)){ + + if($urlPost){ + if(preg_match_all('/([^\&]+?)\=([^\&]*)/',$urlPost,$mUrlPost)){ + $urlPostData=array(); + foreach($mUrlPost[1] as $k=>$v){ + $urlPostData[$v]=rawurldecode($mUrlPost[2][$k]); + } + $urlPost=$urlPostData; + unset($urlPostData); + } + } + $urlPost=is_array($urlPost)?$urlPost:array(); + $urlPost=array_merge($urlPost,$postData); + unset($postData); + } } - if($pageRenderTool){ if(!empty($options['useragent'])){ @@ -1340,15 +1489,11 @@ class CpatternBase extends Collector{ $headers['user-agent']=$options['useragent']; unset($options['useragent']); } - if(!empty($options['proxy'])){ - - $options['proxy']=$proxy_ip; - } if($pageRenderTool=='chrome'){ - $chromeConfig=$GLOBALS['config']['page_render']['chrome']; + $chromeConfig=$GLOBALS['_sc']['c']['page_render']['chrome']; try { - $chromeSocket=new \util\ChromeSocket($chromeConfig['host'],$chromeConfig['port'],$GLOBALS['config']['page_render']['timeout'],$chromeConfig['filename']); + $chromeSocket=new \util\ChromeSocket($chromeConfig['host'],$chromeConfig['port'],$GLOBALS['_sc']['c']['page_render']['timeout'],$chromeConfig['filename']); $chromeSocket->newTab(); $chromeSocket->websocket(null); if($is_post){ @@ -1367,16 +1512,17 @@ class CpatternBase extends Collector{ } }else{ if($is_post){ - $html=get_html($url,$headers,$options,$this->config['charset'],$urlPost); + $urlPost=$urlPost?$urlPost:''; }else{ - $html=get_html($url,$headers,$options,$this->config['charset']); + $urlPost=null; } + $html=get_html($url,$headers,$options,$this->config['charset'],$urlPost); } if($html==null){ - if(!empty($proxy_ip)){ - $mproxy->set_ip_failed($proxy_ip); + if(!empty($proxyDbIp)){ + $mproxy->set_ip_failed($proxyDbIp); } return null; } @@ -1393,8 +1539,8 @@ class CpatternBase extends Collector{ return \skycaiji\admin\event\Cpattern::create_complete_url($matche[1], $base_url, $domain_url); },$html); } - if($open_cache){ - $this->html_cache_list[$url]=$html; + if($openCache){ + $this->html_cache_list[$urlMd5]=$html; } return $html; } diff --git a/SkycaijiApp/admin/event/Rapi.php b/SkycaijiApp/admin/event/Rapi.php index cce2318..1a669c0 100644 --- a/SkycaijiApp/admin/event/Rapi.php +++ b/SkycaijiApp/admin/event/Rapi.php @@ -9,7 +9,6 @@ |-------------------------------------------------------------------------- */ -/*发布设置:本地cms*/ namespace skycaiji\admin\event; use skycaiji\admin\model\CacheModel; class Rapi extends Release{ diff --git a/SkycaijiApp/admin/event/Rcms.php b/SkycaijiApp/admin/event/Rcms.php index 0847681..6da4aba 100644 --- a/SkycaijiApp/admin/event/Rcms.php +++ b/SkycaijiApp/admin/event/Rcms.php @@ -11,6 +11,7 @@ /*发布设置:本地cms*/ namespace skycaiji\admin\event; + class Rcms extends Release{ protected $rele_cms_list=array(); /** diff --git a/SkycaijiApp/admin/event/Rdb.php b/SkycaijiApp/admin/event/Rdb.php index 814de11..2d31b7f 100644 --- a/SkycaijiApp/admin/event/Rdb.php +++ b/SkycaijiApp/admin/event/Rdb.php @@ -9,7 +9,6 @@ |-------------------------------------------------------------------------- */ -/*发布设置:本地cms*/ namespace skycaiji\admin\event; use skycaiji\admin\model\DbCommon; class Rdb extends Release{ diff --git a/SkycaijiApp/admin/event/ReleaseBase.php b/SkycaijiApp/admin/event/ReleaseBase.php index 99eef00..34b0d53 100644 --- a/SkycaijiApp/admin/event/ReleaseBase.php +++ b/SkycaijiApp/admin/event/ReleaseBase.php @@ -74,7 +74,7 @@ class ReleaseBase extends \skycaiji\admin\controller\BaseController{ return ''; } $val=$collFieldVal['value']; - if(!empty($GLOBALS['config']['caiji']['download_img'])){ + if(!empty($GLOBALS['_sc']['c']['download_img']['download_img'])){ if(!empty($collFieldVal['img'])){ @@ -96,8 +96,8 @@ class ReleaseBase extends \skycaiji\admin\controller\BaseController{ $curI++; if($curI<$total){ - if(!empty($GLOBALS['config']['caiji']['img_interval'])){ - sleep($GLOBALS['config']['caiji']['img_interval']); + if(!empty($GLOBALS['_sc']['c']['download_img']['img_interval'])){ + sleep($GLOBALS['_sc']['c']['download_img']['img_interval']); } } } @@ -110,8 +110,8 @@ class ReleaseBase extends \skycaiji\admin\controller\BaseController{ static $imgPaths=array(); static $imgUrls=array(); - $img_path=$GLOBALS['config']['caiji']['img_path']; - $img_url=$GLOBALS['config']['caiji']['img_url']; + $img_path=$GLOBALS['_sc']['c']['download_img']['img_path']; + $img_url=$GLOBALS['_sc']['c']['download_img']['img_url']; if(!isset($imgPaths[$img_path])){ if(empty($img_path)){ @@ -142,6 +142,15 @@ class ReleaseBase extends \skycaiji\admin\controller\BaseController{ return $url; } + + if(!empty($GLOBALS['_sc']['c']['caiji']['robots'])){ + + if(!model('Collector')->abide_by_robots($url)){ + $this->echo_msg('robots拒绝访问的网址:'.$url); + return $url; + } + } + static $imgList=array(); $key=md5($url); if(!isset($imgList[$key])){ @@ -153,43 +162,75 @@ class ReleaseBase extends \skycaiji\admin\controller\BaseController{ } $filename=''; $imgurl=''; - $imgname=''; + $isExists=false; + $imgname=$GLOBALS['_sc']['c']['download_img']['img_name']; - if('url'==$GLOBALS['config']['caiji']['img_name']){ + if('url'==$imgname){ - $imgname=substr($key,0,2).'/'.substr($key,-2,2).'/'; + + + + $imgname=substr($key,0,2).'/'.substr($key,-2,2).'/'.$key.'.'.$prop; + $filename=$img_path.$imgname; + $isExists=file_exists($filename); + + if(!$isExists){ + + + $imgname=substr($key,0,2).'/'.substr($key,2).'.'.$prop; + $filename=$img_path.$imgname; + $isExists=file_exists($filename); + } + }elseif('custom'==$imgname){ + + $imgname=model('Config')->convert_img_name_path($GLOBALS['_sc']['c']['download_img']['name_custom_path'],$url); + $imgname.='/'.$key.'.'.$prop; + $filename=$img_path.$imgname; + $isExists=file_exists($filename); }else{ - $imgname=date('Y-m-d',NOW_TIME).'/'; + $imgname=date('Y-m-d',NOW_TIME).'/'.$key.'.'.$prop; + $filename=$img_path.$imgname; + $isExists=file_exists($filename); } - $imgname.=$key.'.'.$prop; - $filename=$img_path.$imgname; $imgurl=$img_url.$imgname; - if(!file_exists($filename)){ + if(!$isExists){ $mproxy=model('Proxyip'); try { $options=array(); - if(!empty($GLOBALS['config']['caiji']['img_timeout'])){ + $headers=array(); + + if(!empty($GLOBALS['_sc']['task_request_headers'])&&!empty($GLOBALS['_sc']['task_request_headers']['request_headers']['download_img'])){ - $options['timeout']=$GLOBALS['config']['caiji']['img_timeout']; + $headers=$GLOBALS['_sc']['task_request_headers']['headers']; + if(!empty($headers['useragent'])){ + + $options['useragent']=$headers['useragent']; + } + unset($headers['useragent']); } - if(!empty($GLOBALS['config']['proxy']['open'])){ + + if(!empty($GLOBALS['_sc']['c']['download_img']['img_timeout'])){ - $proxy_ip=$mproxy->get_usable_ip(); - $proxyIp=$mproxy->to_proxy_ip($proxy_ip); + $options['timeout']=$GLOBALS['_sc']['c']['download_img']['img_timeout']; + } + if(!empty($GLOBALS['_sc']['c']['proxy']['open'])){ + + $proxyIp=$mproxy->get_usable_ip(); + $proxyIp=$mproxy->to_proxy_ip($proxyIp); if(!empty($proxyIp)){ $options['proxy']=$proxyIp; } } - if(!empty($GLOBALS['config']['caiji']['img_max'])){ + if(!empty($GLOBALS['_sc']['c']['download_img']['img_max'])){ - $options['max_bytes']=intval($GLOBALS['config']['caiji']['img_max'])*1024*1024; + $options['max_bytes']=intval($GLOBALS['_sc']['c']['download_img']['img_max'])*1024*1024; } - $imgCode=get_html($url,null,$options,'utf-8'); + $imgCode=get_html($url,$headers,$options,'utf-8'); if(!empty($imgCode)){ diff --git a/SkycaijiApp/admin/event/Rfile.php b/SkycaijiApp/admin/event/Rfile.php index 8db9589..992aee1 100644 --- a/SkycaijiApp/admin/event/Rfile.php +++ b/SkycaijiApp/admin/event/Rfile.php @@ -9,7 +9,6 @@ |-------------------------------------------------------------------------- */ -/*发布设置:本地cms*/ namespace skycaiji\admin\event; class Rfile extends Release { /** diff --git a/SkycaijiApp/admin/event/Rtoapi.php b/SkycaijiApp/admin/event/Rtoapi.php new file mode 100644 index 0000000..9436f19 --- /dev/null +++ b/SkycaijiApp/admin/event/Rtoapi.php @@ -0,0 +1,137 @@ +error('请输入接口地址'); + } + if(empty($toapi['response']['id'])){ + $this->error('请输入响应id的健名'); + } + + + if(is_array($toapi['param_name'])){ + $toapi['param_name']=array_array_map('trim', $toapi['param_name']); + foreach ($toapi['param_name'] as $k=>$v){ + if(empty($v)){ + + unset($toapi['param_name'][$k]); + unset($toapi['param_val'][$k]); + unset($toapi['param_addon'][$k]); + } + } + } + $config['toapi']=$toapi; + return $config; + } + /*导出数据*/ + public function export($collFieldsList,$options=null){ + $addedNum=0; + if(empty($this->config['toapi']['url'])){ + $this->echo_msg('接口地址为空'); + }else{ + $urlMd5=md5($this->config['toapi']['url']); + $url=''; + if(!isset($this->url_list[$urlMd5])){ + + $url=$this->config['toapi']['url']; + if(strpos($url, '/')===0){ + $url=config('root_website').$url; + }elseif(!preg_match('/^\w+\:\/\//', $url)){ + $url='http://'.$url; + } + $this->url_list[$urlMd5]=$url; + }else{ + $url=$this->url_list[$urlMd5]; + } + $response=$this->config['toapi']['response']; + $response=is_array($response)?$response:array(); + + foreach ($collFieldsList as $collFieldsKey=>$collFields){ + + $contTitle=$collFields['title']; + $contUrl=$collFields['url']; + $collFields=$collFields['fields']; + + $params=array(); + if(is_array($this->config['toapi']['param_name'])){ + + foreach($this->config['toapi']['param_name'] as $k=>$pname){ + if(empty($pname)){ + + continue; + } + $pval=$this->config['toapi']['param_val'][$k]; + if(empty($pval)){ + $params[$pname]=$pval; + }elseif($pval=='custom'){ + $params[$pname]=$this->config['toapi']['param_addon'][$k]; + }elseif(preg_match('/^field\:(.+)$/ui',$pval,$fieldName)){ + + $params[$pname]=$this->get_field_val($collFields[$fieldName[1]]); + } + } + } + if($this->config['toapi']['type']=='post'){ + + $params=is_array($params)?$params:''; + }else{ + + $url.=(strpos($url,'?')===false?'?':'&').http_build_query($params); + $params=null; + } + $json=get_html($url,null,array(),'utf-8',$params); + $json=json_decode($json,true); + + $returnData=array('id'=>'','target'=>'','desc'=>'','error'=>''); + + if(!empty($response['id'])&&isset($json[$response['id']])){ + + foreach ($returnData as $k=>$v){ + + if(isset($response[$k])){ + $returnData[$k]=$json[$response[$k]]?$json[$response[$k]]:''; + }else{ + $returnData[$k]=''; + } + } + + if($returnData['id']>0){ + $addedNum++; + if($returnData['id']>1&&empty($returnData['target'])){ + + $returnData['target']='编号:'.$returnData['id']; + } + } + }else{ + + $returnData['id']=0; + $returnData['error']='无响应状态'; + } + + $this->record_collected($contUrl,$returnData,$this->release,$contTitle); + + unset($collFieldsList[$collFieldsKey]['fields']); + } + } + return $addedNum; + } +} +?> \ No newline at end of file diff --git a/SkycaijiApp/admin/lang/zh-cn.php b/SkycaijiApp/admin/lang/zh-cn.php index adbd73e..c75a6d4 100644 --- a/SkycaijiApp/admin/lang/zh-cn.php +++ b/SkycaijiApp/admin/lang/zh-cn.php @@ -76,8 +76,8 @@ return array( 'user_login_in'=>'登录中...', 'user_auto_login'=>'正在自动登录...', - 'usertoken_error'=>'用户token错误,请刷新页面重新获取!', - + 'usertoken_error'=>'用户token错误,请刷新界面重新获取或清除浏览器缓存!', + 'task'=>'任务', 'task_add'=>'添加任务', 'task_edit'=>'编辑任务', @@ -166,6 +166,7 @@ return array( 'process_module_batch'=>'批量替换', 'process_module_substr'=>'截取字符串', 'process_module_func'=>'使用函数', + 'process_module_api'=>'调用接口', 'p_m_if_1'=>'满足条件采集', 'p_m_if_2'=>'满足条件不采集', @@ -182,7 +183,8 @@ return array( 'rele_module'=>'发布方式', 'rele_module_cms'=>'本地CMS程序', 'rele_module_db'=>'数据库', - 'rele_module_api'=>'API接口', + 'rele_module_api'=>'生成API', + 'rele_module_toapi'=>'调用接口', 'rele_module_file'=>'文件存储', 'rele_module_diy'=>'自定义插件', 'rele_btn_detect'=>'开始检测', @@ -206,6 +208,7 @@ return array( 'collected_rele_cms'=>'CMS', 'collected_rele_db'=>'数据库', 'collected_rele_file'=>'文件', + 'collected_rele_toapi'=>'接口', 'collected_rele_api'=>'API', 'collected_rele_diy'=>'插件', diff --git a/SkycaijiApp/admin/model/App.php b/SkycaijiApp/admin/model/App.php index 91127f9..433a840 100644 --- a/SkycaijiApp/admin/model/App.php +++ b/SkycaijiApp/admin/model/App.php @@ -42,6 +42,8 @@ class App extends BaseModel{ $config=array(); } $appClass->config=is_array($config)?$config:array(); + }else{ + $appClass->config=array(); } } } diff --git a/SkycaijiApp/admin/model/CacheModel.php b/SkycaijiApp/admin/model/CacheModel.php index 9507bad..a1f37db 100644 --- a/SkycaijiApp/admin/model/CacheModel.php +++ b/SkycaijiApp/admin/model/CacheModel.php @@ -54,6 +54,7 @@ class CacheModel{ * @return mixed */ public function getCache($cname,$key=null){ + $cache=$this->db()->where('cname',$cname)->find(); switch($cache['ctype']){ case 1:$cache['data']=intval($cache['data']);break; diff --git a/SkycaijiApp/admin/model/Collector.php b/SkycaijiApp/admin/model/Collector.php index 8fbfa6b..0c91a5d 100644 --- a/SkycaijiApp/admin/model/Collector.php +++ b/SkycaijiApp/admin/model/Collector.php @@ -26,7 +26,110 @@ class Collector extends BaseModel{ $this->strict(false)->where(array('id'=>$id))->update($data); } - + /*遵守robots协议*/ + public function abide_by_robots($url){ + static $robotsList=array(); + $domain=null; + if(preg_match('/^(\w+\:\/\/[^\/\\\]+)(.*)$/i',$url,$domain)){ + $url='/'.ltrim($domain[2],'\/\\'); + $domain=rtrim($domain[1],'\/\\'); + } + if(empty($domain)){ + + return true; + } + + $robots=array(); + if(isset($robotsList[$domain])){ + $robots=$robotsList[$domain]; + }else{ + $robotsTxt=get_html($domain.'/robots.txt'); + + if(!empty($robotsTxt)){ + + $robotsTxt=preg_replace('/\#[^\r\n]*$/m', '', $robotsTxt); + + $rule=null; + if(preg_match('/\bUser-agent\s*:\s*skycaiji\s+(?P[\s\S]+?)(?=((\bUser-agent\s*\:)|\s*$))/i',$robotsTxt,$rule)){ + + $rule=$rule['rule']; + }elseif(preg_match('/\bUser-agent\s*:\s*\*\s+(?P[\s\S]+?)(?=((\bUser-agent\s*\:)|\s*$))/i',$robotsTxt,$rule)){ + + $rule=$rule['rule']; + }else{ + $rule=null; + } + if(!empty($rule)){ + + + static $replace=array('\\','/','.','*','?','~','!','@','#','%','&','(',')','[',']','{','}','+','=','|',':',','); + static $replaceTo=array('\\\\','\/','\.','.*','\?','\~','\!','\@','\#','\%','\&','\(','\)','\[','\]','\{','\}','\+','\=','\|','\:','\,'); + + $allow=array(); + $disallow=array(); + + if(preg_match_all('/\bAllow\s*:([^\r\n]+)/i',$rule,$allow)){ + $allow=array_unique($allow[1]); + }else{ + $allow=array(); + } + if(preg_match_all('/\bDisallow\s*:([^\r\n]+)/i',$rule,$disallow)){ + $disallow=array_unique($disallow[1]); + }else{ + $disallow=array(); + } + + $robots=array( + 'allow'=>$allow, + 'disallow'=>$disallow + ); + + foreach ($robots as $k=>$v){ + foreach ($v as $vk=>$vv){ + $vv=trim($vv); + if(empty($vv)||$vv=='/'){ + + unset($v[$vk]); + }else{ + $vv=str_replace($replace, $replaceTo, $vv); + if(strpos($vv,'\/')===0){ + + $vv='^'.$vv; + } + $v[$vk]=$vv; + } + } + $robots[$k]=$v; + } + } + } + $robotsList[$domain]=$robots; + } + if(empty($robots)){ + + return true; + } + if(!empty($robots['allow'])){ + foreach ($robots['allow'] as $v){ + if(preg_match('/'.$v.'/', $url)){ + + return true; + break; + } + } + } + + if(!empty($robots['disallow'])){ + foreach ($robots['disallow'] as $v){ + if(preg_match('/'.$v.'/', $url)){ + + return false; + break; + } + } + } + return true; + } } ?> \ No newline at end of file diff --git a/SkycaijiApp/admin/model/Config.php b/SkycaijiApp/admin/model/Config.php index 729b450..069a49a 100644 --- a/SkycaijiApp/admin/model/Config.php +++ b/SkycaijiApp/admin/model/Config.php @@ -30,6 +30,7 @@ class Config extends BaseModel { * @return mixed */ public function getConfig($cname,$key=null){ + $item=$this->where('cname',$cname)->find(); if(!empty($item)){ $item=$item->toArray(); @@ -55,7 +56,29 @@ class Config extends BaseModel { } $data['dateline']=time(); $this->insert($data,true); + + + $this->cacheConfigList(); } + /*缓存所有配置*/ + public function cacheConfigList(){ + $keyConfig='cache_config_all'; + $configDbList=$this->column('*'); + $configDbList=empty($configDbList)?array():$configDbList; + $configList=array(); + foreach ($configDbList as $configItem){ + $configItem=$this->convertData($configItem); + $configList[$configItem['cname']]=$configItem['data']; + } + cache($keyConfig,array('list'=>$configList)); + } + public function getConfigList(){ + $keyConfig='cache_config_all'; + $cacheConfig=cache($keyConfig); + $configList=$cacheConfig['list']; + return is_array($configList)?$configList:array(); + } + /*设置版本号*/ public function setVersion($version){ $version=trim(strtoupper($version),'V'); @@ -111,5 +134,105 @@ class Config extends BaseModel { } return $return; } + + public function check_img_name_path($path){ + static $check_list=array(); + if(!isset($check_list[$path])){ + $return=array('success'=>false,'msg'=>''); + if(!empty($path)){ + if(!preg_match('/^(\w+|\-|\/|(\[(年|月|日|时|前两位|后两位)\]))+$/u',$path)){ + $return['msg']='图片名称自定义目录只能输入字母、数字、- 、/ 或 标签'; + }else{ + if(preg_match('/^\/+$/', $path)){ + $return['msg']='图片名称自定义目录不能只由/组成'; + }else{ + $return['success']=true; + } + } + } + $check_list[$path]=$return; + }else{ + $return=$check_list[$path]; + } + + return $return; + } + + public function convert_img_name_path($path,$url){ + $check=$this->check_img_name_path($path); + if($check['success']){ + $md5=md5($url); + static $tags=array('[年]','[月]','[日]','[时]','[前两位]','[后两位]'); + $tagsRe=array( + date('Y',NOW_TIME), + date('m',NOW_TIME), + date('d',NOW_TIME), + date('H',NOW_TIME), + substr($md5,0,2), + substr($md5,-2,2), + ); + $path=preg_replace('/\/{2,}/', '/', $path); + $path=str_replace($tags, $tagsRe, $path); + $path=trim($path,'/'); + }else{ + $path='temp'; + } + return $path; + } + + + public function get_img_config_from_caiji($caijiConfig){ + $config=array(); + if(!empty($caijiConfig)){ + + static $vars=array('download_img','img_path','img_url','img_name','img_timeout','img_interval','img_max'); + foreach ($vars as $var){ + if(isset($caijiConfig[$var])){ + $config[$var]=$caijiConfig[$var]; + } + } + } + return $config; + } + + public function detect_php_exe(){ + static $php_filename=null; + + if(!isset($php_filename)){ + $ds=DIRECTORY_SEPARATOR; + $ini_all=ini_get_all(); + $php_ext_path=$ini_all['extension_dir']['local_value']; + if($php_ext_path){ + $php_ext_path=preg_replace('/[\/\\\]+/', '/', $php_ext_path); + $phpPaths=explode('/', $php_ext_path); + $phpPath=''; + if(IS_WIN){ + + foreach ($phpPaths as $v){ + $phpPath.=$v.$ds; + if(is_file($phpPath.'php-cli.exe')){ + $php_filename=$phpPath.'php-cli.exe'; + break; + }elseif(is_file($phpPath.'php.exe')){ + $php_filename=$phpPath.'php.exe'; + break; + } + } + }else{ + + foreach ($phpPaths as $v){ + $phpPath.=$v.$ds; + if(is_file($phpPath.'bin'.$ds.'php')){ + $php_filename=$phpPath.'bin'.$ds.'php'; + break; + } + } + } + }else{ + $php_filename='php'; + } + } + return $php_filename; + } } ?> \ No newline at end of file diff --git a/SkycaijiApp/admin/model/DbCommon.php b/SkycaijiApp/admin/model/DbCommon.php index e7183a5..962344e 100644 --- a/SkycaijiApp/admin/model/DbCommon.php +++ b/SkycaijiApp/admin/model/DbCommon.php @@ -32,7 +32,7 @@ class DbCommon{ 'break_reconnect'=>true, 'params'=>array(), ); - if(!empty($GLOBALS['config']['site']['dblong'])){ + if(!empty($GLOBALS['_sc']['c']['site']['dblong'])){ $this->config['params'][\PDO::ATTR_PERSISTENT]=true; } diff --git a/SkycaijiApp/admin/model/FuncApp.php b/SkycaijiApp/admin/model/FuncApp.php new file mode 100644 index 0000000..833eb42 --- /dev/null +++ b/SkycaijiApp/admin/model/FuncApp.php @@ -0,0 +1,219 @@ +array ( + 'name'=>'数据处理', + 'loc'=>'数据处理》使用函数' + ), + 'processIf'=>array( + 'name'=>'条件判断', + 'loc'=>'数据处理》条件判断》使用函数' + ) + ); + public function __construct($data = []){ + parent::__construct($data); + $this->funcPath=config('plugin_path').DIRECTORY_SEPARATOR.'func'.DIRECTORY_SEPARATOR; + } + /*添加入库*/ + public function insertApp($data){ + $data=is_array($data)?$data:array(); + $data['module']=$this->format_module($data['module']); + $data['name']=strip_tags($data['name']); + $data['addtime']=time(); + $data['enable']=0; + $data['uptime']=$data['uptime']>0?$data['uptime']:time(); + return $this->strict(false)->insert($data,false,true); + } + /*创建插件并入库*/ + public function createApp($module,$app,$appData=array()){ + $module=$this->format_module($module); + $funcFile=$this->filename($module,$app); + $funcTpl=file_get_contents(config('app_path').'/public/func_app/class.tpl'); + + $name=$appData['name']; + if(!empty($appData['name'])){ + $appData['name']="/**\r\n * ".$appData['name']."\r\n */"; + }else{ + $appData['name']=''; + } + + if(is_array($appData['methods'])){ + $methods=''; + foreach ($appData['methods']['method'] as $k=>$v){ + if(preg_match('/^[a-z\_]\w*/',$v)){ + + $methods.="\r\n /**\r\n * ".strip_tags($appData['methods']['comment'][$k])."\r\n */" + ."\r\n public function {$v}(\$val){\r\n return \$val;\r\n }"; + } + } + $appData['methods']=$methods; + }else{ + $appData['methods']=''; + } + + $funcTpl=str_replace(array('{$module}','{$classname}','{$name}','{$methods}'), array($module,$app,$appData['name'],$appData['methods']), $funcTpl); + + if(write_dir_file($funcFile,$funcTpl)){ + return $this->insertApp(array('module'=>$module,'app'=>$app,'name'=>$name)); + }else{ + return false; + } + } + /*添加插件*/ + public function addFunc($func,$code=''){ + if(empty($func['app'])){ + return false; + } + $func['module']=$this->format_module($func['module']); + $func['uptime']=$func['uptime']>0?$func['uptime']:time(); + $funcData=$this->where('app',$func['app'])->find(); + $success=false; + + if(!empty($funcData)){ + + $this->strict(false)->where('app',$func['app'])->update($func); + $success=true; + }else{ + + $func['id']=$this->insertApp($func); + $success=$func['id']>0?true:false; + } + if($success){ + $funcAppPath=config('plugin_path').'/func'; + if(!empty($code)){ + + write_dir_file($funcAppPath.'/'.$func['module'].'/'.ucfirst($func['app']).'.php', $code); + } + } + return $success; + } + public function filename($module,$app){ + $module=$this->format_module($module); + return $this->funcPath."{$module}/{$app}.php"; + } + /*获取插件文件类的属性*/ + public function get_app_class($module,$app){ + $module=$this->format_module($module); + $filename=$this->funcPath."{$module}/{$app}.php"; + if(file_exists($filename)){ + $class=$this->app_classname($module, $app); + if(class_exists($class)){ + $copyright=''; + $identifier=''; + if(preg_match('/^(\w+?)([A-Z])(\w*)$/',$app,$mapp)){ + $identifier=$mapp[1]; + $copyright=$mapp[2].$mapp[3]; + } + $class=new $class(); + + $reClass = new \ReflectionClass($class); + $name=$reClass->getDocComment(); + $name=preg_replace('/^[\/\*\s]+/m', '', $name); + $name=trim($name); + + $reMethods=$reClass->getMethods(\ReflectionMethod::IS_PUBLIC); + $methods=array(); + if(!empty($reMethods)){ + foreach ($reMethods as $reMethod){ + $comment=$reMethod->getDocComment(); + $comment=preg_replace('/^[\/\*\s]+/m', '', $comment); + $comment=trim($comment); + + $methods[$reMethod->name]=array('comment'=>$comment); + } + } + return array ( + 'module' => $module, + 'app' => $app, + 'filename' => $filename, + 'copyright' => $copyright, + 'identifier' => $identifier, + 'name' => $name, + 'methods' => $methods + ); + } + } + return array(); + } + public function app_classname($module,$app){ + return '\\plugin\\func\\'.$module.'\\'.$app; + } + /*转换成app名称*/ + public function app_name($copyright,$identifier){ + $copyright=$this->format_copyright($copyright); + $identifier=$this->format_identifier($identifier); + return $identifier.$copyright; + } + public function format_module($module){ + return $module; + } + public function format_copyright($copyright){ + return ucfirst(strtolower($copyright)); + } + public function format_identifier($identifier){ + return ucfirst(strtolower($identifier)); + } + + public function right_module($module){ + if(empty($this->funcModules[$module])){ + return false; + }else{ + return true; + } + } + public function right_copyright($copyright){ + if(preg_match('/^[a-z]+[a-z0-9]*$/i',$copyright)){ + return true; + }else{ + return false; + } + } + public function right_identifier($identifier){ + if(preg_match('/^[a-z]+[a-z0-9]*$/i',$identifier)){ + return true; + }else{ + return false; + } + } + /*获取所有插件类*/ + public function get_class_list($module){ + $apps=$this->get_app_list($module); + $classList=array(); + foreach($apps as $app){ + $class=$this->get_app_class($module,$app); + if(!empty($class)){ + $classList[$app]=$class; + } + } + return $classList; + } + public function get_app_list($module){ + $apps=scandir($this->funcPath.$module); + $appList=array(); + if(!empty($apps)){ + foreach($apps as $app){ + if(preg_match('/(\w+)\.php/i',$app,$mapp)){ + $appList[$app]=$mapp[1]; + } + } + } + return $appList; + } + +} + +?> \ No newline at end of file diff --git a/SkycaijiApp/admin/model/Provider.php b/SkycaijiApp/admin/model/Provider.php index 5256d83..b41acac 100644 --- a/SkycaijiApp/admin/model/Provider.php +++ b/SkycaijiApp/admin/model/Provider.php @@ -13,10 +13,11 @@ namespace skycaiji\admin\model; /*第三方服务商*/ class Provider extends BaseModel{ /*匹配域名*/ - public static function matchDomain($url){ + public static function match_domain($url){ $domain=null; if(preg_match('/^\w+\:\/\/[\w\-]+(\.[\w\-]+)*(\:\d+){0,1}/', $url,$domain)){ $domain=rtrim($domain[0],'/'); + $domain=strtolower($domain); }else{ $domain=null; } @@ -24,7 +25,7 @@ class Provider extends BaseModel{ } /*获取id*/ public function getIdByUrl($url){ - $url=self::matchDomain($url); + $url=self::match_domain($url); if(is_official_url($url)){ $url=null; diff --git a/SkycaijiApp/admin/model/Proxyip.php b/SkycaijiApp/admin/model/Proxyip.php index fd00797..7f58581 100644 --- a/SkycaijiApp/admin/model/Proxyip.php +++ b/SkycaijiApp/admin/model/Proxyip.php @@ -16,25 +16,27 @@ class Proxyip extends BaseModel { public $setting; public function __construct($data=[]){ parent::__construct($data); - $this->setting=$GLOBALS['config']['proxy']; + $this->setting=$GLOBALS['_sc']['c']['proxy']; } - /*转换成get_html中格式的ip*/ - public function to_proxy_ip($proxy_ip){ - $proxyIp=null; - if(empty($proxy_ip)||empty($proxy_ip['ip'])){ + /*数据库ip转换成get_html格式的ip*/ + public function to_proxy_ip($proxyDbIp){ + if(empty($proxyDbIp)||empty($proxyDbIp['ip'])){ - $proxyIp=null; + return null; } - if(empty($proxy_ip['user'])){ + $ip=explode(':',$proxyDbIp['ip']); + if(empty($ip[0])){ + return null; + } + $proxyIp=array( + 'ip'=>$ip[0], + 'port'=>$ip[1], + 'type'=>$proxyDbIp['type'], + ); + if(!empty($proxyDbIp['user'])){ - $proxyIp=$proxy_ip['ip']; - }else{ - - $proxyIp=array( - $proxy_ip['ip'], - $proxy_ip['user'], - $proxy_ip['pwd'] - ); + $proxyIp['user']=$proxyDbIp['user']; + $proxyIp['pwd']=$proxyDbIp['pwd']; } return $proxyIp; } @@ -43,7 +45,6 @@ class Proxyip extends BaseModel { if(!empty($this->setting['open'])){ $cond=array(); - $cond['invalid']=0; if(!empty($this->setting['use'])){ if($this->setting['use']=='num'){ @@ -57,7 +58,25 @@ class Proxyip extends BaseModel { $cond['num']=array('lt',1); } + $cond['invalid']=0; $proxyipData=$this->where($cond)->find(); + + if(empty($proxyipData)&&!empty($this->setting['api']['open'])){ + + $apiInsert=strtolower($this->setting['api']['insert']); + if(empty($apiInsert)){ + + if($this->where('invalid',0)->count()<=0){ + + $this->add_api_ips($this->setting); + $proxyipData=$this->where($cond)->find(); + } + }elseif($apiInsert=='end'){ + + $this->add_api_ips($this->setting); + $proxyipData=$this->where($cond)->find(); + } + } if(empty($proxyipData)){ @@ -117,6 +136,118 @@ class Proxyip extends BaseModel { } $this->strict(false)->where(array('ip'=>$proxy_ip['ip']))->update($upData); } + /*代理类型*/ + public function proxy_types(){ + return array('http(s)'=>'','socks4'=>'socks4','socks5'=>'socks5'); + } + /*匹配格式的ip*/ + public function get_format_ips($html,$format,$multiple=false){ + if(empty($html)||empty($format)){ + return null; + } + $format=$this->convert_format($format); + + if(!$multiple){ + + $ip=null; + if(preg_match('/'.$format.'/i',$html,$mip)){ + $ip=array( + 'ip'=>$mip['ip'], + 'port'=>$mip['port'], + 'user'=>$mip['user'], + 'pwd'=>$mip['pwd'], + ); + } + return $ip; + }else{ + + $ips=array(); + if(preg_match_all('/'.$format.'/i',$html,$mips)){ + for($i=0;$i$mips['ip'][$i], + 'port'=>$mips['port'][$i], + 'user'=>$mips['user'][$i], + 'pwd'=>$mips['pwd'][$i], + ); + } + } + return $ips; + } + return null; + } + + public function convert_format($format){ + static $list=array(); + $md5=md5($format); + if(!isset($list[$md5])){ + $format=preg_replace('/\\\*([\'\/])/',"\\\\$1",$format); + $format=str_replace(array('[ip]','[端口]','[用户名]','[密码]','(*)') + ,array('(?P(\d+\.){3}\d+)','(?P\d+)','(?P[^\s\\\'\"\<\>\,]*)','(?P[^\s\\\'\"\<\>\,]*)','[\s\S]*?') + ,$format); + + $list[$md5]=$format; + }else{ + $format=$list[$md5]; + } + return $format; + } + + public function ips_format2db($ipList,$default=array()){ + $ipList=is_array($ipList)?$ipList:array(); + $default=is_array($default)?$default:array(); + foreach ($ipList as $k=>$ip){ + if(empty($ip)||empty($ip['ip'])){ + unset($ipList[$k]); + continue; + } + if(empty($ip['user'])){ + $ip['user']=$default['user']; + + $ip['pwd']=$default['pwd']; + } + $ip['ip']=$ip['ip'].':'.$ip['port']; + $ip['type']=$default['type']; + $ip['addtime']=NOW_TIME; + unset($ip['port']); + $ipList[$k]=$ip; + } + return $ipList; + } + + + private function add_api_ips(){ + $config=$this->setting; + if(!is_array($config)||empty($config['api'])||empty($config['api']['open'])||!is_array($config['apis'])){ + return; + } + foreach ($config['apis'] as $api){ + if(empty($api['api_url'])||!preg_match('/^\w+\:\/\//',$api['api_url'])||empty($api['api_format'])){ + + continue; + } + $timeout=intval($api['api_interval']); + $timeout=$timeout>0?$timeout*60:60; + $cname=md5($api['api_url']); + $mcahce=CacheModel::getInstance('proxy_api'); + if($mcahce->expire($cname,$timeout)){ + + $mcahce->setCache($cname, 1); + $html=get_html($api['api_url']); + $ips=$this->get_format_ips($html, $api['api_format'],true); + $ips = $this->ips_format2db ( $ips, array ( + 'type' => $api ['api_type']?$api ['api_type']:'', + 'user' => $api ['api_user']?$api ['api_user']:'', + 'pwd' => $api ['api_pwd']?$api ['api_pwd']:'', + ) ); + + if(!empty($ips)){ + + $this->strict(false)->insertAll($ips,true,500); + } + } + } + } } ?> \ No newline at end of file diff --git a/SkycaijiApp/admin/model/Task.php b/SkycaijiApp/admin/model/Task.php index 4e13550..6a10b90 100644 --- a/SkycaijiApp/admin/model/Task.php +++ b/SkycaijiApp/admin/model/Task.php @@ -22,35 +22,35 @@ class Task extends BaseModel{ static $global_config=null; if(!isset($global_config)){ - $global_config=$GLOBALS['config']; + $global_config=$GLOBALS['_sc']['c']; } - if(!empty($global_config['caiji']['download_img'])){ + if(!empty($global_config['download_img']['download_img'])){ if($config['download_img']=='n'){ - $GLOBALS['config']['caiji']['download_img']=0; + $GLOBALS['_sc']['c']['download_img']['download_img']=0; }else{ - $GLOBALS['config']['caiji']['download_img']=1; + $GLOBALS['_sc']['c']['download_img']['download_img']=1; } }else{ - $GLOBALS['config']['caiji']['download_img']=0; + $GLOBALS['_sc']['c']['download_img']['download_img']=0; } if(!empty($global_config['proxy']['open'])){ if($config['proxy']=='n'){ - $GLOBALS['config']['proxy']['open']=0; + $GLOBALS['_sc']['c']['proxy']['open']=0; }else{ - $GLOBALS['config']['proxy']['open']=1; + $GLOBALS['_sc']['c']['proxy']['open']=1; } }else{ - $GLOBALS['config']['proxy']['open']=0; + $GLOBALS['_sc']['c']['proxy']['open']=0; } - $GLOBALS['config']['caiji']['img_path']=empty($config['img_path'])?$global_config['caiji']['img_path']:$config['img_path']; - $GLOBALS['config']['caiji']['img_url']=empty($config['img_url'])?$global_config['caiji']['img_url']:$config['img_url']; + $GLOBALS['_sc']['c']['download_img']['img_path']=empty($config['img_path'])?$global_config['download_img']['img_path']:$config['img_path']; + $GLOBALS['_sc']['c']['download_img']['img_url']=empty($config['img_url'])?$global_config['download_img']['img_url']:$config['img_url']; } } diff --git a/SkycaijiApp/admin/model/User.php b/SkycaijiApp/admin/model/User.php index fc0a086..930dffc 100644 --- a/SkycaijiApp/admin/model/User.php +++ b/SkycaijiApp/admin/model/User.php @@ -40,7 +40,7 @@ class User extends BaseModel{ */ public static function right_pwd($pwd,$name='password'){ $return=array('name'=>$name); - if(!preg_match('/^[a-zA-Z0-9\!\@\#\$\%\^\&\*]{6,20}$/i', $pwd)){ + if(!preg_match('/^[a-zA-Z0-9\`\~\!\@\#\$\%\^\*\(\)\-\_\+\=\|\{\}\[\]\:\;\,\.\?\&\'\"\<\>]{6,30}$/i', $pwd)){ $return['msg']=lang('user_error_password'); }else{ $return['success']=true; diff --git a/SkycaijiApp/admin/model/Usergroup.php b/SkycaijiApp/admin/model/Usergroup.php index f1265eb..ebdc6da 100644 --- a/SkycaijiApp/admin/model/Usergroup.php +++ b/SkycaijiApp/admin/model/Usergroup.php @@ -22,7 +22,7 @@ class Usergroup extends BaseModel{ } /*等级限制:判断当前用户组等级小于等于传入的等级*/ public function user_level_limit($level){ - if($GLOBALS['user']['group']['level']<=$level){ + if($GLOBALS['_sc']['user']['group']['level']<=$level){ return true; }else{ return false; diff --git a/SkycaijiApp/admin/view/backstage/index.html b/SkycaijiApp/admin/view/backstage/index.html index 08e2240..089354a 100644 --- a/SkycaijiApp/admin/view/backstage/index.html +++ b/SkycaijiApp/admin/view/backstage/index.html @@ -75,8 +75,11 @@ {/if} - 社区论坛 - SkyCaiji交流社区 + 官方网站 + + 社区  + 官网 + 清除缓存 @@ -114,7 +117,35 @@
-
+{if !empty($systemWarning)} +
+ {if !empty($systemWarning['php'])} + 请开启PHP模块:{:implode(' , ',$systemWarning['php'])}
+ {/if} + {if !empty($systemWarning['path_write'])} + 请设置可写权限:{:implode(' , ',$systemWarning['path_write'])}
+ {/if} + {if !empty($systemWarning['path_read'])} + 请设置可读权限:{:implode(' , ',$systemWarning['path_read'])}
+ {/if} +
+{/if} + +
+
+

系统消息

+
+ +
+
+
+ {if !empty($adminIndexData)} + {$adminIndexData['html']} + {/if} +
+
{/block} \ No newline at end of file diff --git a/SkycaijiApp/admin/view/collected/chart.html b/SkycaijiApp/admin/view/collected/chart.html new file mode 100644 index 0000000..edec785 --- /dev/null +++ b/SkycaijiApp/admin/view/collected/chart.html @@ -0,0 +1,64 @@ +{extend name="common:main" /} +{block name="cssjs"} + + + +{/block} +{block name="content"} +
+
+ +
+
+
+
+

发布方式

+
+
+
+ +
+
+
+ +
+
+
+
+
+
+

任务

+
+
+
+ +
+
+
+ +
+
+
+
+ + +{/block} \ No newline at end of file diff --git a/SkycaijiApp/admin/view/collected/list.html b/SkycaijiApp/admin/view/collected/list.html index 8ddb7a6..5d46dff 100644 --- a/SkycaijiApp/admin/view/collected/list.html +++ b/SkycaijiApp/admin/view/collected/list.html @@ -12,7 +12,7 @@
- +
@@ -46,13 +46,13 @@ - {if condition="!empty($pagenav)"} - + {/if} diff --git a/SkycaijiApp/admin/view/collector/set.html b/SkycaijiApp/admin/view/collector/set.html index 1842a02..70cd56a 100644 --- a/SkycaijiApp/admin/view/collector/set.html +++ b/SkycaijiApp/admin/view/collector/set.html @@ -8,6 +8,10 @@ + {if input('?easymode')} + + {/if} + {if condition="$taskData['module']=='pattern'"} {include file="cpattern:set" /} {elseif condition="$taskData['module']=='weixin'" /} diff --git a/SkycaijiApp/admin/view/common/browser_is_old.html b/SkycaijiApp/admin/view/common/browser_is_old.html index 929ead9..817f054 100644 --- a/SkycaijiApp/admin/view/common/browser_is_old.html +++ b/SkycaijiApp/admin/view/common/browser_is_old.html @@ -1,4 +1,4 @@ -{if condition="!empty($GLOBALS['browser_is_old'])"} +{if condition="!empty($GLOBALS['_sc']['browser_is_old'])"} -

其它标签可直接输入,用空格或逗号分隔

+

其它标签可直接输入,用空格或逗号分隔

@@ -52,6 +52,7 @@

替换成

+

可使用$数字调用正则捕获组(不包括通配符)的内容

@@ -98,13 +99,13 @@
';packHtml=packHtml.replace('_name_',pack.name).replace('_type_',this.packTypes[pack.type]).replace('_link_',pack.nav_link).replace('_pack_',url_base64encode(JSON.stringify(pack)));if(objid){$('#'+objid).html(packHtml)}else{$('#pack_list tbody').append(''+packHtml+'')}},init_app_pack:function(){var $_o=this;$('#win_form_pack').submit(function(){var checkName=!0;var curName=$('#win_form_pack [name="pack[name]"]').val();var objid=$('#win_form_pack input[name="objid"]').val();if(objid){if(curName==$('#'+objid).find('.pack-name').text()){checkName=!1}} if(checkName){var hasName=!1;$('#pack_list .pack-name').each(function(){if(curName==$(this).text()){hasName=!0;return!1}});if(hasName){toastr.error('名称已存在!');return!1}} -$.ajax({type:'POST',dataType:'json',url:$(this).attr('action'),data:$(this).serialize(),success:function(data){if(data.code==1){$_o.add_app_pack(data.data,objid);$('#myModal').modal('hide')}else{toastr.error(data.msg)}},error:function(data){toastr.error(data)}});return!1});$('#win_form_pack [name="pack[type]"]').bind('change',function(){var type=$(this).val();$(this).siblings('.help-block').each(function(){if($(this).hasClass('type-'+type)){$(this).show()}else{$(this).hide()}})})},load_app_pack:function(pack){if(pack){$('#win_form_pack [name="pack[name]"]').val(pack.name);$('#win_form_pack [name="pack[type]"]').val(pack.type).trigger('change');$('#win_form_pack [name="pack[nav_link]"]').val(pack.nav_link);$('#win_form_pack [name="pack[target]"][value="'+parseInt(pack.target)+'"]').prop('checked','checked')}},down_framework:function(list,size){var $_o=this;$.ajax({type:'get',dataType:'json',url:ulink('develop/installFramework?op=down&app='+$('#form_app [name="app"]').val()),data:{'size':list.size,'start_size':size.start,'end_size':size.end,'id':size.id},success:function(data){if(data.code==1){$_o.downFrameworkSize+=size.end-size.start+1;$_o.downFrameworkNum++;$('#install_framework').find('.num').text(parseInt($_o.downFrameworkSize/1024)+'kb');$('#install_framework').find('.perct').text(parseInt(($_o.downFrameworkSize/list.size)*100));if(list.list.length>size.id&&list.list[size.id]){$_o.down_framework(list,list.list[size.id])}}else{$('#install_framework_error').append('

下载片段文件'+size.id+'失败

')}},error:function(){$('#install_framework_error').append('

获取片段文件'+size.id+'失败

')},complete:function(){if($_o.downFrameworkNum==list.list.length){$('#install_framework').find('.num').text(parseInt(list.size/1024)+'kb');$('#install_framework').find('.perct').text(100);$('#install_framework').find('.status').text('下载完成,正在安装...');$.ajax({type:'get',dataType:'json',url:ulink('develop/installFramework?op=install&app='+$('#form_app [name="app"]').val()),data:{size:list.size},success:function(data){if(data.code==1){ajaxDataMsg(data)}else{$('#install_framework_error').html(data.msg?data.msg:'

文件缺失,请刷新界面重新下载

')}}})}}})}} +$.ajax({type:'POST',dataType:'json',url:$(this).attr('action'),data:$(this).serialize(),success:function(data){if(data.code==1){$_o.add_app_pack(data.data,objid);$('#myModal').modal('hide')}else{toastr.error(data.msg)}},error:function(data){toastr.error(data)}});return!1});$('#win_form_pack [name="pack[type]"]').bind('change',function(){var type=$(this).val();$(this).siblings('.help-block').each(function(){if($(this).hasClass('type-'+type)){$(this).show()}else{$(this).hide()}})})},load_app_pack:function(pack){if(pack){$('#win_form_pack [name="pack[name]"]').val(pack.name);$('#win_form_pack [name="pack[type]"]').val(pack.type).trigger('change');$('#win_form_pack [name="pack[nav_link]"]').val(pack.nav_link);$('#win_form_pack [name="pack[target]"][value="'+parseInt(pack.target)+'"]').prop('checked','checked')}},down_framework:function(list,size){var $_o=this;$.ajax({type:'get',dataType:'json',url:ulink('develop/installFramework?op=down&app='+$('#form_app [name="app"]').val()),data:{'size':list.size,'start_size':size.start,'end_size':size.end,'id':size.id},success:function(data){if(data.code==1){$_o.downFrameworkSize+=size.end-size.start+1;$_o.downFrameworkNum++;$('#install_framework').find('.num').text(parseInt($_o.downFrameworkSize/1024)+'kb');$('#install_framework').find('.perct').text(parseInt(($_o.downFrameworkSize/list.size)*100));if(list.list.length>size.id&&list.list[size.id]){$_o.down_framework(list,list.list[size.id])}}else{$('#install_framework_error').append('

下载片段文件'+size.id+'失败

')}},error:function(){$('#install_framework_error').append('

获取片段文件'+size.id+'失败

')},complete:function(){if($_o.downFrameworkNum==list.list.length){$('#install_framework').find('.num').text(parseInt(list.size/1024)+'kb');$('#install_framework').find('.perct').text(100);$('#install_framework').find('.status').text('下载完成,正在安装...');$.ajax({type:'get',dataType:'json',url:ulink('develop/installFramework?op=install&app='+$('#form_app [name="app"]').val()),data:{size:list.size},success:function(data){if(data.code==1){ajaxDataMsg(data)}else{$('#install_framework_error').html(data.msg?data.msg:'

文件缺失,请刷新界面重新下载

')}}})}}})},func:function(config){var $_o=this;$('#add_method').bind('click',function(){$_o.func_add_method()});$('#form_func').on('click','.delete-method',function(){$(this).parents('tr').remove()});if(config&&!$.isEmptyObject(config)){for(var i in config){$('#form_func [name="'+i+'"]').val(config[i])} +$('#form_func [name="module"]').attr('disabled','disabled');$('#form_func [name="identifier"]').attr('disabled','disabled');$('#form_func [name="copyright"]').attr('disabled','disabled')}},func_add_method:function(method,desc){method=method?method:'';desc=desc?desc:'';var tr=''+''+'';$('#form_func table.method_list tbody').append(tr)}} var developClass=new DevelopClass() \ No newline at end of file diff --git a/public/static/js/admin/index.js b/public/static/js/admin/index.js index 3f35615..fb506d9 100644 --- a/public/static/js/admin/index.js +++ b/public/static/js/admin/index.js @@ -8,8 +8,9 @@ |-------------------------------------------------------------------------- */ 'use strict';$(document).ready(function(){$('#op_clean').bind('click',function(){var obj=$(this);if(obj.attr('cleaning')==1){return!1} -confirmRight('确定清除缓存?',function(){obj.attr('cleaning',1).html('正在清理...');$.ajax({type:'get',dataType:'json',url:ulink('Setting/clean'),success:function(data){obj.attr('cleaning',0);if(data.code==1){obj.html('清理完成')}else{obj.html('清理失败')}}})});return!1});$('#upgrade_db').bind('click',function(){var obj=$(this);$.ajax({type:'get',dataType:'json',url:ulink('Install/upgrade/db'),success:function(data){if(data.code==1){obj.html(data.msg?data.msg:'升级成功')}else{obj.html(data.msg?data.msg:'升级失败')}}})});$('#a_collect_now').bind('click',function(){windowIframe('实时采集',ulink('Admin/Backstage/collect'))});$('#upgrade_check').html('正在检测更新...');$.ajax({type:'get',dataType:'json',async:!0,url:ulink('Upgrade/newVersion'),success:function(data){if(data.code==1){data=data.data;if(data){var html='检测到新版本V'+data.new_version+'点击升级';if(data.version_link){html+='  '+data.version_link[0]+''} -$('#upgrade_check').html(html)}}else{$('#upgrade_check').html('暂无更新')}}});$('body').on('click','#op_upgrade',function(){var obj=$(this);if(obj.attr('upgrading')==1){return!1} +confirmRight('确定清除缓存?',function(){obj.attr('cleaning',1).html('正在清理...');$.ajax({type:'get',dataType:'json',url:ulink('Setting/clean'),success:function(data){obj.attr('cleaning',0);if(data.code==1){obj.html('清理完成')}else{obj.html('清理失败')}}})});return!1});$('#upgrade_db').bind('click',function(){var obj=$(this);$.ajax({type:'get',dataType:'json',url:ulink('Install/upgrade/db'),success:function(data){if(data.code==1){obj.html(data.msg?data.msg:'升级成功')}else{obj.html(data.msg?data.msg:'升级失败')}}})});$('#a_collect_now').bind('click',function(){windowIframe('实时采集',ulink('Admin/Backstage/collect'))});$('#upgrade_check').html('正在检测更新...');$.ajax({type:'get',dataType:'json',async:!0,url:ulink('Upgrade/newVersion'),success:function(data){data=data.data;if(data){if(data.is_new_version){var html='检测到新版本V'+data.new_version+'点击升级';if(data.version_link){html+='  '+data.version_link[0]+''} +$('#upgrade_check').html(html)}else{$('#upgrade_check').html('暂无更新')} +if(data.is_new_admin_index){$('#refresh_admin_index').trigger('click')}}else{$('#upgrade_check').html('')}}});$('body').on('click','#op_upgrade',function(){var obj=$(this);if(obj.attr('upgrading')==1){return!1} obj.html('正在检索更新文件...');$.ajax({type:'get',dataType:'json',url:ulink('Upgrade/newFiles'),success:function(data){obj.attr('upgrading',1);if(data.code==1){var fileList=new Array();if(data.data.files){for(var i in data.data.files){fileList.push(data.data.files[i])}} -if(fileList.length>0){obj.html('正在更新...');var upgradeClass=new UpgradeClass(fileList);upgradeClass.down_file(0)}else{obj.html('没有需要更新的文件')}}else{var upgradeClass=new UpgradeClass(null);upgradeClass.down_complete()}}});return!1});$.ajax({type:'get',dataType:'jsonp',async:!0,url:ulink('Backstage/adminIndex'),success:function(data){$('#skycaiji_admin_index').html(data.html)}})});function UpgradeClass(fileList){this.fileList=fileList;this.fileNum=fileList?fileList.length:0;this.downNum=0} +if(fileList.length>0){obj.html('正在更新...');var upgradeClass=new UpgradeClass(fileList);upgradeClass.down_file(0)}else{obj.html('没有需要更新的文件')}}else{var upgradeClass=new UpgradeClass(null);upgradeClass.down_complete()}}});return!1});$('#refresh_admin_index').bind('click',function(){$('#skycaiji_admin_index').parents('.box').eq(0).find('.overlay').show();$.ajax({type:'get',dataType:'json',async:!0,url:ulink('Backstage/adminIndex?refresh=1'),success:function(data){var html=data.html?data.html:'';$('#skycaiji_admin_index').html(html);$('#skycaiji_admin_index').parents('.box').eq(0).find('.overlay').hide()}})})});function UpgradeClass(fileList){this.fileList=fileList;this.fileNum=fileList?fileList.length:0;this.downNum=0} UpgradeClass.prototype={constructor:UpgradeClass,down_file:function(index){var $_o=this;var file=$_o.fileList[index];$.ajax({type:'get',dataType:'json',url:ulink('Upgrade/downFile'),data:{filename:file.file,filemd5:file.md5},success:function(data){if(data.code==1){$_o.downNum++;$('#op_upgrade').html('正在更新... '+$_o.downNum+'/'+$_o.fileNum)}else{$('#op_upgrade').html('更新失败');$('#upgrade_error').show();if(data.msg){$('#upgrade_error').append(data.msg+"\r\n")}else{$('#upgrade_error').append('更新失败:'+file.file+"\r\n")}}},error:function(){$('#op_upgrade').html('更新失败');$('#upgrade_error').show();$('#upgrade_error').append('获取失败:'+file.file+"\r\n")},complete:function(){if(index+1>=$_o.fileNum){if($_o.downNum>=$_o.fileNum){$_o.down_complete()}else{$('#op_upgrade').html('请刷新界面重新下载失败的文件!')}}else{$_o.down_file(index+1)}}})},down_complete:function(){$('#op_upgrade').html('正在校验更新文件...');$.ajax({type:'get',dataType:'json',url:ulink('Upgrade/downComplete'),success:function(data){if(data.code==1){$('#op_upgrade').html('更新成功')}else{$('#op_upgrade').html('更新失败');$('#upgrade_error').show();for(var fi in data.data){$('#upgrade_error').append('文件校验失败:'+data.data[fi]+"\r\n")}}}})}} \ No newline at end of file diff --git a/public/static/js/admin/json_tree.js b/public/static/js/admin/json_tree.js index d64a8b9..46378ad 100644 --- a/public/static/js/admin/json_tree.js +++ b/public/static/js/admin/json_tree.js @@ -9,7 +9,7 @@ */ 'use strict';function JsonTree(){this.treeId='';this.treeClass=''} JsonTree.prototype={constructor:JsonTree,load:function(data){var $_o=this;if(htmlIsJson(data)){data=JSON.parse(data);if(data){var html='
    ';for(var i in data){html+=$_o.node(i,data[i])} -html+='
';$($_o.treeId).html(html)}} +html+='';$($_o.treeId).html(html)}}else{$($_o.treeId).html('未获取到JSON数组')} $($_o.treeId).on('click',$_o.treeClass,function(){if($(this).hasClass('glyphicon-triangle-bottom')){$(this).removeClass('glyphicon-triangle-bottom');$(this).addClass('glyphicon-triangle-right');var hasSub=!1;$(this).siblings('ul').children('li').each(function(){var subTree=$(this).find($_o.treeClass).eq(0);if(subTree.length>0){subTree.removeClass('glyphicon-triangle-bottom').addClass('glyphicon-triangle-right').siblings('ul').hide();hasSub=!0}});if(!hasSub){$(this).siblings('ul').hide()}}else{$(this).removeClass('glyphicon-triangle-right');$(this).addClass('glyphicon-triangle-bottom');$(this).siblings('ul').show();$(this).siblings('ul').children('li').each(function(){$(this).find($_o.treeClass).eq(0).removeClass('glyphicon-triangle-right').addClass('glyphicon-triangle-bottom').siblings('ul').show()})}})},node:function(node,list){var $_o=this;var html='
  • ';var isList=!1;if(list){if(typeof(list)=='object'&&!$.isEmptyObject(list)){isList=!0}} if(isList){html+=''+node+'
      ';for(var i in list){html+=$_o.node(i,list[i])} html+='
    '}else{html+=''+node+': '+list+''} diff --git a/public/static/js/admin/mystore.js b/public/static/js/admin/mystore.js index debaf66..619eee3 100644 --- a/public/static/js/admin/mystore.js +++ b/public/static/js/admin/mystore.js @@ -7,11 +7,17 @@ | 使用协议 https://www.skycaiji.com/licenses |-------------------------------------------------------------------------- */ -$(document).ready(function(){storeClass.init_my()});function init_rules(){$('table.datatable thead th[data-order]').bind('click',function(){var order=$(this).attr('data-order');if(!order){return!1} +'use strict';$(document).ready(function(){if(window.storeClass){window.storeClass.init_my()}});function init_rules(){$('table.datatable thead th[data-order]').bind('click',function(){var order=$(this).attr('data-order');if(!order){return!1} var className=$(this).attr('class');var sort='desc';if(className=='sorting_desc'){sort='asc'} window.location.href=ulink('Mystore/rule?order='+order+'&sort='+sort);return!1});$('#deleteall').bind('click',function(){var obj=$(this);confirmRight(window.tpl_lang.confirm_delete,function(){$.ajax({type:"POST",url:ulink('Mystore/ruleOp?op=deleteall'),dataType:"json",data:$('#form_list').serialize(),success:function(data){data.code==1?toastr.success(data.msg):toastr.error(data.msg);setTimeout("window.location.reload();",2500)}})})});$('#list_table .delete').bind('click',function(){var obj=$(this);confirmRight(window.tpl_lang.confirm_delete,function(){$.ajax({type:"GET",url:obj.attr('url'),dataType:"json",success:function(data){data.code==1?toastr.success(data.msg):toastr.error(data.msg);if(data.code==1){obj.parents('tr').eq(0).remove()}}})})});$('#list_table').on('click','.store-detail',function(){windowStore('详细',$(this).attr('href'))});$('#auto_check').bind('click',function(){var auto=$(this).is(':checked')?1:0;$.ajax({type:"GET",url:ulink('Mystore/ruleOp?op=auto_check&auto='+auto),dataType:"json",success:function(data){data.code==1?toastr.success(data.msg):toastr.error(data.msg)}})});if($('#auto_check').is(':checked')){check_rules_update()}} function check_rules_update(){var ids=new Array();$('#list_table').find('tr[data-rule-id]').each(function(){ids.push($(this).attr('data-rule-id'))});if(ids.length>0){$('.nav-check-update a').html(' 正在检测更新');$('.store-info').find('.has-update').remove();$.ajax({type:"get",url:ulink('Mystore/ruleOp?op=check_store_update'),dataType:"json",async:!0,data:{ids:ids},success:function(data){if(data.code==1){for(var id in data.data){var storeInfo=$('tr[data-rule-id="'+data.data[id]+'"]').find('.store-info');storeInfo.append('
    有更新
    ')}}},complete:function(){$('.nav-check-update a').html('检测更新')}})}} function init_releaseapp(){$('table.datatable thead th[data-order]').bind('click',function(){var order=$(this).attr('data-order');if(!order){return!1} var className=$(this).attr('class');var sort='desc';if(className=='sorting_desc'){sort='asc'} window.location.href=ulink('Mystore/releaseApp?order='+order+'&sort='+sort);return!1});$('#deleteall').bind('click',function(){var obj=$(this);confirmRight(window.tpl_lang.confirm_delete,function(){$.ajax({type:"POST",url:ulink('Mystore/releaseAppOp?op=deleteall'),dataType:"json",data:$('#form_list').serialize(),success:function(data){data.code==1?toastr.success(data.msg):toastr.error(data.msg);setTimeout("window.location.reload();",2500)}})})});$('#list_table .delete').bind('click',function(){var obj=$(this);confirmRight(window.tpl_lang.confirm_delete,function(){$.ajax({type:"GET",url:obj.attr('url'),dataType:"json",success:function(data){data.code==1?toastr.success(data.msg):toastr.error(data.msg);if(data.code==1){obj.parents('tr').eq(0).remove()}}})})});$('#list_table').on('click','.store-detail',function(){windowStore('详细',$(this).attr('href'))});$('#auto_check').bind('click',function(){var auto=$(this).is(':checked')?1:0;$.ajax({type:"GET",url:ulink('Mystore/releaseAppOp?op=auto_check&auto='+auto),dataType:"json",success:function(data){data.code==1?toastr.success(data.msg):toastr.error(data.msg)}})});if($('#auto_check').is(':checked')){check_releaseapp_update()}} -function check_releaseapp_update(){var ids=new Array();$('#list_table').find('tr[data-app-id]').each(function(){ids.push($(this).attr('data-app-id'))});if(ids.length>0){$('.nav-check-update a').html(' 正在检测更新');$('.store-info').find('.has-update').remove();$.ajax({type:"get",url:ulink('Mystore/releaseAppOp?op=check_store_update'),dataType:"json",async:!0,data:{ids:ids},success:function(data){if(data.code==1){for(var id in data.data){var storeInfo=$('tr[data-app-id="'+data.data[id]+'"]').find('.store-info');storeInfo.append('
    有更新
    ')}}},complete:function(){$('.nav-check-update a').html('检测更新')}})}} \ No newline at end of file +function check_releaseapp_update(){var ids=new Array();$('#list_table').find('tr[data-app-id]').each(function(){ids.push($(this).attr('data-app-id'))});if(ids.length>0){$('.nav-check-update a').html(' 正在检测更新');$('.store-info').find('.has-update').remove();$.ajax({type:"get",url:ulink('Mystore/releaseAppOp?op=check_store_update'),dataType:"json",async:!0,data:{ids:ids},success:function(data){if(data.code==1){for(var id in data.data){var storeInfo=$('tr[data-app-id="'+data.data[id]+'"]').find('.store-info');storeInfo.append('
    有更新
    ')}}},complete:function(){$('.nav-check-update a').html('检测更新')}})}} +function init_funcapp(){$('table.datatable thead th[data-order]').bind('click',function(){var order=$(this).attr('data-order');if(!order){return!1} +var className=$(this).attr('class');var sort='desc';if(className=='sorting_desc'){sort='asc'} +window.location.href=ulink('Mystore/funcApp?order='+order+'&sort='+sort);return!1});$('#deleteall').bind('click',function(){var obj=$(this);confirmRight(window.tpl_lang.confirm_delete,function(){$.ajax({type:"POST",url:ulink('Mystore/funcAppOp?op=deleteall'),dataType:"json",data:$('#form_list').serialize(),success:function(data){data.code==1?toastr.success(data.msg):toastr.error(data.msg);setTimeout("window.location.reload();",2500)}})})});$('#list_table .delete').bind('click',function(){var obj=$(this);var id=$(this).parents('tr[data-app-id]').attr('data-app-id');confirmRight(window.tpl_lang.confirm_delete,function(){$.ajax({type:"GET",url:ulink('Mystore/funcAppOp?op=delete'),dataType:"json",data:{id:id},success:function(data){data.code==1?toastr.success(data.msg):toastr.error(data.msg);if(data.code==1){obj.parents('tr').eq(0).remove()}}})})});$('#list_table .enable').bind('click',function(){var obj=$(this);var id=$(this).parents('tr[data-app-id]').attr('data-app-id');var enable=($(this).text()=='已启用')?0:1;$.ajax({type:'GET',url:ulink('Mystore/funcAppOp?op=enable'),dataType:'json',data:{id:id,enable:enable},success:function(data){if(data.code){obj.text(enable?'已启用':'已禁用');obj.css('color',(enable?'green':'red'))}else{toastr.error(data.msg)}}})});$('#list_table .methods .dropdown-toggle').bind('click',function(){var obj=$(this);var id=$(this).parents('tr[data-app-id]').attr('data-app-id');var box=obj.parents('.methods');box.find('.dropdown-menu').html('
  • ');$.ajax({type:'GET',url:ulink('Mystore/funcAppOp?op=detail'),dataType:'json',data:{id:id},success:function(data){if(data.code&&data.data){var methods=data.data.methods;var html='';for(var i in methods){html+='
  • '+i+':'+methods[i].comment+'
  • '} +if(!html){html='
  • 无方法
  • '} +box.find('.dropdown-menu').html(html)}}})});$('#auto_check').bind('click',function(){var auto=$(this).is(':checked')?1:0;$.ajax({type:"GET",url:ulink('Mystore/funcAppOp?op=auto_check&auto='+auto),dataType:"json",success:function(data){data.code==1?toastr.success(data.msg):toastr.error(data.msg)}})});if($('#auto_check').is(':checked')){check_funcapp_update()}} +function check_funcapp_update(){var ids=new Array();$('#list_table').find('tr[data-app-id]').each(function(){ids.push($(this).attr('data-app-id'))});if(ids.length>0){$('.nav-check-update a').html(' 正在检测更新');$('.store-info').find('.has-update').remove();$.ajax({type:"get",url:ulink('Mystore/funcAppOp?op=check_store_update'),dataType:"json",async:!0,data:{ids:ids},success:function(data){if(data.code==1){for(var id in data.data){var storeInfo=$('tr[data-app-id="'+data.data[id]+'"]').find('.store-info');storeInfo.append('
    有更新
    ')}}},complete:function(){$('.nav-check-update a').html('检测更新')}})}} \ No newline at end of file diff --git a/public/static/js/admin/proxy.js b/public/static/js/admin/proxy.js index 98b053a..cc4f4e0 100644 --- a/public/static/js/admin/proxy.js +++ b/public/static/js/admin/proxy.js @@ -7,13 +7,18 @@ | 使用协议 https://www.skycaiji.com/licenses |-------------------------------------------------------------------------- */ -function ProxyClass(){} -ProxyClass.prototype={constructor:ProxyClass,init:function(proxyConfig){var $_o=this;$('#btn_sub').bind('click',function(){var ip_list=new Array();var user_list=new Array();var pwd_list=new Array();$('[data-name="ip_list[]"]').each(function(){ip_list.push($(this).val())});$('[data-name="user_list[]"]').each(function(){user_list.push($(this).val())});$('[data-name="pwd_list[]"]').each(function(){pwd_list.push($(this).val())});if(ip_list){$('[name="ip_list"]').val(JSON.stringify(ip_list))} +'use strict';function ProxyClass(){} +ProxyClass.prototype={constructor:ProxyClass,init_setting:function(proxyConfig){var $_o=this;$('#proxy_ip_table').attr('data-tpl',$('#proxy_ip_table .proxy-ip-tpl').html());$('#proxy_ip_table .proxy-ip-tpl').remove();$('#btn_sub').bind('click',function(){var ip_list=new Array();var user_list=new Array();var pwd_list=new Array();var type_list=new Array();$('[data-name="ip_list[]"]').each(function(){ip_list.push($(this).val())});$('[data-name="user_list[]"]').each(function(){user_list.push($(this).val())});$('[data-name="pwd_list[]"]').each(function(){pwd_list.push($(this).val())});$('[data-name="type_list[]"]').each(function(){type_list.push($(this).val())});if(ip_list){$('[name="ip_list"]').val(JSON.stringify(ip_list))} if(user_list){$('[name="user_list"]').val(JSON.stringify(user_list))} -if(pwd_list){$('[name="pwd_list"]').val(JSON.stringify(pwd_list))}});$('[name="open"]').bind('click',function(){if($(this).val()==1){$('#proxy_open').show()}else{$('#proxy_open').hide()}});$('#add_proxy_ip').bind('click',function(){$_o.add(!0,'','','')});$('#batch_proxy_ip').bind('click',function(){windowModal('批量添加',ulink('Setting/proxyBatch'))});$('#clear_proxy_ip').bind('click',function(){confirmRight('确定清空所有代理?',function(){$('#proxy_ip_table tbody').html('')})});$('#proxy_ip_table').on('click','.delete-proxy-ip',function(){$(this).parents('tr').eq(0).remove()});$('[name="use"]').bind('click',function(){$('[id^="proxy_use_"]').hide();$('#proxy_use_'+$(this).val()).show()});if(proxyConfig){$('[name="open"][value="'+parseInt(proxyConfig.open)+'"]').trigger('click');$('[name="random"][value="'+parseInt(proxyConfig.random)+'"]').prop('checked','checked');$('[name="failed"]').val(parseInt(proxyConfig.failed));$('[name="use"][value="'+proxyConfig.use+'"]').trigger('click');$('[name="use_num"]').val(parseInt(proxyConfig.use_num));$('[name="use_time"]').val(parseInt(proxyConfig.use_time));if(proxyConfig.ip_list){var html='';for(var i in proxyConfig.ip_list){var ip=proxyConfig.ip_list[i];html+=$_o.add(!1,ip.ip,ip.user,ip.pwd,{invalid:ip.invalid,failed:ip.failed})} -$('#proxy_ip_table tbody').append(html)}}},add:function(is_append,ip,user,pwd,info){var infoStr='';if(info){if(info.invalid>0){infoStr+='无效的  '} -if(info.failed>0){infoStr+='失败'+info.failed+'次'}} -var html=''+(infoStr?(''):'')+''+''+'';if(!is_append){return html}else{$('#proxy_ip_table tbody').append(html)}},batch:function(){var $_o=this;$('#win_form_proxy_batch .format a').bind('click',function(){var fmt_val=$('#win_form_proxy_batch input[name="format"]').val();$('#win_form_proxy_batch input[name="format"]').val(fmt_val+$(this).text())});$('#win_form_proxy_batch').bind('submit',function(){$.ajax({type:'POST',dataType:'json',url:$(this).attr('action'),data:$(this).serialize(),success:function(data){if(data.code==1){if(data.data){var html='';for(var i in data.data){var ip=data.data[i];html+=$_o.add(!1,ip.ip,ip.user,ip.pwd)} -$('#proxy_ip_table tbody').append(html)}}else{toastr.error(data.msg)} -$('#myModal').modal('hide')},error:function(data){$_o.find('button[type="submit"]').removeAttr('disabled');toastr.error(data)}});return!1})}} +if(pwd_list){$('[name="pwd_list"]').val(JSON.stringify(pwd_list))} +if(type_list){$('[name="type_list"]').val(JSON.stringify(type_list))}});$('[name="open"]').bind('click',function(){if($(this).val()==1){$('#proxy_open').show();$('#proxy_api').show()}else{$('#proxy_open').hide();$('#proxy_api').hide()}});$('#batch_proxy_ip').bind('click',function(){windowModal('批量添加',ulink('Proxy/batch'))});$('#add_proxy_ip').bind('click',function(){windowModal('添加代理IP',ulink('Proxy/add'))});$('#invalid_proxy_ip').bind('click',function(){confirmRight('确定清理无效ip?',function(){windowModal('正在清理...',ulink('Proxy/clearInvalid'),{ajax:{success:function(){$_o.reload_iframe('清理完成')}}})})});$('#proxy_ip_iframe').bind('load',function(){$('#panel_proxy_ip .loading').hide();$(this).show()});$('#proxy_ip_table').on('click','.delete-proxy-ip',function(){$(this).parents('tr').eq(0).remove()});$('[name="use"]').bind('click',function(){$('[id^="proxy_use_"]').hide();$('#proxy_use_'+$(this).val()).show()});$('[name="api[open]"]').bind('click',function(){if($(this).val()==1){$('#proxy_api_box').show()}else{$('#proxy_api_box').hide()}});$('#proxy_api .p-api-add').bind('click',function(){$_o.add_api()});$('#proxy_api').on('click','.p-api-format a',function(){var obj=$(this).parents('.p-api-panel').eq(0).find('[data-name="api_format"]');insertAtCaret(obj,$(this).text())});eleExchange('#proxy_api','.p-api-up','.p-api-down','.p-api-panel');$('#proxy_api').on('click','.p-api-delete',function(){var obj=$(this);confirmRight('确定删除?',function(){obj.parents('.p-api-panel').eq(0).remove()})});$('#proxy_api').on('click','.btn-api-test',function(){var config={};$(this).parents('.p-api-panel').eq(0).find('[data-name]').each(function(){var name=$(this).attr('data-name');config[name]=$(this).val()});windowModal('测试接口',ulink('Proxy/testApi'),{ajax:{method:'post',data:{config:config}}})});if(proxyConfig){$('[name="open"][value="'+parseInt(proxyConfig.open)+'"]').trigger('click');$('[name="failed"]').val(parseInt(proxyConfig.failed));$('[name="use"][value="'+proxyConfig.use+'"]').trigger('click');$('[name="use_num"]').val(parseInt(proxyConfig.use_num));$('[name="use_time"]').val(parseInt(proxyConfig.use_time));if(proxyConfig.api){$('[name="api[open]"][value="'+parseInt(proxyConfig.api.open)+'"]').trigger('click');$('[name="api[insert]"]').val(proxyConfig.api.insert)} +if(proxyConfig.apis){for(var i in proxyConfig.apis){$_o.add_api(proxyConfig.apis[i])}}}},reload_iframe:function(msg){$('#myModal').modal('hide');toastr.success(msg);$('#panel_proxy_ip .loading').show();$('#proxy_ip_iframe').hide();$('#proxy_ip_iframe').attr('src',$('#proxy_ip_iframe').attr('src')).hide()},add_api:function(data){data=data?data:{};var tpl=$('#proxy_api_tpl').clone();tpl.removeAttr('id').css('display','block');var unique=generateUUID();var collapseId='api_collapse_'+unique;tpl.find('.p-api-title').attr('href','#'+collapseId);tpl.find('.p-api-collapse').attr('id',collapseId);tpl.find('[data-name]').each(function(){var name=$(this).attr('data-name');$(this).attr('name','apis[i_'+unique+']['+name+']');if(data[name]){$(this).val(data[name])}});if(data.api_url){tpl.find('.p-api-title small').text(':'+data.api_url)} +$('#proxy_api_box').append(tpl)},init_list:function(search){search=search?search:{};if(search){for(var i in search){$('#form_search').find('[name="'+i+'"]').val(search[i])}} +$('#form_list').on('change','[data-name="ip_list[]"],[data-name="user_list[]"],[data-name="pwd_list[]"],[data-name="type_list[]"]',function(){$(this).parents('tr').eq(0).find('[data-name="ips[]"]').prop('checked',!0)});$('#form_list').on('click','.op-delete',function(){var tr=$(this).parents('tr').eq(0);var ip=tr.find('[data-name="ips[]"]').val();$.ajax({type:'get',dataType:'json',url:ulink('Proxy/op?op=delete&ip=_ip_',{'_ip_':ip}),success:function(data){if(data.code==1){tr.fadeOut(100,function(){tr.remove()});toastr.success(data.msg)}else{toastr.error(data.msg)}}})});$('#form_list').on('click','.check-all-ip',function(){var checked=$(this).is(":checked")?true:!1;$('[data-name="ips[]"]').prop('checked',checked)});$('#form_list').on('click','.delete-all-ip',function(){confirmRight('确定删除选中的IP?',function(){$('#form_list').find('[name="op"]').val('delete_all');var ips=new Array();$('#form_list').find('[data-name="ips[]"]').each(function(){if($(this).is(':checked')){ips.push($(this).val())}});if(ips){$('[name="ips"]').val(JSON.stringify(ips))} +$('#form_list').submit()})});$('#form_list').on('click','.update-all-ip',function(){confirmRight('确定修改?',function(){$('#form_list').find('[name="op"]').val('update_all');var ips=new Array();var ip_list=new Array();var user_list=new Array();var pwd_list=new Array();var type_list=new Array();$('#form_list').find('[data-name="ips[]"]').each(function(){if($(this).is(':checked')){ips.push($(this).val());var tr=$(this).parents('tr').eq(0);ip_list.push(tr.find('[data-name="ip_list[]"]').val());user_list.push(tr.find('[data-name="user_list[]"]').val());pwd_list.push(tr.find('[data-name="pwd_list[]"]').val());type_list.push(tr.find('[data-name="type_list[]"]').val())}});if(ips){$('[name="ips"]').val(JSON.stringify(ips))} +if(ip_list){$('[name="ip_list"]').val(JSON.stringify(ip_list))} +if(user_list){$('[name="user_list"]').val(JSON.stringify(user_list))} +if(pwd_list){$('[name="pwd_list"]').val(JSON.stringify(pwd_list))} +if(type_list){$('[name="type_list"]').val(JSON.stringify(type_list))} +$('#form_list').submit()})})},init_add:function(){var $_o=this;var formid='#win_form_proxy_add';$(formid+' .proxy-ip-list').attr('data-tpl',''+$(formid+' .tpl-proxy-ip').html()+'');$(formid+' .tpl-proxy-ip').remove();$(formid+' .add-proxy-ip').bind('click',function(){$(formid+' .proxy-ip-list tbody').append($(formid+' .proxy-ip-list').attr('data-tpl'))});$(formid).bind('submit',function(){$.ajax({type:'post',dataType:'json',url:$(this).attr('action'),data:$(this).serialize(),success:function(data){if(data.code==1){$_o.reload_iframe('添加成功')}else{toastr.error(data.msg)}}});return!1})},init_batch:function(){var $_o=this;var formid='#win_form_proxy_batch';$(formid+' .format a').bind('click',function(){var obj=$('#win_form_proxy_batch input[name="format"]');insertAtCaret(obj,$(this).text())});$(formid+' .btn-test').bind('click',function(){$(formid).find('[name="is_test"]').val(1);$.ajax({type:'POST',dataType:'json',url:$(formid).attr('action'),data:$(formid).serialize(),success:function(data){if(data.code==1){$(formid+' .test-result').show();$(formid+' .test-result').find('textarea').val(data.msg)}else{toastr.error(data.msg)}}})});$(formid).bind('submit',function(){$(formid).find('[name="is_test"]').val('');$.ajax({type:'POST',dataType:'json',url:$(this).attr('action'),data:$(this).serialize(),success:function(data){if(data.code==1){$_o.reload_iframe('添加成功')}else{toastr.error(data.msg)}},});return!1})},} var proxyClass=new ProxyClass() \ No newline at end of file diff --git a/public/static/js/admin/release.js b/public/static/js/admin/release.js index 8fc1d65..9a4557b 100644 --- a/public/static/js/admin/release.js +++ b/public/static/js/admin/release.js @@ -8,15 +8,18 @@ |-------------------------------------------------------------------------- */ 'use strict';function ReleaseClass(formid,releid){this.formid='#'+formid;this.releid=releid} -ReleaseClass.prototype={constructor:ReleaseClass,init:function(){var $_o=this;$($_o.formid+' select[name="module"]').bind('change',function(){$($_o.formid+' .rele-module').hide();$($_o.formid+' .rele-module[module="'+$(this).val()+'"]').show()});$('#btn_import_release').bind('click',function(){windowModal('导入配置会覆盖当前任务的发布设置,且不可恢复',ulink('Release/import'))});$('#rele_module_cms .btn-cms-detect').bind('click',function(){$_o.cms_detect()});$('#rele_module_cms .btn-cms-bind').bind('click',function(){$_o.cms_bind()});$('#rele_module_cms').on('change','select[name="cms[app]"]',function(){var cmsApp=$(this).val();$_o.cms_bind({cms:{app:cmsApp}})});$('#cms_list').on('click','li a',function(){var path=$(this).attr('path');if(path){$($_o.formid+' [name="cms[path]"]').val(path);$('#cms_tab a[href="#cms_tab_bind"]').tab('show');$_o.cms_bind()}});$('#rele_module_cms').on('change','select[name^="cms_app[param]"]',function(){var cusName=$(this).attr('name').replace('cms_app[param]','cms_app[custom]');if($(this).val()=='custom:'){$('input[name="'+cusName+'"]').show()}else{$('input[name="'+cusName+'"]').hide()}});$('#db_tab_config .btn-db-names').bind('click',function(){$_o.db_connect('db_names')});$('#db_tab_config .btn-db-connect').bind('click',function(){$_o.db_connect()});$('#db_tab_table').on('change','select[name^="db_table[field]"]',function(){var cusName=$(this).attr('name').replace('db_table[field]','db_table[custom]');if($(this).val()=='custom:'){$('input[name="'+cusName+'"]').show()}else{$('input[name="'+cusName+'"]').hide()}});$('#rele_module_file').on('click','.btn-file-rand-path',function(){var randStr=$_o.rand_str(10);$($_o.formid+' [name="file[path]"]').val(randStr)});$('#rele_module_api').on('click','.btn-api-rand-url',function(){var randStr=$_o.rand_str(10);$($_o.formid+' [name="api[url]"]').val(randStr)});$('#diy_tab').on('click','[data-type]',function(){$($_o.formid+' [name="diy[type]"]').val($(this).attr('data-type'))})},load:function(data){var $_o=this;if(data.module){$($_o.formid+' select[name="module"]').val(data.module).trigger('change')} +ReleaseClass.prototype={constructor:ReleaseClass,init:function(){var $_o=this;$($_o.formid+' select[name="module"]').bind('change',function(){$($_o.formid+' .rele-module').hide();$($_o.formid+' .rele-module[module="'+$(this).val()+'"]').show()});$('#btn_import_release').bind('click',function(){windowModal('导入配置会覆盖当前任务的发布设置,且不可恢复',ulink('Release/import'))});$('#rele_module_cms .btn-cms-detect').bind('click',function(){$_o.cms_detect()});$('#rele_module_cms .btn-cms-bind').bind('click',function(){$_o.cms_bind()});$('#rele_module_cms').on('change','select[name="cms[app]"]',function(){var cmsApp=$(this).val();$_o.cms_bind({cms:{app:cmsApp}})});$('#cms_list').on('click','li a',function(){var path=$(this).attr('path');if(path){$($_o.formid+' [name="cms[path]"]').val(path);$('#cms_tab a[href="#cms_tab_bind"]').tab('show');$_o.cms_bind()}});$('#rele_module_cms').on('change','select[name^="cms_app[param]"]',function(){var cusName=$(this).attr('name').replace('cms_app[param]','cms_app[custom]');if($(this).val()=='custom:'){$('input[name="'+cusName+'"]').show()}else{$('input[name="'+cusName+'"]').hide()}});$('#db_tab_config .btn-db-names').bind('click',function(){$_o.db_connect('db_names')});$('#db_tab_config .btn-db-connect').bind('click',function(){$_o.db_connect()});$('#db_tab_table').on('change','select[name^="db_table[field]"]',function(){var cusName=$(this).attr('name').replace('db_table[field]','db_table[custom]');if($(this).val()=='custom:'){$('input[name="'+cusName+'"]').show()}else{$('input[name="'+cusName+'"]').hide()}});$('#rele_module_file').on('click','.btn-file-rand-path',function(){var randStr=$_o.rand_str(10);$($_o.formid+' [name="file[path]"]').val(randStr)});$('#rele_module_api').on('click','.btn-api-rand-url',function(){var randStr=$_o.rand_str(10);$($_o.formid+' [name="api[url]"]').val(randStr)});$('#diy_tab').on('click','[data-type]',function(){$($_o.formid+' [name="diy[type]"]').val($(this).attr('data-type'))});$('#rele_module_toapi').on('click','.toapi-add-param',function(){$_o.toapi_add_param(null,null)});$('#rele_module_toapi').on('click','.toapi-del-param',function(){$(this).parents('tr').eq(0).remove()});$('#rele_module_toapi').on('change','[name="toapi[param_val][]"]',function(){var paddon=$(this).parents('tr').eq(0).find('[name="toapi[param_addon][]"]');if($(this).val()=='custom'){paddon.show()}else{paddon.hide()}})},load:function(data){var $_o=this;if(data.module){$($_o.formid+' select[name="module"]').val(data.module).trigger('change')} if(data.config){if('cms'==data.module){$_o.cms_bind(data.config);$(document).ready(function(){$('#cms_tab a[href="#cms_tab_bind"]').tab('show')})}else if('db'==data.module){$_o.db_bind(data.config)}else if('file'==data.module){if(data.config.file){$($_o.formid+' [name="file[path]"]').val(data.config.file.path);$($_o.formid+' [name="file[type]"]').each(function(){if($(this).val()==data.config.file.type){$(this).prop('checked',!0)}});$($_o.formid+' [name="file[txt_implode]"]').val(data.config.file.txt_implode);if(data.config.file.hide_fields){for(var fi in data.config.file.hide_fields){$($_o.formid+' [name="file[hide_fields][]"][value="'+data.config.file.hide_fields[fi]+'"]').prop('checked',!0)}}}}else if('api'==data.module){if(data.config.api){$($_o.formid+' [name="api[url]"]').val(data.config.api.url);$($_o.formid+' [name="api[cache_time]"]').val(data.config.api.cache_time);if(data.config.api.hide_fields){for(var fi in data.config.api.hide_fields){$($_o.formid+' [name="api[hide_fields][]"][value="'+data.config.api.hide_fields[fi]+'"]').prop('checked',!0)}}}}else if('diy'==data.module){if(data.config.diy){$(document).ready(function(){$('#diy_tab a[href="#diy_tab_'+data.config.diy.type+'"]').tab('show');for(var i in data.config.diy){$($_o.formid+' [name="diy['+i+']"]').val(data.config.diy[i])} if(data.config.diy.app){var appName=data.config.diy.app;if(appName.length>1){appName=appName.substr(0,1).toUpperCase()+appName.substr(1).toLowerCase()}else{appName=appName.toUpperCase()} -$($_o.formid+' [name="diy[app]"]').siblings('.help-block').find('b').text(appName+'.php')}})}}}},cms_detect:function(){var $_o=this;$('#cms_list').html('').addClass('loading');$.ajax({type:'get',url:ulink("Release/cmsDetect"),dataType:'json',success:function(data){$('#cms_list').removeClass('loading');if(data.code==1){var html='

    点击选择CMS

    ';for(var x in data.data){var list=data.data[x];html+='
      ';for(var y in list){html+='
    • '+list[y]+'
    • '} +$($_o.formid+' [name="diy[app]"]').siblings('.help-block').find('b').text(appName+'.php')}})}}else if('toapi'==data.module){var config=data.config.toapi;if(config){$($_o.formid+' [name="toapi[url]"]').val(config.url);$($_o.formid+' [name="toapi[type]"]').val(config.type);if(config.response){for(var i in config.response){$($_o.formid+' [name="toapi[response]['+i+']"]').val(config.response[i])}} +if(config.param_name){config.param_val=config.param_val?config.param_val:{};config.param_addon=config.param_addon?config.param_addon:{};for(var i in config.param_name){var pname=config.param_name[i]?config.param_name[i]:'';var pval=config.param_val[i]?config.param_val[i]:'';var paddon=config.param_addon[i]?config.param_addon[i]:'';$_o.toapi_add_param({name:pname,val:pval,addon:paddon},i)}}}}}},cms_detect:function(){var $_o=this;$('#cms_list').html('').addClass('loading');$.ajax({type:'get',url:ulink("Release/cmsDetect"),dataType:'json',success:function(data){$('#cms_list').removeClass('loading');if(data.code==1){var html='

      点击选择CMS

      ';for(var x in data.data){var list=data.data[x];html+=''} $('#cms_list').html(html)}else{$('#cms_list').html(data.msg)}}})},cms_bind:function(config){var $_o=this;$('#cms_bind').html('').addClass('loading');var postData=$($_o.formid).serialize();if(config&&config.cms&&config.cms.app){postData='cms[app]='+encodeURIComponent(config.cms.app)+'&'+postData} $.ajax({type:'post',url:ulink("Release/cmsBind"),dataType:'html',data:postData,success:function(data,textStatus,request){$('#cms_bind').removeClass('loading').show();if((/application\/json/i).test(request.getResponseHeader('Content-Type'))){data=jQuery.parseJSON(data);$('#cms_bind').html(''+data.msg+'')}else{$('#cms_bind').html(data);if(config&&config.cms_app){if(config.cms_app.param){for(var f in config.cms_app.param){var paramEle=$('#cms_bind').find('[name="cms_app[param]['+f+']"]');if(paramEle.is('select')){paramEle.val(config.cms_app.param[f]).trigger('change')}else if(paramEle.is('input:radio')){$('#cms_bind').find('[name="cms_app[param]['+f+']"][value="'+config.cms_app.param[f]+'"]').prop('checked','checked')}else{paramEle.val(config.cms_app.param[f])}} if(config.cms_app.custom){for(var f in config.cms_app.custom){$('#cms_bind').find('[name="cms_app[custom]['+f+']"]').val(config.cms_app.custom[f])}}}}}},error:function(XMLHttpRequest,textStatus,errorThrown){$('#cms_bind').removeClass('loading').show();$('#cms_bind').html(XMLHttpRequest.responseText)}})},db_bind:function(config){var $_o=this;$($_o.formid+' select[name="db[type]"]').val(config.db.type);$(document).ready(function(){$('#db_tab a[href="#db_tab_table"]').tab('show');$('#db_tab_table .db-table-list').html('').addClass('loading');$.ajax({type:'get',url:ulink("Release/dbTables?id=_id_",{_id_:$_o.releid}),timeout:5000,dataType:'json',success:function(data){if(data.code==1){$('#db_tab_table .db-table-list').html(data.msg)}else{$('#db_tab_table .db-table-list').css('color','red').html(data.msg)}},complete:function(XMLHttpRequest,status){$('#db_tab_table .db-table-list').removeClass('loading');    if(status=='timeout'){      $('#db_tab_table .db-table-list').css('color','red').html('连接数据库失败');    }   }});$('#db_tab_table .db-table-list').on('click','.btn-db-table-bind',function(){var curTable=$(this).parent().siblings('select').val();$_o.db_table_bind(curTable)});eleExchange('#db_table_bind_list','.glyphicon-arrow-up','.glyphicon-arrow-down','.panel-default');$('#db_table_bind_list').on('click','.glyphicon-remove',function(){var obj=$(this);confirmRight(window.tpl_lang.confirm_delete,function(){obj.parents('.panel').eq(0).remove()})});$('#db_table_bind_list').on('change','select[name^="field"]',function(){if($(this).val()=='custom:'){$(this).siblings('input[name^="custom"]').show()}});if(config.db_table&&config.db_table.field){var tables=new Array();for(var table in config.db_table.field){tables.push(table)} tables=tables.join(',');$('#db_tab_table .db-table-binding').addClass('loading');$_o.db_table_bind(tables)}})},db_table_bind:function(curTable){var $_o=this;var bindUrl=ulink("Release/dbTableBind?id=_id_&table=_table_",{_id_:$_o.releid,_table_:curTable});if($('#db_table_bind_list').find('[id^="db_table_name_'+curTable+'"]').length>0){toastr.error('已绑定该表')}else{$.ajax({type:'get',url:bindUrl,dataType:'html',success:function(data){if(htmlIsJson(data)){ajaxDataMsg(data)}else{$('#db_table_bind_list').append(data)}},complete:function(){$('#db_tab_table .db-table-binding').removeClass('loading').hide()}})}},db_connect:function(op){op=op?op:'';var $_o=this;$('#db_tab_config .rele-db-error').html('').addClass('loading');$.ajax({type:'post',url:ulink("Release/dbConnect?op="+op),timeout:5000,dataType:'json',data:$($_o.formid).serialize(),success:function(data){if(data.code==1){if(op=='db_names'){modal('选择数据库',data.msg)}else{$('#db_tab_config .rele-db-error').css('color','green').html(data.msg)}}else{toastr.error(data.msg);$('#db_tab_config .rele-db-error').css('color','red').html(data.msg)}},complete:function(XMLHttpRequest,status){$('#db_tab_config .rele-db-error').removeClass('loading');if(status=='timeout'){$('#db_tab_config .rele-db-error').css('color','red').html('连接数据库失败')} -  }})},rand_str:function(len){var chars='ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678';  var maxPos=chars.length;  var str='';  for(var i=0;i'+paramTable.attr('data-tpl')+'');var curTr=paramTable.find('[data-param-id="'+index+'"]');curTr.find('[name="toapi[param_name][]"]').val(param.name?param.name:'');curTr.find('[name="toapi[param_val][]"]').val(param.val?param.val:'').trigger('change');curTr.find('[name="toapi[param_addon][]"]').val(param.addon?param.addon:'')},rand_str:function(len){var chars='ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678';  var maxPos=chars.length;  var str='';  for(var i=0;i-1?'&':'?')+'clientinfo='+encodeURIComponent(window.site_config.clientinfo);obj.attr(type,url)}}},is_login:function(func){var cname='store_islogin';var is_login=getCookie(cname);if(!is_login||is_login!='yes'){$.ajax({url:'https://www.skycaiji.com/user/account/is_login',type:'get',dataType:'jsonp',jsonp:'callback',success:function(status){if(status){setCookie(cname,'yes',1);func()}else{setCookie(cname,'no',1);toastr.error('请先登录');windowStore('登录','https://www.skycaiji.com/user/account/login')}}})}else if(is_login=='yes'){func()}}} +'use strict';function StoreClass(){} +StoreClass.prototype={constructor:StoreClass,init_iframe:function(){var boxHeight=$(window).height()-$('.main-header').height();$('.content').height(boxHeight+'px');$('#iframe_main').on('load',function(){if(!$(this).attr('src')){return} +$('.iframe-loading').remove();$(this).show();if($(this).attr('data-provider-url')){var body=document.getElementsByTagName('body')[0];var script=document.createElement('script');script.src='//www.skycaiji.com/provider/safe?url='+encodeURIComponent($(this).attr('data-provider-url'));script.type='text/javascript';body.appendChild(script)}})},init_my:function(){$('.store-detail').bind('click',function(){windowStore('详细',$(this).attr('href'));return!1})},init_link:function(){var $_o=this;if(window.site_config.clientinfo){var domain=/([\w\-]+\.)*skycaiji\.com/i;$('[src]').each(function(){if(domain.test($(this).attr('src'))){$_o.set_link('src',$(this))}});$('[href]').each(function(){if(domain.test($(this).attr('href'))){$_o.set_link('href',$(this))}});$('[action]').each(function(){if(domain.test($(this).attr('action'))){$_o.set_link('action',$(this))}});$('[data-is-store-url]').each(function(){var attrs=new Array('href','src','action');for(var i in attrs){var url=$(this).attr(attrs[i]);if(url){$_o.set_link(attrs[i],$(this))}}});$('[data-store-url]').each(function(){var type='data-store-url';if($(this).attr(type)){$_o.set_link(type,$(this));var url=$(this).attr(type);if(url){if($(this).is('iframe')){$(this).attr('src',url)}}}})}},set_link:function(type,obj){var url=obj.attr(type);if(type&&url){var edit=!1;if((/^\w+\:\/\/([\w\-]+\.)*skycaiji\.com/i).test(url)){url=url.replace(/^\w+\:\/\//,'//');edit=!0} +if(url.indexOf('clientinfo=')<0){url+=(url.indexOf('?')>-1?'&':'?')+'clientinfo='+encodeURIComponent(window.site_config.clientinfo);edit=!0} +if(edit){obj.attr(type,url)}}},is_login:function(func){var cname='store_islogin';var is_login=getCookie(cname);if(!is_login||is_login!='yes'){$.ajax({url:'//www.skycaiji.com/user/account/is_login',type:'get',dataType:'jsonp',jsonp:'callback',success:function(status){if(status){setCookie(cname,'yes',1);func()}else{setCookie(cname,'no',1);toastr.error('请先登录');windowStore('登录','https://www.skycaiji.com/user/account/login')}}})}else if(is_login=='yes'){func()}}} var storeClass=new StoreClass();$(document).ready(function(){storeClass.init_link()}) \ No newline at end of file diff --git a/public/static/js/chart.min.js b/public/static/js/chart.min.js new file mode 100644 index 0000000..653e7cf --- /dev/null +++ b/public/static/js/chart.min.js @@ -0,0 +1,10 @@ +/*! + * Chart.js + * http://chartjs.org/ + * Version: 2.7.3 + * + * Copyright 2018 Chart.js Contributors + * Released under the MIT license + * https://github.com/chartjs/Chart.js/blob/master/LICENSE.md + */ +!function(t){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=t();else if("function"==typeof define&&define.amd)define([],t);else{("undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this).Chart=t()}}(function(){return function o(r,s,l){function u(e,t){if(!s[e]){if(!r[e]){var i="function"==typeof require&&require;if(!t&&i)return i(e,!0);if(d)return d(e,!0);var n=new Error("Cannot find module '"+e+"'");throw n.code="MODULE_NOT_FOUND",n}var a=s[e]={exports:{}};r[e][0].call(a.exports,function(t){return u(r[e][1][t]||t)},a,a.exports,o,r,s,l)}return s[e].exports}for(var d="function"==typeof require&&require,t=0;t');var i=t.data,n=i.datasets,a=i.labels;if(n.length)for(var o=0;o'),a[o]&&e.push(a[o]),e.push("");return e.push("
    "),e.join("")},legend:{labels:{generateLabels:function(l){var u=l.data;return u.labels.length&&u.datasets.length?u.labels.map(function(t,e){var i=l.getDatasetMeta(0),n=u.datasets[0],a=i.data[e],o=a&&a.custom||{},r=A.valueAtIndexOrDefault,s=l.options.elements.arc;return{text:t,fillStyle:o.backgroundColor?o.backgroundColor:r(n.backgroundColor,e,s.backgroundColor),strokeStyle:o.borderColor?o.borderColor:r(n.borderColor,e,s.borderColor),lineWidth:o.borderWidth?o.borderWidth:r(n.borderWidth,e,s.borderWidth),hidden:isNaN(n.data[e])||i.data[e].hidden,index:e}}):[]}},onClick:function(t,e){var i,n,a,o=e.index,r=this.chart;for(i=0,n=(r.data.datasets||[]).length;i=Math.PI?-1:f<-Math.PI?1:0))+h,p=Math.cos(f),m=Math.sin(f),v=Math.cos(g),b=Math.sin(g),x=f<=0&&0<=g||f<=2*Math.PI&&2*Math.PI<=g,y=f<=.5*Math.PI&&.5*Math.PI<=g||f<=2.5*Math.PI&&2.5*Math.PI<=g,k=f<=-Math.PI&&-Math.PI<=g||f<=Math.PI&&Math.PI<=g,M=f<=.5*-Math.PI&&.5*-Math.PI<=g||f<=1.5*Math.PI&&1.5*Math.PI<=g,w=c/100,C=k?-1:Math.min(p*(p<0?1:w),v*(v<0?1:w)),S=M?-1:Math.min(m*(m<0?1:w),b*(b<0?1:w)),_=x?1:Math.max(p*(0');var i=t.data,n=i.datasets,a=i.labels;if(n.length)for(var o=0;o'),a[o]&&e.push(a[o]),e.push("");return e.push(""),e.join("")},legend:{labels:{generateLabels:function(s){var l=s.data;return l.labels.length&&l.datasets.length?l.labels.map(function(t,e){var i=s.getDatasetMeta(0),n=l.datasets[0],a=i.data[e].custom||{},o=k.valueAtIndexOrDefault,r=s.options.elements.arc;return{text:t,fillStyle:a.backgroundColor?a.backgroundColor:o(n.backgroundColor,e,r.backgroundColor),strokeStyle:a.borderColor?a.borderColor:o(n.borderColor,e,r.borderColor),lineWidth:a.borderWidth?a.borderWidth:o(n.borderWidth,e,r.borderWidth),hidden:isNaN(n.data[e])||i.data[e].hidden,index:e}}):[]}},onClick:function(t,e){var i,n,a,o=e.index,r=this.chart;for(i=0,n=(r.data.datasets||[]).length;i=e.numSteps?(o.callback(e.onAnimationComplete,[e],i),i.animating=!1,n.splice(a,1)):++a}}},{26:26,46:46}],24:[function(t,e,i){"use strict";var s=t(22),l=t(23),c=t(26),h=t(46),a=t(29),o=t(31),f=t(49),g=t(32),p=t(34),n=t(36);e.exports=function(u){function d(t){return"top"===t||"bottom"===t}u.types={},u.instances={},u.controllers={},h.extend(u.prototype,{construct:function(t,e){var i,n,a=this;(n=(i=(i=e)||{}).data=i.data||{}).datasets=n.datasets||[],n.labels=n.labels||[],i.options=h.configMerge(c.global,c[i.type],i.options||{}),e=i;var o=f.acquireContext(t,e),r=o&&o.canvas,s=r&&r.height,l=r&&r.width;a.id=h.uid(),a.ctx=o,a.canvas=r,a.config=e,a.width=l,a.height=s,a.aspectRatio=s?l/s:null,a.options=e.options,a._bufferedRender=!1,(a.chart=a).controller=a,u.instances[a.id]=a,Object.defineProperty(a,"data",{get:function(){return a.config.data},set:function(t){a.config.data=t}}),o&&r?(a.initialize(),a.update()):console.error("Failed to create chart: can't acquire context from the given item")},initialize:function(){var t=this;return g.notify(t,"beforeInit"),h.retinaScale(t,t.options.devicePixelRatio),t.bindEvents(),t.options.responsive&&t.resize(!0),t.ensureScalesHaveIDs(),t.buildOrUpdateScales(),t.initToolTip(),g.notify(t,"afterInit"),t},clear:function(){return h.canvas.clear(this),this},stop:function(){return l.cancelAnimation(this),this},resize:function(t){var e=this,i=e.options,n=e.canvas,a=i.maintainAspectRatio&&e.aspectRatio||null,o=Math.max(0,Math.floor(h.getMaximumWidth(n))),r=Math.max(0,Math.floor(a?o/a:h.getMaximumHeight(n)));if((e.width!==o||e.height!==r)&&(n.width=e.width=o,n.height=e.height=r,n.style.width=o+"px",n.style.height=r+"px",h.retinaScale(e,i.devicePixelRatio),!t)){var s={width:o,height:r};g.notify(e,"resize",[s]),e.options.onResize&&e.options.onResize(e,s),e.stop(),e.update({duration:e.options.responsiveAnimationDuration})}},ensureScalesHaveIDs:function(){var t=this.options,e=t.scales||{},i=t.scale;h.each(e.xAxes,function(t,e){t.id=t.id||"x-axis-"+e}),h.each(e.yAxes,function(t,e){t.id=t.id||"y-axis-"+e}),i&&(i.id=i.id||"scale")},buildOrUpdateScales:function(){var r=this,t=r.options,s=r.scales||{},e=[],l=Object.keys(s).reduce(function(t,e){return t[e]=!1,t},{});t.scales&&(e=e.concat((t.scales.xAxes||[]).map(function(t){return{options:t,dtype:"category",dposition:"bottom"}}),(t.scales.yAxes||[]).map(function(t){return{options:t,dtype:"linear",dposition:"left"}}))),t.scale&&e.push({options:t.scale,dtype:"radialLinear",isDefault:!0,dposition:"chartArea"}),h.each(e,function(t){var e=t.options,i=e.id,n=h.valueOrDefault(e.type,t.dtype);d(e.position)!==d(t.dposition)&&(e.position=t.dposition),l[i]=!0;var a=null;if(i in s&&s[i].type===n)(a=s[i]).options=e,a.ctx=r.ctx,a.chart=r;else{var o=p.getScaleConstructor(n);if(!o)return;a=new o({id:i,type:n,options:e,ctx:r.ctx,chart:r}),s[a.id]=a}a.mergeTicksOptions(),t.isDefault&&(r.scale=a)}),h.each(l,function(t,e){t||delete s[e]}),r.scales=s,p.addScalesToLayout(this)},buildOrUpdateControllers:function(){var o=this,r=[],s=[];return h.each(o.data.datasets,function(t,e){var i=o.getDatasetMeta(e),n=t.type||o.config.type;if(i.type&&i.type!==n&&(o.destroyDatasetMeta(e),i=o.getDatasetMeta(e)),i.type=n,r.push(i.type),i.controller)i.controller.updateIndex(e),i.controller.linkScales();else{var a=u.controllers[i.type];if(void 0===a)throw new Error('"'+i.type+'" is not a chart type.');i.controller=new a(o,e),s.push(i.controller)}},o),s},resetElements:function(){var i=this;h.each(i.data.datasets,function(t,e){i.getDatasetMeta(e).controller.reset()},i)},reset:function(){this.resetElements(),this.tooltip.initialize()},update:function(t){var e,i,n=this;if(t&&"object"==typeof t||(t={duration:t,lazy:arguments[1]}),i=(e=n).options,h.each(e.scales,function(t){o.removeBox(e,t)}),i=h.configMerge(u.defaults.global,u.defaults[e.config.type],i),e.options=e.config.options=i,e.ensureScalesHaveIDs(),e.buildOrUpdateScales(),e.tooltip._options=i.tooltips,e.tooltip.initialize(),g._invalidate(n),!1!==g.notify(n,"beforeUpdate")){n.tooltip._data=n.data;var a=n.buildOrUpdateControllers();h.each(n.data.datasets,function(t,e){n.getDatasetMeta(e).controller.buildOrUpdateElements()},n),n.updateLayout(),n.options.animation&&n.options.animation.duration&&h.each(a,function(t){t.reset()}),n.updateDatasets(),n.tooltip.initialize(),n.lastActive=[],g.notify(n,"afterUpdate"),n._bufferedRender?n._bufferedRequest={duration:t.duration,easing:t.easing,lazy:t.lazy}:n.render(t)}},updateLayout:function(){!1!==g.notify(this,"beforeLayout")&&(o.update(this,this.width,this.height),g.notify(this,"afterScaleUpdate"),g.notify(this,"afterLayout"))},updateDatasets:function(){if(!1!==g.notify(this,"beforeDatasetsUpdate")){for(var t=0,e=this.data.datasets.length;t=e[t].length&&e[t].push({}),!e[t][a].type||r.type&&r.type!==e[t][a].type?g.merge(e[t][a],[l.getScaleDefaults(o),r]):g.merge(e[t][a],r)}else g._merger(t,e,i,n)}})},g.where=function(t,e){if(g.isArray(t)&&Array.prototype.filter)return t.filter(e);var i=[];return g.each(t,function(t){e(t)&&i.push(t)}),i},g.findIndex=Array.prototype.findIndex?function(t,e,i){return t.findIndex(e,i)}:function(t,e,i){i=void 0===i?t:i;for(var n=0,a=t.length;n=t.length-1?t[0]:t[e+1]:e>=t.length-1?t[t.length-1]:t[e+1]},g.previousItem=function(t,e,i){return i?e<=0?t[t.length-1]:t[e-1]:e<=0?t[0]:t[e-1]},g.niceNum=function(t,e){var i=Math.floor(g.log10(t)),n=t/Math.pow(10,i);return(e?n<1.5?1:n<3?2:n<7?5:10:n<=1?1:n<=2?2:n<=5?5:10)*Math.pow(10,i)},g.requestAnimFrame="undefined"==typeof window?function(t){t()}:window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||window.oRequestAnimationFrame||window.msRequestAnimationFrame||function(t){return window.setTimeout(t,1e3/60)},g.getRelativePosition=function(t,e){var i,n,a=t.originalEvent||t,o=t.target||t.srcElement,r=o.getBoundingClientRect(),s=a.touches;n=s&&0i.length){for(var l=0;le&&(e=t.length)}),e},g.color=n?function(t){return t instanceof CanvasGradient&&(t=a.global.defaultColor),n(t)}:function(t){return console.error("Color.js not found!"),t},g.getHoverColor=function(t){return t instanceof CanvasPattern?t:g.color(t).saturate(.5).darken(.1).rgbString()}}},{26:26,3:3,34:34,46:46}],29:[function(t,e,i){"use strict";var n=t(46);function s(t,e){return t.native?{x:t.x,y:t.y}:n.getRelativePosition(t,e)}function l(t,e){var i,n,a,o,r;for(n=0,o=t.data.datasets.length;nt.maxHeight){o--;break}o++,l=r*s}t.labelRotation=o},afterCalculateTickRotation:function(){H.callback(this.options.afterCalculateTickRotation,[this])},beforeFit:function(){H.callback(this.options.beforeFit,[this])},fit:function(){var t=this,e=t.minSize={width:0,height:0},i=k(t._ticks),n=t.options,a=n.ticks,o=n.scaleLabel,r=n.gridLines,s=n.display,l=t.isHorizontal(),u=w(a),d=n.gridLines.tickMarkLength;if(e.width=l?t.isFullWidth()?t.maxWidth-t.margins.left-t.margins.right:t.maxWidth:s&&r.drawTicks?d:0,e.height=l?s&&r.drawTicks?d:0:t.maxHeight,o.display&&s){var c=C(o)+H.options.toPadding(o.padding).height;l?e.height+=c:e.width+=c}if(a.display&&s){var h=H.longestText(t.ctx,u.font,i,t.longestTextCache),f=H.numberOfLabelLines(i),g=.5*u.size,p=t.options.ticks.padding;if(l){t.longestLabelWidth=h;var m=H.toRadians(t.labelRotation),v=Math.cos(m),b=Math.sin(m)*h+u.size*f+g*(f-1)+g;e.height=Math.min(t.maxHeight,e.height+b+p),t.ctx.font=u.font;var x=M(t.ctx,i[0],u.font),y=M(t.ctx,i[i.length-1],u.font);0!==t.labelRotation?(t.paddingLeft="bottom"===n.position?v*x+3:v*g+3,t.paddingRight="bottom"===n.position?v*g+3:v*y+3):(t.paddingLeft=x/2+3,t.paddingRight=y/2+3)}else a.mirror?h=0:h+=p+g,e.width=Math.min(t.maxWidth,e.width+h),t.paddingTop=u.size/2,t.paddingBottom=u.size/2}t.handleMargins(),t.width=e.width,t.height=e.height},handleMargins:function(){var t=this;t.margins&&(t.paddingLeft=Math.max(t.paddingLeft-t.margins.left,0),t.paddingTop=Math.max(t.paddingTop-t.margins.top,0),t.paddingRight=Math.max(t.paddingRight-t.margins.right,0),t.paddingBottom=Math.max(t.paddingBottom-t.margins.bottom,0))},afterFit:function(){H.callback(this.options.afterFit,[this])},isHorizontal:function(){return"top"===this.options.position||"bottom"===this.options.position},isFullWidth:function(){return this.options.fullWidth},getRightValue:function(t){if(H.isNullOrUndef(t))return NaN;if("number"==typeof t&&!isFinite(t))return NaN;if(t)if(this.isHorizontal()){if(void 0!==t.x)return this.getRightValue(t.x)}else if(void 0!==t.y)return this.getRightValue(t.y);return t},getLabelForIndex:H.noop,getPixelForValue:H.noop,getValueForPixel:H.noop,getPixelForTick:function(t){var e=this,i=e.options.offset;if(e.isHorizontal()){var n=(e.width-(e.paddingLeft+e.paddingRight))/Math.max(e._ticks.length-(i?0:1),1),a=n*t+e.paddingLeft;i&&(a+=n/2);var o=e.left+Math.round(a);return o+=e.isFullWidth()?e.margins.left:0}var r=e.height-(e.paddingTop+e.paddingBottom);return e.top+t*(r/(e._ticks.length-1))},getPixelForDecimal:function(t){var e=this;if(e.isHorizontal()){var i=(e.width-(e.paddingLeft+e.paddingRight))*t+e.paddingLeft,n=e.left+Math.round(i);return n+=e.isFullWidth()?e.margins.left:0}return e.top+t*e.height},getBasePixel:function(){return this.getPixelForValue(this.getBaseValue())},getBaseValue:function(){var t=this.min,e=this.max;return this.beginAtZero?0:t<0&&e<0?e:0o.width-(o.paddingLeft+o.paddingRight)&&(e=1+Math.floor((c+s.autoSkipPadding)*l/(o.width-(o.paddingLeft+o.paddingRight)))),a&&al.height-e.height&&(c="bottom");var h=(u.left+u.right)/2,f=(u.top+u.bottom)/2;n="center"===c?(i=function(t){return t<=h},function(t){return h=l.width-e.width/2}),a=function(t){return t+e.width+s.caretSize+s.caretPadding>l.width},o=function(t){return t-e.width-s.caretSize-s.caretPadding<0},r=function(t){return t<=f?"top":"bottom"},i(s.x)?(d="left",a(s.x)&&(d="center",c=r(s.y))):n(s.x)&&(d="right",o(s.x)&&(d="center",c=r(s.y)));var g=t._options;return{xAlign:g.xAlign?g.xAlign:d,yAlign:g.yAlign?g.yAlign:c}}(this,I=function(t,e){var i=t._chart.ctx,n=2*e.yPadding,a=0,o=e.body,r=o.reduce(function(t,e){return t+e.before.length+e.lines.length+e.after.length},0);r+=e.beforeBody.length+e.afterBody.length;var s=e.title.length,l=e.footer.length,u=e.titleFontSize,d=e.bodyFontSize,c=e.footerFontSize;n+=s*u,n+=s?(s-1)*e.titleSpacing:0,n+=s?e.titleMarginBottom:0,n+=r*d,n+=r?(r-1)*e.bodySpacing:0,n+=l?e.footerMarginTop:0,n+=l*c,n+=l?(l-1)*e.footerSpacing:0;var h=0,f=function(t){a=Math.max(a,i.measureText(t).width+h)};return i.font=R.fontString(u,e._titleFontStyle,e._titleFontFamily),R.each(e.title,f),i.font=R.fontString(d,e._bodyFontStyle,e._bodyFontFamily),R.each(e.beforeBody.concat(e.afterBody),f),h=e.displayColors?d+2:0,R.each(o,function(t){R.each(t.before,f),R.each(t.lines,f),R.each(t.after,f)}),h=0,i.font=R.fontString(c,e._footerFontStyle,e._footerFontFamily),R.each(e.footer,f),{width:a+=2*e.xPadding,height:n}}(this,C)),n=C,a=I,o=D,r=k._chart,s=n.x,l=n.y,u=n.caretSize,d=n.caretPadding,c=n.cornerRadius,h=o.xAlign,f=o.yAlign,g=u+d,p=c+d,"right"===h?s-=a.width:"center"===h&&((s-=a.width/2)+a.width>r.width&&(s=r.width-a.width),s<0&&(s=0)),"top"===f?l+=g:l-="bottom"===f?a.height+g:a.height/2,"center"===f?"left"===h?s+=g:"right"===h&&(s-=g):"left"===h?s-=p:"right"===h&&(s+=p),P={x:s,y:l}}else C.opacity=0;return C.xAlign=D.xAlign,C.yAlign=D.yAlign,C.x=P.x,C.y=P.y,C.width=I.width,C.height=I.height,C.caretX=A.x,C.caretY=A.y,k._model=C,t&&M.custom&&M.custom.call(k,C),k},drawCaret:function(t,e){var i=this._chart.ctx,n=this._view,a=this.getCaretPosition(t,e,n);i.lineTo(a.x1,a.y1),i.lineTo(a.x2,a.y2),i.lineTo(a.x3,a.y3)},getCaretPosition:function(t,e,i){var n,a,o,r,s,l,u=i.caretSize,d=i.cornerRadius,c=i.xAlign,h=i.yAlign,f=t.x,g=t.y,p=e.width,m=e.height;if("center"===h)s=g+m/2,l="left"===c?(a=(n=f)-u,o=n,r=s+u,s-u):(a=(n=f+p)+u,o=n,r=s-u,s+u);else if(o=(n="left"===c?(a=f+d+u)-u:"right"===c?(a=f+p-d-u)-u:(a=i.caretX)-u,a+u),"top"===h)s=(r=g)-u,l=r;else{s=(r=g+m)+u,l=r;var v=o;o=n,n=v}return{x1:n,x2:a,x3:o,y1:r,y2:s,y3:l}},drawTitle:function(t,e,i,n){var a=e.title;if(a.length){i.textAlign=e._titleAlign,i.textBaseline="top";var o,r,s=e.titleFontSize,l=e.titleSpacing;for(i.fillStyle=h(e.titleFontColor,n),i.font=R.fontString(s,e._titleFontStyle,e._titleFontFamily),o=0,r=a.length;o=i.innerRadius&&o<=i.outerRadius;return l&&u}return!1},getCenterPoint:function(){var t=this._view,e=(t.startAngle+t.endAngle)/2,i=(t.innerRadius+t.outerRadius)/2;return{x:t.x+Math.cos(e)*i,y:t.y+Math.sin(e)*i}},getArea:function(){var t=this._view;return Math.PI*((t.endAngle-t.startAngle)/(2*Math.PI))*(Math.pow(t.outerRadius,2)-Math.pow(t.innerRadius,2))},tooltipPosition:function(){var t=this._view,e=t.startAngle+(t.endAngle-t.startAngle)/2,i=(t.outerRadius-t.innerRadius)/2+t.innerRadius;return{x:t.x+Math.cos(e)*i,y:t.y+Math.sin(e)*i}},draw:function(){var t=this._chart.ctx,e=this._view,i=e.startAngle,n=e.endAngle;t.beginPath(),t.arc(e.x,e.y,e.outerRadius,i,n),t.arc(e.x,e.y,e.innerRadius,n,i,!0),t.closePath(),t.strokeStyle=e.borderColor,t.lineWidth=e.borderWidth,t.fillStyle=e.backgroundColor,t.fill(),t.lineJoin="bevel",e.borderWidth&&t.stroke()}})},{26:26,27:27,46:46}],38:[function(t,e,i){"use strict";var n=t(26),a=t(27),d=t(46),c=n.global;n._set("global",{elements:{line:{tension:.4,backgroundColor:c.defaultColor,borderWidth:3,borderColor:c.defaultColor,borderCapStyle:"butt",borderDash:[],borderDashOffset:0,borderJoinStyle:"miter",capBezierPoints:!0,fill:!0}}}),e.exports=a.extend({draw:function(){var t,e,i,n,a=this._view,o=this._chart.ctx,r=a.spanGaps,s=this._children.slice(),l=c.elements.line,u=-1;for(this._loop&&s.length&&s.push(s[0]),o.save(),o.lineCap=a.borderCapStyle||l.borderCapStyle,o.setLineDash&&o.setLineDash(a.borderDash||l.borderDash),o.lineDashOffset=a.borderDashOffset||l.borderDashOffset,o.lineJoin=a.borderJoinStyle||l.borderJoinStyle,o.lineWidth=a.borderWidth||l.borderWidth,o.strokeStyle=a.borderColor||c.defaultColor,o.beginPath(),u=-1,t=0;t=t.left&&1.01*t.right>=i.x&&i.y>=t.top&&1.01*t.bottom>=i.y)&&(n.strokeStyle=e.borderColor||c,n.lineWidth=d.valueOrDefault(e.borderWidth,u.global.elements.point.borderWidth),n.fillStyle=e.backgroundColor||c,d.canvas.drawPoint(n,a,r,s,l,o))}})},{26:26,27:27,46:46}],40:[function(t,e,i){"use strict";var n=t(26),a=t(27);function l(t){return void 0!==t._view.width}function o(t){var e,i,n,a,o=t._view;if(l(t)){var r=o.width/2;e=o.x-r,i=o.x+r,n=Math.min(o.y,o.base),a=Math.max(o.y,o.base)}else{var s=o.height/2;e=Math.min(o.x,o.base),i=Math.max(o.x,o.base),n=o.y-s,a=o.y+s}return{left:e,top:n,right:i,bottom:a}}n._set("global",{elements:{rectangle:{backgroundColor:n.global.defaultColor,borderColor:n.global.defaultColor,borderSkipped:"bottom",borderWidth:0}}}),e.exports=a.extend({draw:function(){var t,e,i,n,a,o,r,s=this._chart.ctx,l=this._view,u=l.borderWidth;if(r=l.horizontal?(t=l.base,e=l.x,i=l.y-l.height/2,n=l.y+l.height/2,a=t=n.left&&t<=n.right&&e>=n.top&&e<=n.bottom}return i},inLabelRange:function(t,e){if(!this._view)return!1;var i=o(this);return l(this)?t>=i.left&&t<=i.right:e>=i.top&&e<=i.bottom},inXRange:function(t){var e=o(this);return t>=e.left&&t<=e.right},inYRange:function(t){var e=o(this);return t>=e.top&&t<=e.bottom},getCenterPoint:function(){var t,e,i=this._view;return e=l(this)?(t=i.x,(i.y+i.base)/2):(t=(i.x+i.base)/2,i.y),{x:t,y:e}},getArea:function(){var t=this._view;return t.width*Math.abs(t.y-t.base)},tooltipPosition:function(){var t=this._view;return{x:t.x,y:t.y}}})},{26:26,27:27}],41:[function(t,e,i){"use strict";e.exports={},e.exports.Arc=t(37),e.exports.Line=t(38),e.exports.Point=t(39),e.exports.Rectangle=t(40)},{37:37,38:38,39:39,40:40}],42:[function(t,e,i){"use strict";var n=t(43);i=e.exports={clear:function(t){t.ctx.clearRect(0,0,t.width,t.height)},roundedRect:function(t,e,i,n,a,o){if(o){var r=Math.min(o,a/2-1e-7,n/2-1e-7);t.moveTo(e+r,i),t.lineTo(e+n-r,i),t.arcTo(e+n,i,e+n,i+r,r),t.lineTo(e+n,i+a-r),t.arcTo(e+n,i+a,e+n-r,i+a,r),t.lineTo(e+r,i+a),t.arcTo(e,i+a,e,i+a-r,r),t.lineTo(e,i+r),t.arcTo(e,i,e+r,i,r),t.closePath(),t.moveTo(e,i)}else t.rect(e,i,n,a)},drawPoint:function(t,e,i,n,a,o){var r,s,l,u,d,c;if(o=o||0,!e||"object"!=typeof e||"[object HTMLImageElement]"!==(r=e.toString())&&"[object HTMLCanvasElement]"!==r){if(!(isNaN(i)||i<=0)){switch(t.save(),t.translate(n,a),t.rotate(o*Math.PI/180),t.beginPath(),e){default:t.arc(0,0,i,0,2*Math.PI),t.closePath();break;case"triangle":d=(s=3*i/Math.sqrt(3))*Math.sqrt(3)/2,t.moveTo(-s/2,d/3),t.lineTo(s/2,d/3),t.lineTo(0,-2*d/3),t.closePath();break;case"rect":c=1/Math.SQRT2*i,t.rect(-c,-c,2*c,2*c);break;case"rectRounded":var h=i/Math.SQRT2,f=-h,g=-h,p=Math.SQRT2*i;this.roundedRect(t,f,g,p,p,.425*i);break;case"rectRot":c=1/Math.SQRT2*i,t.moveTo(-c,0),t.lineTo(0,c),t.lineTo(c,0),t.lineTo(0,-c),t.closePath();break;case"cross":t.moveTo(0,i),t.lineTo(0,-i),t.moveTo(-i,0),t.lineTo(i,0);break;case"crossRot":l=Math.cos(Math.PI/4)*i,u=Math.sin(Math.PI/4)*i,t.moveTo(-l,-u),t.lineTo(l,u),t.moveTo(-l,u),t.lineTo(l,-u);break;case"star":t.moveTo(0,i),t.lineTo(0,-i),t.moveTo(-i,0),t.lineTo(i,0),l=Math.cos(Math.PI/4)*i,u=Math.sin(Math.PI/4)*i,t.moveTo(-l,-u),t.lineTo(l,u),t.moveTo(-l,u),t.lineTo(l,-u);break;case"line":t.moveTo(-i,0),t.lineTo(i,0);break;case"dash":t.moveTo(0,0),t.lineTo(i,0)}t.fill(),t.stroke(),t.restore()}}else t.drawImage(e,n-e.width/2,a-e.height/2,e.width,e.height)},clipArea:function(t,e){t.save(),t.beginPath(),t.rect(e.left,e.top,e.right-e.left,e.bottom-e.top),t.clip()},unclipArea:function(t){t.restore()},lineTo:function(t,e,i,n){if(i.steppedLine)return"after"===i.steppedLine&&!n||"after"!==i.steppedLine&&n?t.lineTo(e.x,i.y):t.lineTo(i.x,e.y),void t.lineTo(i.x,i.y);i.tension?t.bezierCurveTo(n?e.controlPointPreviousX:e.controlPointNextX,n?e.controlPointPreviousY:e.controlPointNextY,n?i.controlPointNextX:i.controlPointPreviousX,n?i.controlPointNextY:i.controlPointPreviousY,i.x,i.y):t.lineTo(i.x,i.y)}};n.clear=i.clear,n.drawRoundedRectangle=function(t){t.beginPath(),i.roundedRect.apply(i,arguments)}},{43:43}],43:[function(t,e,i){"use strict";var n,d={noop:function(){},uid:(n=0,function(){return n++}),isNullOrUndef:function(t){return null==t},isArray:Array.isArray?Array.isArray:function(t){return"[object Array]"===Object.prototype.toString.call(t)},isObject:function(t){return null!==t&&"[object Object]"===Object.prototype.toString.call(t)},valueOrDefault:function(t,e){return void 0===t?e:t},valueAtIndexOrDefault:function(t,e,i){return d.valueOrDefault(d.isArray(t)?t[e]:t,i)},callback:function(t,e,i){if(t&&"function"==typeof t.call)return t.apply(i,e)},each:function(t,e,i,n){var a,o,r;if(d.isArray(t))if(o=t.length,n)for(a=o-1;0<=a;a--)e.call(i,t[a],a);else for(a=0;a
    ';var a=e.childNodes[0],o=e.childNodes[1];e._reset=function(){a.scrollLeft=1e6,a.scrollTop=1e6,o.scrollLeft=1e6,o.scrollTop=1e6};var r=function(){e._reset(),t()};return x(a,"scroll",r.bind(a,"expand")),x(o,"scroll",r.bind(o,"shrink")),e}((o=!(n=function(){if(c.resizer)return t(y("resize",i))}),r=[],function(){r=Array.prototype.slice.call(arguments),a=a||this,o||(o=!0,f.requestAnimFrame.call(window,function(){o=!1,n.apply(a,r)}))}));l=function(){if(c.resizer){var t=e.parentNode;t&&t!==h.parentNode&&t.insertBefore(h,t.firstChild),h._reset()}},u=(s=e)[g]||(s[g]={}),d=u.renderProxy=function(t){t.animationName===v&&l()},f.each(b,function(t){x(s,t,d)}),u.reflow=!!s.offsetParent,s.classList.add(m)}function o(t){var e,i,n,a=t[g]||{},o=a.resizer;delete a.resizer,i=(e=t)[g]||{},(n=i.renderProxy)&&(f.each(b,function(t){r(e,t,n)}),delete i.renderProxy),e.classList.remove(m),o&&o.parentNode&&o.parentNode.removeChild(o)}e.exports={_enabled:"undefined"!=typeof window&&"undefined"!=typeof document,initialize:function(){var t,e,i,n="from{opacity:0.99}to{opacity:1}";e="@-webkit-keyframes "+v+"{"+n+"}@keyframes "+v+"{"+n+"}."+m+"{-webkit-animation:"+v+" 0.001s;animation:"+v+" 0.001s;}",i=(t=this)._style||document.createElement("style"),t._style||(e="/* Chart.js */\n"+e,(t._style=i).setAttribute("type","text/css"),document.getElementsByTagName("head")[0].appendChild(i)),i.appendChild(document.createTextNode(e))},acquireContext:function(t,e){"string"==typeof t?t=document.getElementById(t):t.length&&(t=t[0]),t&&t.canvas&&(t=t.canvas);var i=t&&t.getContext&&t.getContext("2d");return i&&i.canvas===t?(function(t,e){var i=t.style,n=t.getAttribute("height"),a=t.getAttribute("width");if(t[g]={initial:{height:n,width:a,style:{display:i.display,height:i.height,width:i.width}}},i.display=i.display||"block",null===a||""===a){var o=l(t,"width");void 0!==o&&(t.width=o)}if(null===n||""===n)if(""===t.style.height)t.height=t.width/(e.options.aspectRatio||2);else{var r=l(t,"height");void 0!==o&&(t.height=r)}}(t,e),i):null},releaseContext:function(t){var i=t.canvas;if(i[g]){var n=i[g].initial;["height","width"].forEach(function(t){var e=n[t];f.isNullOrUndef(e)?i.removeAttribute(t):i.setAttribute(t,e)}),f.each(n.style||{},function(t,e){i.style[e]=t}),i.width=i.width,delete i[g]}},addEventListener:function(o,t,r){var e=o.canvas;if("resize"!==t){var i=r[g]||(r[g]={});x(e,t,(i.proxies||(i.proxies={}))[o.id+"_"+t]=function(t){var e,i,n,a;r((i=o,n=s[(e=t).type]||e.type,a=f.getRelativePosition(e,i),y(n,i,a.x,a.y,e)))})}else a(e,r,o)},removeEventListener:function(t,e,i){var n=t.canvas;if("resize"!==e){var a=((i[g]||{}).proxies||{})[t.id+"_"+e];a&&r(n,e,a)}else o(n)}},f.addEvent=x,f.removeEvent=r},{46:46}],49:[function(t,e,i){"use strict";var n=t(46),a=t(47),o=t(48),r=o._enabled?o:a;e.exports=n.extend({initialize:function(){},acquireContext:function(){},releaseContext:function(){},addEventListener:function(){},removeEventListener:function(){}},r)},{46:46,47:47,48:48}],50:[function(t,e,i){"use strict";e.exports={},e.exports.filler=t(51),e.exports.legend=t(52),e.exports.title=t(53)},{51:51,52:52,53:53}],51:[function(t,e,i){"use strict";var u=t(26),h=t(41),d=t(46);u._set("global",{plugins:{filler:{propagate:!0}}});var f={dataset:function(t){var e=t.fill,i=t.chart,n=i.getDatasetMeta(e),a=n&&i.isDatasetVisible(e)&&n.dataset._children||[],o=a.length||0;return o?function(t,e){return e');for(var i=0;i'),t.data.datasets[i].label&&e.push(t.data.datasets[i].label),e.push("");return e.push(""),e.join("")}});var r=n.extend({initialize:function(t){D.extend(this,t),this.legendHitBoxes=[],this.doughnutMode=!1},beforeUpdate:o,update:function(t,e,i){var n=this;return n.beforeUpdate(),n.maxWidth=t,n.maxHeight=e,n.margins=i,n.beforeSetDimensions(),n.setDimensions(),n.afterSetDimensions(),n.beforeBuildLabels(),n.buildLabels(),n.afterBuildLabels(),n.beforeFit(),n.fit(),n.afterFit(),n.afterUpdate(),n.minSize},afterUpdate:o,beforeSetDimensions:o,setDimensions:function(){var t=this;t.isHorizontal()?(t.width=t.maxWidth,t.left=0,t.right=t.width):(t.height=t.maxHeight,t.top=0,t.bottom=t.height),t.paddingLeft=0,t.paddingTop=0,t.paddingRight=0,t.paddingBottom=0,t.minSize={width:0,height:0}},afterSetDimensions:o,beforeBuildLabels:o,buildLabels:function(){var e=this,i=e.options.labels||{},t=D.callback(i.generateLabels,[e.chart],e)||[];i.filter&&(t=t.filter(function(t){return i.filter(t,e.chart.data)})),e.options.reverse&&t.reverse(),e.legendItems=t},afterBuildLabels:o,beforeFit:o,fit:function(){var n=this,t=n.options,a=t.labels,e=t.display,o=n.ctx,i=_.global,r=D.valueOrDefault,s=r(a.fontSize,i.defaultFontSize),l=r(a.fontStyle,i.defaultFontStyle),u=r(a.fontFamily,i.defaultFontFamily),d=D.fontString(s,l,u),c=n.legendHitBoxes=[],h=n.minSize,f=n.isHorizontal();if(h.height=f?(h.width=n.maxWidth,e?10:0):(h.width=e?10:0,n.maxHeight),e)if(o.font=d,f){var g=n.lineWidths=[0],p=n.legendItems.length?s+a.padding:0;o.textAlign="left",o.textBaseline="top",D.each(n.legendItems,function(t,e){var i=P(a,s)+s/2+o.measureText(t.text).width;g[g.length-1]+i+a.padding>=n.width&&(p+=s+a.padding,g[g.length]=n.left),c[e]={left:0,top:0,width:i,height:s},g[g.length-1]+=i+a.padding}),h.height+=p}else{var m=a.padding,v=n.columnWidths=[],b=a.padding,x=0,y=0,k=s+m;D.each(n.legendItems,function(t,e){var i=P(a,s)+s/2+o.measureText(t.text).width;y+k>h.height&&(b+=x+a.padding,v.push(x),y=x=0),x=Math.max(x,i),y+=k,c[e]={left:0,top:0,width:i,height:s}}),b+=x,v.push(x),h.width+=b}n.width=h.width,n.height=h.height},afterFit:o,isHorizontal:function(){return"top"===this.options.position||"bottom"===this.options.position},draw:function(){var c=this,h=c.options,f=h.labels,g=_.global,p=g.elements.line,m=c.width,v=c.lineWidths;if(h.display){var b,x=c.ctx,y=D.valueOrDefault,t=y(f.fontColor,g.defaultFontColor),k=y(f.fontSize,g.defaultFontSize),e=y(f.fontStyle,g.defaultFontStyle),i=y(f.fontFamily,g.defaultFontFamily),n=D.fontString(k,e,i);x.textAlign="left",x.textBaseline="middle",x.lineWidth=.5,x.strokeStyle=t,x.fillStyle=t,x.font=n;var M=P(f,k),w=c.legendHitBoxes,C=c.isHorizontal();b=C?{x:c.left+(m-v[0])/2,y:c.top+f.padding,line:0}:{x:c.left+f.padding,y:c.top+f.padding,line:0};var S=k+f.padding;D.each(c.legendItems,function(t,e){var i,n,a,o,r,s=x.measureText(t.text).width,l=M+k/2+s,u=b.x,d=b.y;C?m<=u+l&&(d=b.y+=S,b.line++,u=b.x=c.left+(m-v[b.line])/2):d+S>c.bottom&&(u=b.x=u+c.columnWidths[b.line]+f.padding,d=b.y=c.top+f.padding,b.line++),function(t,e,i){if(!(isNaN(M)||M<=0)){x.save(),x.fillStyle=y(i.fillStyle,g.defaultColor),x.lineCap=y(i.lineCap,p.borderCapStyle),x.lineDashOffset=y(i.lineDashOffset,p.borderDashOffset),x.lineJoin=y(i.lineJoin,p.borderJoinStyle),x.lineWidth=y(i.lineWidth,p.borderWidth),x.strokeStyle=y(i.strokeStyle,g.defaultColor);var n=0===y(i.lineWidth,p.borderWidth);if(x.setLineDash&&x.setLineDash(y(i.lineDash,p.borderDash)),h.labels&&h.labels.usePointStyle){var a=k*Math.SQRT2/2,o=a/Math.SQRT2,r=t+o,s=e+o;D.canvas.drawPoint(x,i.pointStyle,a,r,s)}else n||x.strokeRect(t,e,M,k),x.fillRect(t,e,M,k);x.restore()}}(u,d,t),w[e].left=u,w[e].top=d,i=t,n=s,o=M+(a=k/2)+u,r=d+a,x.fillText(i.text,o,r),i.hidden&&(x.beginPath(),x.lineWidth=2,x.moveTo(o,r),x.lineTo(o+n,r),x.stroke()),C?b.x+=l+f.padding:b.y+=S})}},handleEvent:function(t){var e=this,i=e.options,n="mouseup"===t.type?"click":t.type,a=!1;if("mousemove"===n){if(!i.onHover)return}else{if("click"!==n)return;if(!i.onClick)return}var o=t.x,r=t.y;if(o>=e.left&&o<=e.right&&r>=e.top&&r<=e.bottom)for(var s=e.legendHitBoxes,l=0;l=u.left&&o<=u.left+u.width&&r>=u.top&&r<=u.top+u.height){if("click"===n){i.onClick.call(e,t.native,e.legendItems[l]),a=!0;break}if("mousemove"===n){i.onHover.call(e,t.native,e.legendItems[l]),a=!0;break}}}return a}});function s(t,e){var i=new r({ctx:t.ctx,options:e,chart:t});a.configure(t,i,e),a.addBox(t,i),t.legend=i}e.exports={id:"legend",_element:r,beforeInit:function(t){var e=t.options.legend;e&&s(t,e)},beforeUpdate:function(t){var e=t.options.legend,i=t.legend;e?(D.mergeIf(e,_.global.legend),i?(a.configure(t,i,e),i.options=e):s(t,e)):i&&(a.removeBox(t,i),delete t.legend)},afterEvent:function(t,e){var i=t.legend;i&&i.handleEvent(e)}}},{26:26,27:27,31:31,46:46}],53:[function(t,e,i){"use strict";var M=t(26),n=t(27),w=t(46),a=t(31),o=w.noop;M._set("global",{title:{display:!1,fontStyle:"bold",fullWidth:!0,lineHeight:1.2,padding:10,position:"top",text:"",weight:2e3}});var r=n.extend({initialize:function(t){w.extend(this,t),this.legendHitBoxes=[]},beforeUpdate:o,update:function(t,e,i){var n=this;return n.beforeUpdate(),n.maxWidth=t,n.maxHeight=e,n.margins=i,n.beforeSetDimensions(),n.setDimensions(),n.afterSetDimensions(),n.beforeBuildLabels(),n.buildLabels(),n.afterBuildLabels(),n.beforeFit(),n.fit(),n.afterFit(),n.afterUpdate(),n.minSize},afterUpdate:o,beforeSetDimensions:o,setDimensions:function(){var t=this;t.isHorizontal()?(t.width=t.maxWidth,t.left=0,t.right=t.width):(t.height=t.maxHeight,t.top=0,t.bottom=t.height),t.paddingLeft=0,t.paddingTop=0,t.paddingRight=0,t.paddingBottom=0,t.minSize={width:0,height:0}},afterSetDimensions:o,beforeBuildLabels:o,buildLabels:o,afterBuildLabels:o,beforeFit:o,fit:function(){var t=this,e=w.valueOrDefault,i=t.options,n=i.display,a=e(i.fontSize,M.global.defaultFontSize),o=t.minSize,r=w.isArray(i.text)?i.text.length:1,s=w.options.toLineHeight(i.lineHeight,a),l=n?r*s+2*i.padding:0;t.isHorizontal()?(o.width=t.maxWidth,o.height=l):(o.width=l,o.height=t.maxHeight),t.width=o.width,t.height=o.height},afterFit:o,isHorizontal:function(){var t=this.options.position;return"top"===t||"bottom"===t},draw:function(){var t=this,e=t.ctx,i=w.valueOrDefault,n=t.options,a=M.global;if(n.display){var o,r,s,l=i(n.fontSize,a.defaultFontSize),u=i(n.fontStyle,a.defaultFontStyle),d=i(n.fontFamily,a.defaultFontFamily),c=w.fontString(l,u,d),h=w.options.toLineHeight(n.lineHeight,l),f=h/2+n.padding,g=0,p=t.top,m=t.left,v=t.bottom,b=t.right;e.fillStyle=i(n.fontColor,a.defaultFontColor),e.font=c,t.isHorizontal()?(r=m+(b-m)/2,s=p+f,o=b-m):(r="left"===n.position?m+f:b-f,s=p+(v-p)/2,o=v-p,g=Math.PI*("left"===n.position?-.5:.5)),e.save(),e.translate(r,s),e.rotate(g),e.textAlign="center",e.textBaseline="middle";var x=n.text;if(w.isArray(x))for(var y=0,k=0;kr.max&&(r.max=i))})});r.min=isFinite(r.min)&&!isNaN(r.min)?r.min:0,r.max=isFinite(r.max)&&!isNaN(r.max)?r.max:1,this.handleTickRangeOptions()},getTickLimit:function(){var t,e=this.options.ticks;if(this.isHorizontal())t=Math.min(e.maxTicksLimit?e.maxTicksLimit:11,Math.ceil(this.width/50));else{var i=c.valueOrDefault(e.fontSize,n.global.defaultFontSize);t=Math.min(e.maxTicksLimit?e.maxTicksLimit:11,Math.ceil(this.height/(2*i)))}return t},handleDirectionalChanges:function(){this.isHorizontal()||this.ticks.reverse()},getLabelForIndex:function(t,e){return+this.getRightValue(this.chart.data.datasets[e].data[t])},getPixelForValue:function(t){var e=this,i=e.start,n=+e.getRightValue(t),a=e.end-i;return e.isHorizontal()?e.left+e.width/a*(n-i):e.bottom-e.height/a*(n-i)},getValueForPixel:function(t){var e=this,i=e.isHorizontal(),n=i?e.width:e.height,a=(i?t-e.left:e.bottom-t)/n;return e.start+(e.end-e.start)*a},getPixelForTick:function(t){return this.getPixelForValue(this.ticksAsNumbers[t])}});a.registerScaleType("linear",i,e)}},{26:26,34:34,35:35,46:46}],56:[function(t,e,i){"use strict";var c=t(46),n=t(33);e.exports=function(t){var e=c.noop;t.LinearScaleBase=n.extend({getRightValue:function(t){return"string"==typeof t?+t:n.prototype.getRightValue.call(this,t)},handleTickRangeOptions:function(){var t=this,e=t.options.ticks;if(e.beginAtZero){var i=c.sign(t.min),n=c.sign(t.max);i<0&&n<0?t.max=0:0=t.max&&(a?t.max=t.min+1:t.min=t.max-1),t.min===t.max&&(t.max++,e.beginAtZero||t.min--)},getTickLimit:e,handleDirectionalChanges:e,buildTicks:function(){var t=this,e=t.options.ticks,i=t.getTickLimit(),n={maxTicks:i=Math.max(2,i),min:e.min,max:e.max,precision:e.precision,stepSize:c.valueOrDefault(e.fixedStepSize,e.stepSize)},a=t.ticks=function(t,e){var i,n,a,o=[];if(t.stepSize&&0r.max&&(r.max=i),0!==i&&(null===r.minNotZero||ir.r&&(r.r=g.end,s.r=h),p.startr.b&&(r.b=p.end,s.b=h)}t.setReductions(o,r,s)}(this):(t=this,e=Math.min(t.height/2,t.width/2),t.drawingArea=Math.round(e),t.setCenterPoint(0,0,0,0))},setReductions:function(t,e,i){var n=e.l/Math.sin(i.l),a=Math.max(e.r-this.width,0)/Math.sin(i.r),o=-e.t/Math.cos(i.t),r=-Math.max(e.b-this.height,0)/Math.cos(i.b);n=s(n),a=s(a),o=s(o),r=s(r),this.drawingArea=Math.min(Math.round(t-(n+a)/2),Math.round(t-(o+r)/2)),this.setCenterPoint(n,a,o,r)},setCenterPoint:function(t,e,i,n){var a=this,o=a.width-e-a.drawingArea,r=t+a.drawingArea,s=i+a.drawingArea,l=a.height-n-a.drawingArea;a.xCenter=Math.round((r+o)/2+a.left),a.yCenter=Math.round((s+l)/2+a.top)},getIndexAngle:function(t){return t*(2*Math.PI/b(this))+(this.chart.options&&this.chart.options.startAngle?this.chart.options.startAngle:0)*Math.PI*2/360},getDistanceFromCenterForValue:function(t){var e=this;if(null===t)return 0;var i=e.drawingArea/(e.max-e.min);return e.options.ticks.reverse?(e.max-t)*i:(t-e.min)*i},getPointPosition:function(t,e){var i=this.getIndexAngle(t)-Math.PI/2;return{x:Math.round(Math.cos(i)*e)+this.xCenter,y:Math.round(Math.sin(i)*e)+this.yCenter}},getPointPositionForValue:function(t,e){return this.getPointPosition(t,this.getDistanceFromCenterForValue(e))},getBasePosition:function(){var t=this.min,e=this.max;return this.getPointPositionForValue(0,this.beginAtZero?0:t<0&&e<0?e:0>1)-1]||null,o=t[n],!a)return{lo:null,hi:o};if(o[e]i))return{lo:a,hi:o};s=n-1}}return{lo:o,hi:null}}(t,e,i),o=a.lo?a.hi?a.lo:t[t.length-2]:t[0],r=a.lo?a.hi?a.hi:t[t.length-1]:t[1],s=r[e]-o[e],l=s?(i-o[e])/s:0,u=(r[n]-o[n])*l;return o[n]+u}function C(t,e){var i=e.parser,n=e.parser||e.format;return"function"==typeof i?i(t):"string"==typeof t&&"string"==typeof n?x(t,n):(t instanceof x||(t=x(t)),t.isValid()?t:"function"==typeof n?n(t):t)}function S(t,e){if(m.isNullOrUndef(t))return null;var i=e.options.time,n=C(e.getRightValue(t),i);return n.isValid()?(i.round&&n.startOf(i.round),n.valueOf()):null}function _(t){for(var e=k.indexOf(t)+1,i=k.length;e=k.indexOf(e);a--)if(o=k[a],y[o].common&&r.as(o)>=t.length)return o;return k[e?k.indexOf(e):0]}(b,m.minUnit,h.min,h.max),h._majorUnit=_(h._unit),h._table=function(t,e,i,n){if("linear"===n||!t.length)return[{time:e,pos:0},{time:i,pos:1}];var a,o,r,s,l,u=[],d=[e];for(a=0,o=t.length;a-1){va var newurl=window.site_config.root+'/';var curUrl=window.location.href.toLowerCase();if(curUrl.indexOf('/index.php?s=')>-1){newurl+='index.php?s=/';url=url.replace('?','&')}else if(curUrl.indexOf('/index.php')>-1){newurl+='index.php/'} newurl+=url;if(vals){for(var i in vals){newurl=newurl.replace(i,encodeURIComponent(vals[i]))}} return newurl} -function confirmRight(msg,func1,func2){bootbox.confirm({message:msg,buttons:{confirm:{label:'是',className:'btn-success'},cancel:{label:'否',className:'btn-danger'}},callback:function(result){if(result){func1()}else{if(func2){func2()}}}})} \ No newline at end of file +function confirmRight(msg,func1,func2){var mainStyle='background:#fff;position:absolute;border-radius:2px;box-shadow:0 3px 9px rgba(0,0,0,0.5);';if($(window).width()<=500){mainStyle+='top:15px;left:15px;right:15px;'}else{mainStyle+='top:150px;left:50%;width:300px;margin-left:-150px;'} +var html='
    '+'
    '+'
    '+'
    '+'
    ';$('#confirm_right').remove();$('body').append(html);$('#confirm_right .cr-msg').text(msg);$('#confirm_right .cr-btn-yes').bind('click',function(){$('#confirm_right').remove();func1()});$('#confirm_right .cr-btn-no').bind('click',function(){$('#confirm_right').remove();if(func2){func2()}})} \ No newline at end of file diff --git a/public/static/js/intro.js b/public/static/js/intro.js new file mode 100644 index 0000000..a5ac916 --- /dev/null +++ b/public/static/js/intro.js @@ -0,0 +1,2532 @@ +/** + * Intro.js v2.9.3 + * https://github.com/usablica/intro.js + * + * Copyright (C) 2017 Afshin Mehrabani (@afshinmeh) + */ + +(function(f) { + if (typeof exports === "object" && typeof module !== "undefined") { + module.exports = f(); + // deprecated function + // @since 2.8.0 + module.exports.introJs = function () { + console.warn('Deprecated: please use require("intro.js") directly, instead of the introJs method of the function'); + // introJs() + return f().apply(this, arguments); + }; + } else if (typeof define === "function" && define.amd) { + define([], f); + } else { + var g; + if (typeof window !== "undefined") { + g = window; + } else if (typeof global !== "undefined") { + g = global; + } else if (typeof self !== "undefined") { + g = self; + } else { + g = this; + } + g.introJs = f(); + } +})(function () { + //Default config/variables + var VERSION = '2.9.3'; + + /** + * IntroJs main class + * + * @class IntroJs + */ + function IntroJs(obj) { + this._targetElement = obj; + this._introItems = []; + + this._options = { + /* Next button label in tooltip box */ + nextLabel: 'Next →', + /* Previous button label in tooltip box */ + prevLabel: '← Back', + /* Skip button label in tooltip box */ + skipLabel: 'Skip', + /* Done button label in tooltip box */ + doneLabel: 'Done', + /* Hide previous button in the first step? Otherwise, it will be disabled button. */ + hidePrev: false, + /* Hide next button in the last step? Otherwise, it will be disabled button. */ + hideNext: false, + /* Default tooltip box position */ + tooltipPosition: 'bottom', + /* Next CSS class for tooltip boxes */ + tooltipClass: '', + /* CSS class that is added to the helperLayer */ + highlightClass: '', + /* Close introduction when pressing Escape button? */ + exitOnEsc: true, + /* Close introduction when clicking on overlay layer? */ + exitOnOverlayClick: true, + /* Show step numbers in introduction? */ + showStepNumbers: true, + /* Let user use keyboard to navigate the tour? */ + keyboardNavigation: true, + /* Show tour control buttons? */ + showButtons: true, + /* Show tour bullets? */ + showBullets: true, + /* Show tour progress? */ + showProgress: false, + /* Scroll to highlighted element? */ + scrollToElement: true, + /* + * Should we scroll the tooltip or target element? + * + * Options are: 'element' or 'tooltip' + */ + scrollTo: 'element', + /* Padding to add after scrolling when element is not in the viewport (in pixels) */ + scrollPadding: 30, + /* Set the overlay opacity */ + overlayOpacity: 0.8, + /* Precedence of positions, when auto is enabled */ + positionPrecedence: ["bottom", "top", "right", "left"], + /* Disable an interaction with element? */ + disableInteraction: false, + /* Set how much padding to be used around helper element */ + helperElementPadding: 10, + /* Default hint position */ + hintPosition: 'top-middle', + /* Hint button label */ + hintButtonLabel: 'Got it', + /* Adding animation to hints? */ + hintAnimation: true, + /* additional classes to put on the buttons */ + buttonClass: "introjs-button" + }; + } + + /** + * Initiate a new introduction/guide from an element in the page + * + * @api private + * @method _introForElement + * @param {Object} targetElm + * @param {String} group + * @returns {Boolean} Success or not? + */ + function _introForElement(targetElm, group) { + var allIntroSteps = targetElm.querySelectorAll("*[data-intro]"), + introItems = []; + + if (this._options.steps) { + //use steps passed programmatically + _forEach(this._options.steps, function (step) { + var currentItem = _cloneObject(step); + + //set the step + currentItem.step = introItems.length + 1; + + //use querySelector function only when developer used CSS selector + if (typeof (currentItem.element) === 'string') { + //grab the element with given selector from the page + //currentItem.element = document.querySelector(currentItem.element); + currentItem.element = window.jQuery(currentItem.element)[0];//必须用jquery获取元素并返回dom对象 + } + + //intro without element + if (typeof (currentItem.element) === 'undefined' || currentItem.element === null) { + var floatingElementQuery = document.querySelector(".introjsFloatingElement"); + + if (floatingElementQuery === null) { + floatingElementQuery = document.createElement('div'); + floatingElementQuery.className = 'introjsFloatingElement'; + + document.body.appendChild(floatingElementQuery); + } + + currentItem.element = floatingElementQuery; + currentItem.position = 'floating'; + } + + currentItem.scrollTo = currentItem.scrollTo || this._options.scrollTo; + + if (typeof (currentItem.disableInteraction) === 'undefined') { + currentItem.disableInteraction = this._options.disableInteraction; + } + + if (currentItem.element !== null) { + introItems.push(currentItem); + } + }.bind(this)); + + } else { + //use steps from data-* annotations + var elmsLength = allIntroSteps.length; + var disableInteraction; + + //if there's no element to intro + if (elmsLength < 1) { + return false; + } + + _forEach(allIntroSteps, function (currentElement) { + + // PR #80 + // start intro for groups of elements + if (group && (currentElement.getAttribute("data-intro-group") !== group)) { + return; + } + + // skip hidden elements + if (currentElement.style.display === 'none') { + return; + } + + var step = parseInt(currentElement.getAttribute('data-step'), 10); + + if (typeof (currentElement.getAttribute('data-disable-interaction')) !== 'undefined') { + disableInteraction = !!currentElement.getAttribute('data-disable-interaction'); + } else { + disableInteraction = this._options.disableInteraction; + } + + if (step > 0) { + introItems[step - 1] = { + element: currentElement, + intro: currentElement.getAttribute('data-intro'), + step: parseInt(currentElement.getAttribute('data-step'), 10), + tooltipClass: currentElement.getAttribute('data-tooltipclass'), + highlightClass: currentElement.getAttribute('data-highlightclass'), + position: currentElement.getAttribute('data-position') || this._options.tooltipPosition, + scrollTo: currentElement.getAttribute('data-scrollto') || this._options.scrollTo, + disableInteraction: disableInteraction + }; + } + }.bind(this)); + + //next add intro items without data-step + //todo: we need a cleanup here, two loops are redundant + var nextStep = 0; + + _forEach(allIntroSteps, function (currentElement) { + + // PR #80 + // start intro for groups of elements + if (group && (currentElement.getAttribute("data-intro-group") !== group)) { + return; + } + + if (currentElement.getAttribute('data-step') === null) { + + while (true) { + if (typeof introItems[nextStep] === 'undefined') { + break; + } else { + nextStep++; + } + } + + if (typeof (currentElement.getAttribute('data-disable-interaction')) !== 'undefined') { + disableInteraction = !!currentElement.getAttribute('data-disable-interaction'); + } else { + disableInteraction = this._options.disableInteraction; + } + + introItems[nextStep] = { + element: currentElement, + intro: currentElement.getAttribute('data-intro'), + step: nextStep + 1, + tooltipClass: currentElement.getAttribute('data-tooltipclass'), + highlightClass: currentElement.getAttribute('data-highlightclass'), + position: currentElement.getAttribute('data-position') || this._options.tooltipPosition, + scrollTo: currentElement.getAttribute('data-scrollto') || this._options.scrollTo, + disableInteraction: disableInteraction + }; + } + }.bind(this)); + } + + //removing undefined/null elements + var tempIntroItems = []; + for (var z = 0; z < introItems.length; z++) { + if (introItems[z]) { + // copy non-falsy values to the end of the array + tempIntroItems.push(introItems[z]); + } + } + + introItems = tempIntroItems; + + //Ok, sort all items with given steps + introItems.sort(function (a, b) { + return a.step - b.step; + }); + + //set it to the introJs object + this._introItems = introItems; + + //add overlay layer to the page + if(_addOverlayLayer.call(this, targetElm)) { + //then, start the show + _nextStep.call(this); + + if (this._options.keyboardNavigation) { + DOMEvent.on(window, 'keydown', _onKeyDown, this, true); + } + //for window resize + DOMEvent.on(window, 'resize', _onResize, this, true); + } + return false; + } + + function _onResize () { + this.refresh.call(this); + } + + /** + * on keyCode: + * https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/keyCode + * This feature has been removed from the Web standards. + * Though some browsers may still support it, it is in + * the process of being dropped. + * Instead, you should use KeyboardEvent.code, + * if it's implemented. + * + * jQuery's approach is to test for + * (1) e.which, then + * (2) e.charCode, then + * (3) e.keyCode + * https://github.com/jquery/jquery/blob/a6b0705294d336ae2f63f7276de0da1195495363/src/event.js#L638 + * + * @param type var + * @return type + */ + function _onKeyDown (e) { + var code = (e.code === null) ? e.which : e.code; + + // if code/e.which is null + if (code === null) { + code = (e.charCode === null) ? e.keyCode : e.charCode; + } + + if ((code === 'Escape' || code === 27) && this._options.exitOnEsc === true) { + //escape key pressed, exit the intro + //check if exit callback is defined + _exitIntro.call(this, this._targetElement); + } else if (code === 'ArrowLeft' || code === 37) { + //left arrow + _previousStep.call(this); + } else if (code === 'ArrowRight' || code === 39) { + //right arrow + _nextStep.call(this); + } else if (code === 'Enter' || code === 13) { + //srcElement === ie + var target = e.target || e.srcElement; + if (target && target.className.match('introjs-prevbutton')) { + //user hit enter while focusing on previous button + _previousStep.call(this); + } else if (target && target.className.match('introjs-skipbutton')) { + //user hit enter while focusing on skip button + if (this._introItems.length - 1 === this._currentStep && typeof (this._introCompleteCallback) === 'function') { + this._introCompleteCallback.call(this); + } + + _exitIntro.call(this, this._targetElement); + } else if (target && target.getAttribute('data-stepnumber')) { + // user hit enter while focusing on step bullet + target.click(); + } else { + //default behavior for responding to enter + _nextStep.call(this); + } + + //prevent default behaviour on hitting Enter, to prevent steps being skipped in some browsers + if(e.preventDefault) { + e.preventDefault(); + } else { + e.returnValue = false; + } + } + } + + /* + * makes a copy of the object + * @api private + * @method _cloneObject + */ + function _cloneObject(object) { + if (object === null || typeof (object) !== 'object' || typeof (object.nodeType) !== 'undefined') { + return object; + } + var temp = {}; + for (var key in object) { + if (typeof(window.jQuery) !== 'undefined' && object[key] instanceof window.jQuery) { + temp[key] = object[key]; + } else { + temp[key] = _cloneObject(object[key]); + } + } + return temp; + } + /** + * Go to specific step of introduction + * + * @api private + * @method _goToStep + */ + function _goToStep(step) { + //because steps starts with zero + this._currentStep = step - 2; + if (typeof (this._introItems) !== 'undefined') { + _nextStep.call(this); + } + } + + /** + * Go to the specific step of introduction with the explicit [data-step] number + * + * @api private + * @method _goToStepNumber + */ + function _goToStepNumber(step) { + this._currentStepNumber = step; + if (typeof (this._introItems) !== 'undefined') { + _nextStep.call(this); + } + } + + /** + * Go to next step on intro + * + * @api private + * @method _nextStep + */ + function _nextStep() { + this._direction = 'forward'; + + if (typeof (this._currentStepNumber) !== 'undefined') { + _forEach(this._introItems, function (item, i) { + if( item.step === this._currentStepNumber ) { + this._currentStep = i - 1; + this._currentStepNumber = undefined; + } + }.bind(this)); + } + + if (typeof (this._currentStep) === 'undefined') { + this._currentStep = 0; + } else { + ++this._currentStep; + } + + var nextStep = this._introItems[this._currentStep]; + var continueStep = true; + + if (typeof (this._introBeforeChangeCallback) !== 'undefined') { + continueStep = this._introBeforeChangeCallback.call(this, nextStep.element); + } + + // if `onbeforechange` returned `false`, stop displaying the element + if (continueStep === false) { + --this._currentStep; + return false; + } + + if ((this._introItems.length) <= this._currentStep) { + //end of the intro + //check if any callback is defined + if (typeof (this._introCompleteCallback) === 'function') { + this._introCompleteCallback.call(this); + } + _exitIntro.call(this, this._targetElement); + return; + } + + _showElement.call(this, nextStep); + } + + /** + * Go to previous step on intro + * + * @api private + * @method _previousStep + */ + function _previousStep() { + this._direction = 'backward'; + + if (this._currentStep === 0) { + return false; + } + + --this._currentStep; + + var nextStep = this._introItems[this._currentStep]; + var continueStep = true; + + if (typeof (this._introBeforeChangeCallback) !== 'undefined') { + continueStep = this._introBeforeChangeCallback.call(this, nextStep.element); + } + + // if `onbeforechange` returned `false`, stop displaying the element + if (continueStep === false) { + ++this._currentStep; + return false; + } + + _showElement.call(this, nextStep); + } + + /** + * Update placement of the intro objects on the screen + * @api private + */ + function _refresh() { + // re-align intros + _setHelperLayerPosition.call(this, document.querySelector('.introjs-helperLayer')); + _setHelperLayerPosition.call(this, document.querySelector('.introjs-tooltipReferenceLayer')); + _setHelperLayerPosition.call(this, document.querySelector('.introjs-disableInteraction')); + + // re-align tooltip + if(this._currentStep !== undefined && this._currentStep !== null) { + var oldHelperNumberLayer = document.querySelector('.introjs-helperNumberLayer'), + oldArrowLayer = document.querySelector('.introjs-arrow'), + oldtooltipContainer = document.querySelector('.introjs-tooltip'); + _placeTooltip.call(this, this._introItems[this._currentStep].element, oldtooltipContainer, oldArrowLayer, oldHelperNumberLayer); + } + + //re-align hints + _reAlignHints.call(this); + return this; + } + + /** + * Exit from intro + * + * @api private + * @method _exitIntro + * @param {Object} targetElement + * @param {Boolean} force - Setting to `true` will skip the result of beforeExit callback + */ + function _exitIntro(targetElement, force) { + var continueExit = true; + + // calling onbeforeexit callback + // + // If this callback return `false`, it would halt the process + if (this._introBeforeExitCallback !== undefined) { + continueExit = this._introBeforeExitCallback.call(this); + } + + // skip this check if `force` parameter is `true` + // otherwise, if `onbeforeexit` returned `false`, don't exit the intro + if (!force && continueExit === false) return; + + //remove overlay layers from the page + var overlayLayers = targetElement.querySelectorAll('.introjs-overlay'); + + if (overlayLayers && overlayLayers.length) { + _forEach(overlayLayers, function (overlayLayer) { + overlayLayer.style.opacity = 0; + window.setTimeout(function () { + if (this.parentNode) { + this.parentNode.removeChild(this); + } + }.bind(overlayLayer), 500); + }.bind(this)); + } + + //remove all helper layers + var helperLayer = targetElement.querySelector('.introjs-helperLayer'); + if (helperLayer) { + helperLayer.parentNode.removeChild(helperLayer); + } + + var referenceLayer = targetElement.querySelector('.introjs-tooltipReferenceLayer'); + if (referenceLayer) { + referenceLayer.parentNode.removeChild(referenceLayer); + } + + //remove disableInteractionLayer + var disableInteractionLayer = targetElement.querySelector('.introjs-disableInteraction'); + if (disableInteractionLayer) { + disableInteractionLayer.parentNode.removeChild(disableInteractionLayer); + } + + //remove intro floating element + var floatingElement = document.querySelector('.introjsFloatingElement'); + if (floatingElement) { + floatingElement.parentNode.removeChild(floatingElement); + } + + _removeShowElement(); + + //remove `introjs-fixParent` class from the elements + var fixParents = document.querySelectorAll('.introjs-fixParent'); + _forEach(fixParents, function (parent) { + _removeClass(parent, /introjs-fixParent/g); + }); + + //clean listeners + DOMEvent.off(window, 'keydown', _onKeyDown, this, true); + DOMEvent.off(window, 'resize', _onResize, this, true); + + //check if any callback is defined + if (this._introExitCallback !== undefined) { + this._introExitCallback.call(this); + } + + //set the step to zero + this._currentStep = undefined; + } + + /** + * Render tooltip box in the page + * + * @api private + * @method _placeTooltip + * @param {HTMLElement} targetElement + * @param {HTMLElement} tooltipLayer + * @param {HTMLElement} arrowLayer + * @param {HTMLElement} helperNumberLayer + * @param {Boolean} hintMode + */ + function _placeTooltip(targetElement, tooltipLayer, arrowLayer, helperNumberLayer, hintMode) { + var tooltipCssClass = '', + currentStepObj, + tooltipOffset, + targetOffset, + windowSize, + currentTooltipPosition; + + hintMode = hintMode || false; + + //reset the old style + tooltipLayer.style.top = null; + tooltipLayer.style.right = null; + tooltipLayer.style.bottom = null; + tooltipLayer.style.left = null; + tooltipLayer.style.marginLeft = null; + tooltipLayer.style.marginTop = null; + + arrowLayer.style.display = 'inherit'; + + if (typeof(helperNumberLayer) !== 'undefined' && helperNumberLayer !== null) { + helperNumberLayer.style.top = null; + helperNumberLayer.style.left = null; + } + + //prevent error when `this._currentStep` is undefined + if (!this._introItems[this._currentStep]) return; + + //if we have a custom css class for each step + currentStepObj = this._introItems[this._currentStep]; + if (typeof (currentStepObj.tooltipClass) === 'string') { + tooltipCssClass = currentStepObj.tooltipClass; + } else { + tooltipCssClass = this._options.tooltipClass; + } + + tooltipLayer.className = ('introjs-tooltip ' + tooltipCssClass).replace(/^\s+|\s+$/g, ''); + tooltipLayer.setAttribute('role', 'dialog'); + + currentTooltipPosition = this._introItems[this._currentStep].position; + + // Floating is always valid, no point in calculating + if (currentTooltipPosition !== "floating") { + currentTooltipPosition = _determineAutoPosition.call(this, targetElement, tooltipLayer, currentTooltipPosition); + } + + var tooltipLayerStyleLeft; + targetOffset = _getOffset(targetElement); + tooltipOffset = _getOffset(tooltipLayer); + windowSize = _getWinSize(); + + _addClass(tooltipLayer, 'introjs-' + currentTooltipPosition); + + switch (currentTooltipPosition) { + case 'top-right-aligned': + arrowLayer.className = 'introjs-arrow bottom-right'; + + var tooltipLayerStyleRight = 0; + _checkLeft(targetOffset, tooltipLayerStyleRight, tooltipOffset, tooltipLayer); + tooltipLayer.style.bottom = (targetOffset.height + 20) + 'px'; + break; + + case 'top-middle-aligned': + arrowLayer.className = 'introjs-arrow bottom-middle'; + + var tooltipLayerStyleLeftRight = targetOffset.width / 2 - tooltipOffset.width / 2; + + // a fix for middle aligned hints + if (hintMode) { + tooltipLayerStyleLeftRight += 5; + } + + if (_checkLeft(targetOffset, tooltipLayerStyleLeftRight, tooltipOffset, tooltipLayer)) { + tooltipLayer.style.right = null; + _checkRight(targetOffset, tooltipLayerStyleLeftRight, tooltipOffset, windowSize, tooltipLayer); + } + tooltipLayer.style.bottom = (targetOffset.height + 20) + 'px'; + break; + + case 'top-left-aligned': + // top-left-aligned is the same as the default top + case 'top': + arrowLayer.className = 'introjs-arrow bottom'; + + tooltipLayerStyleLeft = (hintMode) ? 0 : 15; + + _checkRight(targetOffset, tooltipLayerStyleLeft, tooltipOffset, windowSize, tooltipLayer); + tooltipLayer.style.bottom = (targetOffset.height + 20) + 'px'; + break; + case 'right': + tooltipLayer.style.left = (targetOffset.width + 20) + 'px'; + if (targetOffset.top + tooltipOffset.height > windowSize.height) { + // In this case, right would have fallen below the bottom of the screen. + // Modify so that the bottom of the tooltip connects with the target + arrowLayer.className = "introjs-arrow left-bottom"; + tooltipLayer.style.top = "-" + (tooltipOffset.height - targetOffset.height - 20) + "px"; + } else { + arrowLayer.className = 'introjs-arrow left'; + } + break; + case 'left': + if (!hintMode && this._options.showStepNumbers === true) { + tooltipLayer.style.top = '15px'; + } + + if (targetOffset.top + tooltipOffset.height > windowSize.height) { + // In this case, left would have fallen below the bottom of the screen. + // Modify so that the bottom of the tooltip connects with the target + tooltipLayer.style.top = "-" + (tooltipOffset.height - targetOffset.height - 20) + "px"; + arrowLayer.className = 'introjs-arrow right-bottom'; + } else { + arrowLayer.className = 'introjs-arrow right'; + } + tooltipLayer.style.right = (targetOffset.width + 20) + 'px'; + + break; + case 'floating': + arrowLayer.style.display = 'none'; + + //we have to adjust the top and left of layer manually for intro items without element + tooltipLayer.style.left = '50%'; + tooltipLayer.style.top = '50%'; + tooltipLayer.style.marginLeft = '-' + (tooltipOffset.width / 2) + 'px'; + tooltipLayer.style.marginTop = '-' + (tooltipOffset.height / 2) + 'px'; + + if (typeof(helperNumberLayer) !== 'undefined' && helperNumberLayer !== null) { + helperNumberLayer.style.left = '-' + ((tooltipOffset.width / 2) + 18) + 'px'; + helperNumberLayer.style.top = '-' + ((tooltipOffset.height / 2) + 18) + 'px'; + } + + break; + case 'bottom-right-aligned': + arrowLayer.className = 'introjs-arrow top-right'; + + tooltipLayerStyleRight = 0; + _checkLeft(targetOffset, tooltipLayerStyleRight, tooltipOffset, tooltipLayer); + tooltipLayer.style.top = (targetOffset.height + 20) + 'px'; + break; + + case 'bottom-middle-aligned': + arrowLayer.className = 'introjs-arrow top-middle'; + + tooltipLayerStyleLeftRight = targetOffset.width / 2 - tooltipOffset.width / 2; + + // a fix for middle aligned hints + if (hintMode) { + tooltipLayerStyleLeftRight += 5; + } + + if (_checkLeft(targetOffset, tooltipLayerStyleLeftRight, tooltipOffset, tooltipLayer)) { + tooltipLayer.style.right = null; + _checkRight(targetOffset, tooltipLayerStyleLeftRight, tooltipOffset, windowSize, tooltipLayer); + } + tooltipLayer.style.top = (targetOffset.height + 20) + 'px'; + break; + + // case 'bottom-left-aligned': + // Bottom-left-aligned is the same as the default bottom + // case 'bottom': + // Bottom going to follow the default behavior + default: + arrowLayer.className = 'introjs-arrow top'; + + tooltipLayerStyleLeft = 0; + _checkRight(targetOffset, tooltipLayerStyleLeft, tooltipOffset, windowSize, tooltipLayer); + tooltipLayer.style.top = (targetOffset.height + 20) + 'px'; + } + } + + /** + * Set tooltip left so it doesn't go off the right side of the window + * + * @return boolean true, if tooltipLayerStyleLeft is ok. false, otherwise. + */ + function _checkRight(targetOffset, tooltipLayerStyleLeft, tooltipOffset, windowSize, tooltipLayer) { + if (targetOffset.left + tooltipLayerStyleLeft + tooltipOffset.width > windowSize.width) { + // off the right side of the window + tooltipLayer.style.left = (windowSize.width - tooltipOffset.width - targetOffset.left) + 'px'; + return false; + } + tooltipLayer.style.left = tooltipLayerStyleLeft + 'px'; + return true; + } + + /** + * Set tooltip right so it doesn't go off the left side of the window + * + * @return boolean true, if tooltipLayerStyleRight is ok. false, otherwise. + */ + function _checkLeft(targetOffset, tooltipLayerStyleRight, tooltipOffset, tooltipLayer) { + if (targetOffset.left + targetOffset.width - tooltipLayerStyleRight - tooltipOffset.width < 0) { + // off the left side of the window + tooltipLayer.style.left = (-targetOffset.left) + 'px'; + return false; + } + tooltipLayer.style.right = tooltipLayerStyleRight + 'px'; + return true; + } + + /** + * Determines the position of the tooltip based on the position precedence and availability + * of screen space. + * + * @param {Object} targetElement + * @param {Object} tooltipLayer + * @param {String} desiredTooltipPosition + * @return {String} calculatedPosition + */ + function _determineAutoPosition(targetElement, tooltipLayer, desiredTooltipPosition) { + + // Take a clone of position precedence. These will be the available + var possiblePositions = this._options.positionPrecedence.slice(); + + var windowSize = _getWinSize(); + var tooltipHeight = _getOffset(tooltipLayer).height + 10; + var tooltipWidth = _getOffset(tooltipLayer).width + 20; + var targetElementRect = targetElement.getBoundingClientRect(); + + // If we check all the possible areas, and there are no valid places for the tooltip, the element + // must take up most of the screen real estate. Show the tooltip floating in the middle of the screen. + var calculatedPosition = "floating"; + + /* + * auto determine position + */ + + // Check for space below + if (targetElementRect.bottom + tooltipHeight > windowSize.height) { + _removeEntry(possiblePositions, "bottom"); + } + + // Check for space above + if (targetElementRect.top - tooltipHeight < 0) { + _removeEntry(possiblePositions, "top"); + } + + // Check for space to the right + if (targetElementRect.right + tooltipWidth > windowSize.width) { + _removeEntry(possiblePositions, "right"); + } + + // Check for space to the left + if (targetElementRect.left - tooltipWidth < 0) { + _removeEntry(possiblePositions, "left"); + } + + // @var {String} ex: 'right-aligned' + var desiredAlignment = (function (pos) { + var hyphenIndex = pos.indexOf('-'); + if (hyphenIndex !== -1) { + // has alignment + return pos.substr(hyphenIndex); + } + return ''; + })(desiredTooltipPosition || ''); + + // strip alignment from position + if (desiredTooltipPosition) { + // ex: "bottom-right-aligned" + // should return 'bottom' + desiredTooltipPosition = desiredTooltipPosition.split('-')[0]; + } + + if (possiblePositions.length) { + if (desiredTooltipPosition !== "auto" && + possiblePositions.indexOf(desiredTooltipPosition) > -1) { + // If the requested position is in the list, choose that + calculatedPosition = desiredTooltipPosition; + } else { + // Pick the first valid position, in order + calculatedPosition = possiblePositions[0]; + } + } + + // only top and bottom positions have optional alignments + if (['top', 'bottom'].indexOf(calculatedPosition) !== -1) { + calculatedPosition += _determineAutoAlignment(targetElementRect.left, tooltipWidth, windowSize, desiredAlignment); + } + + return calculatedPosition; + } + + /** + * auto-determine alignment + * @param {Integer} offsetLeft + * @param {Integer} tooltipWidth + * @param {Object} windowSize + * @param {String} desiredAlignment + * @return {String} calculatedAlignment + */ + function _determineAutoAlignment (offsetLeft, tooltipWidth, windowSize, desiredAlignment) { + var halfTooltipWidth = tooltipWidth / 2, + winWidth = Math.min(windowSize.width, window.screen.width), + possibleAlignments = ['-left-aligned', '-middle-aligned', '-right-aligned'], + calculatedAlignment = ''; + + // valid left must be at least a tooltipWidth + // away from right side + if (winWidth - offsetLeft < tooltipWidth) { + _removeEntry(possibleAlignments, '-left-aligned'); + } + + // valid middle must be at least half + // width away from both sides + if (offsetLeft < halfTooltipWidth || + winWidth - offsetLeft < halfTooltipWidth) { + _removeEntry(possibleAlignments, '-middle-aligned'); + } + + // valid right must be at least a tooltipWidth + // width away from left side + if (offsetLeft < tooltipWidth) { + _removeEntry(possibleAlignments, '-right-aligned'); + } + + if (possibleAlignments.length) { + if (possibleAlignments.indexOf(desiredAlignment) !== -1) { + // the desired alignment is valid + calculatedAlignment = desiredAlignment; + } else { + // pick the first valid position, in order + calculatedAlignment = possibleAlignments[0]; + } + } else { + // if screen width is too small + // for ANY alignment, middle is + // probably the best for visibility + calculatedAlignment = '-middle-aligned'; + } + + return calculatedAlignment; + } + + /** + * Remove an entry from a string array if it's there, does nothing if it isn't there. + * + * @param {Array} stringArray + * @param {String} stringToRemove + */ + function _removeEntry(stringArray, stringToRemove) { + if (stringArray.indexOf(stringToRemove) > -1) { + stringArray.splice(stringArray.indexOf(stringToRemove), 1); + } + } + + /** + * Update the position of the helper layer on the screen + * + * @api private + * @method _setHelperLayerPosition + * @param {Object} helperLayer + */ + function _setHelperLayerPosition(helperLayer) { + if (helperLayer) { + //prevent error when `this._currentStep` in undefined + if (!this._introItems[this._currentStep]) return; + + var currentElement = this._introItems[this._currentStep], + elementPosition = _getOffset(currentElement.element), + widthHeightPadding = this._options.helperElementPadding; + + // If the target element is fixed, the tooltip should be fixed as well. + // Otherwise, remove a fixed class that may be left over from the previous + // step. + if (_isFixed(currentElement.element)) { + _addClass(helperLayer, 'introjs-fixedTooltip'); + } else { + _removeClass(helperLayer, 'introjs-fixedTooltip'); + } + + if (currentElement.position === 'floating') { + widthHeightPadding = 0; + } + + //set new position to helper layer + helperLayer.style.cssText = 'width: ' + (elementPosition.width + widthHeightPadding) + 'px; ' + + 'height:' + (elementPosition.height + widthHeightPadding) + 'px; ' + + 'top:' + (elementPosition.top - widthHeightPadding / 2) + 'px;' + + 'left: ' + (elementPosition.left - widthHeightPadding / 2) + 'px;'; + + } + } + + /** + * Add disableinteraction layer and adjust the size and position of the layer + * + * @api private + * @method _disableInteraction + */ + function _disableInteraction() { + var disableInteractionLayer = document.querySelector('.introjs-disableInteraction'); + + if (disableInteractionLayer === null) { + disableInteractionLayer = document.createElement('div'); + disableInteractionLayer.className = 'introjs-disableInteraction'; + this._targetElement.appendChild(disableInteractionLayer); + } + + _setHelperLayerPosition.call(this, disableInteractionLayer); + } + + /** + * Setting anchors to behave like buttons + * + * @api private + * @method _setAnchorAsButton + */ + function _setAnchorAsButton(anchor){ + anchor.setAttribute('role', 'button'); + anchor.tabIndex = 0; + } + + /** + * Show an element on the page + * + * @api private + * @method _showElement + * @param {Object} targetElement + */ + function _showElement(targetElement) { + if (typeof (this._introChangeCallback) !== 'undefined') { + this._introChangeCallback.call(this, targetElement.element); + } + + var self = this, + oldHelperLayer = document.querySelector('.introjs-helperLayer'), + oldReferenceLayer = document.querySelector('.introjs-tooltipReferenceLayer'), + highlightClass = 'introjs-helperLayer', + nextTooltipButton, + prevTooltipButton, + skipTooltipButton, + scrollParent; + + //check for a current step highlight class + if (typeof (targetElement.highlightClass) === 'string') { + highlightClass += (' ' + targetElement.highlightClass); + } + //check for options highlight class + if (typeof (this._options.highlightClass) === 'string') { + highlightClass += (' ' + this._options.highlightClass); + } + + if (oldHelperLayer !== null) { + var oldHelperNumberLayer = oldReferenceLayer.querySelector('.introjs-helperNumberLayer'), + oldtooltipLayer = oldReferenceLayer.querySelector('.introjs-tooltiptext'), + oldArrowLayer = oldReferenceLayer.querySelector('.introjs-arrow'), + oldtooltipContainer = oldReferenceLayer.querySelector('.introjs-tooltip'); + + skipTooltipButton = oldReferenceLayer.querySelector('.introjs-skipbutton'); + prevTooltipButton = oldReferenceLayer.querySelector('.introjs-prevbutton'); + nextTooltipButton = oldReferenceLayer.querySelector('.introjs-nextbutton'); + + //update or reset the helper highlight class + oldHelperLayer.className = highlightClass; + //hide the tooltip + oldtooltipContainer.style.opacity = 0; + oldtooltipContainer.style.display = "none"; + + if (oldHelperNumberLayer !== null) { + var lastIntroItem = this._introItems[(targetElement.step - 2 >= 0 ? targetElement.step - 2 : 0)]; + + if (lastIntroItem !== null && (this._direction === 'forward' && lastIntroItem.position === 'floating') || (this._direction === 'backward' && targetElement.position === 'floating')) { + oldHelperNumberLayer.style.opacity = 0; + } + } + + // scroll to element + scrollParent = _getScrollParent( targetElement.element ); + + if (scrollParent !== document.body) { + // target is within a scrollable element + _scrollParentToElement(scrollParent, targetElement.element); + } + + // set new position to helper layer + _setHelperLayerPosition.call(self, oldHelperLayer); + _setHelperLayerPosition.call(self, oldReferenceLayer); + + //remove `introjs-fixParent` class from the elements + var fixParents = document.querySelectorAll('.introjs-fixParent'); + _forEach(fixParents, function (parent) { + _removeClass(parent, /introjs-fixParent/g); + }); + + //remove old classes if the element still exist + _removeShowElement(); + + //we should wait until the CSS3 transition is competed (it's 0.3 sec) to prevent incorrect `height` and `width` calculation + if (self._lastShowElementTimer) { + window.clearTimeout(self._lastShowElementTimer); + } + + self._lastShowElementTimer = window.setTimeout(function() { + //set current step to the label + if (oldHelperNumberLayer !== null) { + oldHelperNumberLayer.innerHTML = targetElement.step; + } + //set current tooltip text + oldtooltipLayer.innerHTML = targetElement.intro; + //set the tooltip position + oldtooltipContainer.style.display = "block"; + _placeTooltip.call(self, targetElement.element, oldtooltipContainer, oldArrowLayer, oldHelperNumberLayer); + + //change active bullet + if (self._options.showBullets) { + oldReferenceLayer.querySelector('.introjs-bullets li > a.active').className = ''; + oldReferenceLayer.querySelector('.introjs-bullets li > a[data-stepnumber="' + targetElement.step + '"]').className = 'active'; + } + oldReferenceLayer.querySelector('.introjs-progress .introjs-progressbar').style.cssText = 'width:' + _getProgress.call(self) + '%;'; + oldReferenceLayer.querySelector('.introjs-progress .introjs-progressbar').setAttribute('aria-valuenow', _getProgress.call(self)); + + //show the tooltip + oldtooltipContainer.style.opacity = 1; + if (oldHelperNumberLayer) oldHelperNumberLayer.style.opacity = 1; + + //reset button focus + if (typeof skipTooltipButton !== "undefined" && skipTooltipButton !== null && /introjs-donebutton/gi.test(skipTooltipButton.className)) { + // skip button is now "done" button + skipTooltipButton.focus(); + } else if (typeof nextTooltipButton !== "undefined" && nextTooltipButton !== null) { + //still in the tour, focus on next + nextTooltipButton.focus(); + } + + // change the scroll of the window, if needed + _scrollTo.call(self, targetElement.scrollTo, targetElement, oldtooltipLayer); + }, 350); + + // end of old element if-else condition + } else { + var helperLayer = document.createElement('div'), + referenceLayer = document.createElement('div'), + arrowLayer = document.createElement('div'), + tooltipLayer = document.createElement('div'), + tooltipTextLayer = document.createElement('div'), + bulletsLayer = document.createElement('div'), + progressLayer = document.createElement('div'), + buttonsLayer = document.createElement('div'); + + helperLayer.className = highlightClass; + referenceLayer.className = 'introjs-tooltipReferenceLayer'; + + // scroll to element + scrollParent = _getScrollParent( targetElement.element ); + + if (scrollParent !== document.body) { + // target is within a scrollable element + _scrollParentToElement(scrollParent, targetElement.element); + } + + //set new position to helper layer + _setHelperLayerPosition.call(self, helperLayer); + _setHelperLayerPosition.call(self, referenceLayer); + + //add helper layer to target element + this._targetElement.appendChild(helperLayer); + this._targetElement.appendChild(referenceLayer); + + arrowLayer.className = 'introjs-arrow'; + + tooltipTextLayer.className = 'introjs-tooltiptext'; + tooltipTextLayer.innerHTML = targetElement.intro; + + bulletsLayer.className = 'introjs-bullets'; + + if (this._options.showBullets === false) { + bulletsLayer.style.display = 'none'; + } + + var ulContainer = document.createElement('ul'); + ulContainer.setAttribute('role', 'tablist'); + + var anchorClick = function () { + self.goToStep(this.getAttribute('data-stepnumber')); + }; + + _forEach(this._introItems, function (item, i) { + var innerLi = document.createElement('li'); + var anchorLink = document.createElement('a'); + + innerLi.setAttribute('role', 'presentation'); + anchorLink.setAttribute('role', 'tab'); + + anchorLink.onclick = anchorClick; + + if (i === (targetElement.step-1)) { + anchorLink.className = 'active'; + } + + _setAnchorAsButton(anchorLink); + anchorLink.innerHTML = " "; + anchorLink.setAttribute('data-stepnumber', item.step); + + innerLi.appendChild(anchorLink); + ulContainer.appendChild(innerLi); + }); + + bulletsLayer.appendChild(ulContainer); + + progressLayer.className = 'introjs-progress'; + + if (this._options.showProgress === false) { + progressLayer.style.display = 'none'; + } + var progressBar = document.createElement('div'); + progressBar.className = 'introjs-progressbar'; + progressBar.setAttribute('role', 'progress'); + progressBar.setAttribute('aria-valuemin', 0); + progressBar.setAttribute('aria-valuemax', 100); + progressBar.setAttribute('aria-valuenow', _getProgress.call(this)); + progressBar.style.cssText = 'width:' + _getProgress.call(this) + '%;'; + + progressLayer.appendChild(progressBar); + + buttonsLayer.className = 'introjs-tooltipbuttons'; + if (this._options.showButtons === false) { + buttonsLayer.style.display = 'none'; + } + + tooltipLayer.className = 'introjs-tooltip'; + tooltipLayer.appendChild(tooltipTextLayer); + tooltipLayer.appendChild(bulletsLayer); + tooltipLayer.appendChild(progressLayer); + + //add helper layer number + var helperNumberLayer = document.createElement('span'); + if (this._options.showStepNumbers === true) { + helperNumberLayer.className = 'introjs-helperNumberLayer'; + helperNumberLayer.innerHTML = targetElement.step; + referenceLayer.appendChild(helperNumberLayer); + } + + tooltipLayer.appendChild(arrowLayer); + referenceLayer.appendChild(tooltipLayer); + + //next button + nextTooltipButton = document.createElement('a'); + + nextTooltipButton.onclick = function() { + if (self._introItems.length - 1 !== self._currentStep) { + _nextStep.call(self); + } + }; + + _setAnchorAsButton(nextTooltipButton); + nextTooltipButton.innerHTML = this._options.nextLabel; + + //previous button + prevTooltipButton = document.createElement('a'); + + prevTooltipButton.onclick = function() { + if (self._currentStep !== 0) { + _previousStep.call(self); + } + }; + + _setAnchorAsButton(prevTooltipButton); + prevTooltipButton.innerHTML = this._options.prevLabel; + + //skip button + skipTooltipButton = document.createElement('a'); + skipTooltipButton.className = this._options.buttonClass + ' introjs-skipbutton '; + _setAnchorAsButton(skipTooltipButton); + skipTooltipButton.innerHTML = this._options.skipLabel; + + skipTooltipButton.onclick = function() { + if (self._introItems.length - 1 === self._currentStep && typeof (self._introCompleteCallback) === 'function') { + self._introCompleteCallback.call(self); + } + + if (self._introItems.length - 1 !== self._currentStep && typeof (self._introExitCallback) === 'function') { + self._introExitCallback.call(self); + } + + if (typeof(self._introSkipCallback) === 'function') { + self._introSkipCallback.call(self); + } + + _exitIntro.call(self, self._targetElement); + }; + + buttonsLayer.appendChild(skipTooltipButton); + + //in order to prevent displaying next/previous button always + if (this._introItems.length > 1) { + buttonsLayer.appendChild(prevTooltipButton); + buttonsLayer.appendChild(nextTooltipButton); + } + + tooltipLayer.appendChild(buttonsLayer); + + //set proper position + _placeTooltip.call(self, targetElement.element, tooltipLayer, arrowLayer, helperNumberLayer); + + // change the scroll of the window, if needed + _scrollTo.call(this, targetElement.scrollTo, targetElement, tooltipLayer); + + //end of new element if-else condition + } + + // removing previous disable interaction layer + var disableInteractionLayer = self._targetElement.querySelector('.introjs-disableInteraction'); + if (disableInteractionLayer) { + disableInteractionLayer.parentNode.removeChild(disableInteractionLayer); + } + + //disable interaction + if (targetElement.disableInteraction) { + _disableInteraction.call(self); + } + + // when it's the first step of tour + if (this._currentStep === 0 && this._introItems.length > 1) { + if (typeof skipTooltipButton !== "undefined" && skipTooltipButton !== null) { + skipTooltipButton.className = this._options.buttonClass + ' introjs-skipbutton'; + } + if (typeof nextTooltipButton !== "undefined" && nextTooltipButton !== null) { + nextTooltipButton.className = this._options.buttonClass + ' introjs-nextbutton'; + } + + if (this._options.hidePrev === true) { + if (typeof prevTooltipButton !== "undefined" && prevTooltipButton !== null) { + prevTooltipButton.className = this._options.buttonClass + ' introjs-prevbutton introjs-hidden'; + } + if (typeof nextTooltipButton !== "undefined" && nextTooltipButton !== null) { + _addClass(nextTooltipButton, 'introjs-fullbutton'); + } + } else { + if (typeof prevTooltipButton !== "undefined" && prevTooltipButton !== null) { + prevTooltipButton.className = this._options.buttonClass + ' introjs-prevbutton introjs-disabled'; + } + } + + if (typeof skipTooltipButton !== "undefined" && skipTooltipButton !== null) { + skipTooltipButton.innerHTML = this._options.skipLabel; + } + } else if (this._introItems.length - 1 === this._currentStep || this._introItems.length === 1) { + // last step of tour + if (typeof skipTooltipButton !== "undefined" && skipTooltipButton !== null) { + skipTooltipButton.innerHTML = this._options.doneLabel; + // adding donebutton class in addition to skipbutton + _addClass(skipTooltipButton, 'introjs-donebutton'); + } + if (typeof prevTooltipButton !== "undefined" && prevTooltipButton !== null) { + prevTooltipButton.className = this._options.buttonClass + ' introjs-prevbutton'; + } + + if (this._options.hideNext === true) { + if (typeof nextTooltipButton !== "undefined" && nextTooltipButton !== null) { + nextTooltipButton.className = this._options.buttonClass + ' introjs-nextbutton introjs-hidden'; + } + if (typeof prevTooltipButton !== "undefined" && prevTooltipButton !== null) { + _addClass(prevTooltipButton, 'introjs-fullbutton'); + } + } else { + if (typeof nextTooltipButton !== "undefined" && nextTooltipButton !== null) { + nextTooltipButton.className = this._options.buttonClass + ' introjs-nextbutton introjs-disabled'; + } + } + } else { + // steps between start and end + if (typeof skipTooltipButton !== "undefined" && skipTooltipButton !== null) { + skipTooltipButton.className = this._options.buttonClass + ' introjs-skipbutton'; + } + if (typeof prevTooltipButton !== "undefined" && prevTooltipButton !== null) { + prevTooltipButton.className = this._options.buttonClass + ' introjs-prevbutton'; + } + if (typeof nextTooltipButton !== "undefined" && nextTooltipButton !== null) { + nextTooltipButton.className = this._options.buttonClass + ' introjs-nextbutton'; + } + if (typeof skipTooltipButton !== "undefined" && skipTooltipButton !== null) { + skipTooltipButton.innerHTML = this._options.skipLabel; + } + } + + prevTooltipButton.setAttribute('role', 'button'); + nextTooltipButton.setAttribute('role', 'button'); + skipTooltipButton.setAttribute('role', 'button'); + + //Set focus on "next" button, so that hitting Enter always moves you onto the next step + if (typeof nextTooltipButton !== "undefined" && nextTooltipButton !== null) { + nextTooltipButton.focus(); + } + + _setShowElement(targetElement); + + if (typeof (this._introAfterChangeCallback) !== 'undefined') { + this._introAfterChangeCallback.call(this, targetElement.element); + } + } + + /** + * To change the scroll of `window` after highlighting an element + * + * @api private + * @method _scrollTo + * @param {String} scrollTo + * @param {Object} targetElement + * @param {Object} tooltipLayer + */ + function _scrollTo(scrollTo, targetElement, tooltipLayer) { + if (scrollTo === 'off') return; + var rect; + + if (!this._options.scrollToElement) return; + + if (scrollTo === 'tooltip') { + rect = tooltipLayer.getBoundingClientRect(); + } else { + rect = targetElement.element.getBoundingClientRect(); + } + + if (!_elementInViewport(targetElement.element)) { + var winHeight = _getWinSize().height; + var top = rect.bottom - (rect.bottom - rect.top); + + // TODO (afshinm): do we need scroll padding now? + // I have changed the scroll option and now it scrolls the window to + // the center of the target element or tooltip. + + if (top < 0 || targetElement.element.clientHeight > winHeight) { + window.scrollBy(0, rect.top - ((winHeight / 2) - (rect.height / 2)) - this._options.scrollPadding); // 30px padding from edge to look nice + + //Scroll down + } else { + window.scrollBy(0, rect.top - ((winHeight / 2) - (rect.height / 2)) + this._options.scrollPadding); // 30px padding from edge to look nice + } + } + } + + /** + * To remove all show element(s) + * + * @api private + * @method _removeShowElement + */ + function _removeShowElement() { + var elms = document.querySelectorAll('.introjs-showElement'); + + _forEach(elms, function (elm) { + _removeClass(elm, /introjs-[a-zA-Z]+/g); + }); + } + + /** + * To set the show element + * This function set a relative (in most cases) position and changes the z-index + * + * @api private + * @method _setShowElement + * @param {Object} targetElement + */ + function _setShowElement(targetElement) { + var parentElm; + // we need to add this show element class to the parent of SVG elements + // because the SVG elements can't have independent z-index + if (targetElement.element instanceof SVGElement) { + parentElm = targetElement.element.parentNode; + + while (targetElement.element.parentNode !== null) { + if (!parentElm.tagName || parentElm.tagName.toLowerCase() === 'body') break; + + if (parentElm.tagName.toLowerCase() === 'svg') { + _addClass(parentElm, 'introjs-showElement introjs-relativePosition'); + } + + parentElm = parentElm.parentNode; + } + } + + _addClass(targetElement.element, 'introjs-showElement'); + + var currentElementPosition = _getPropValue(targetElement.element, 'position'); + if (currentElementPosition !== 'absolute' && + currentElementPosition !== 'relative' && + currentElementPosition !== 'fixed') { + //change to new intro item + _addClass(targetElement.element, 'introjs-relativePosition'); + } + + parentElm = targetElement.element.parentNode; + while (parentElm !== null) { + if (!parentElm.tagName || parentElm.tagName.toLowerCase() === 'body') break; + + //fix The Stacking Context problem. + //More detail: https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Understanding_z_index/The_stacking_context + var zIndex = _getPropValue(parentElm, 'z-index'); + var opacity = parseFloat(_getPropValue(parentElm, 'opacity')); + var transform = _getPropValue(parentElm, 'transform') || _getPropValue(parentElm, '-webkit-transform') || _getPropValue(parentElm, '-moz-transform') || _getPropValue(parentElm, '-ms-transform') || _getPropValue(parentElm, '-o-transform'); + if (/[0-9]+/.test(zIndex) || opacity < 1 || (transform !== 'none' && transform !== undefined)) { + _addClass(parentElm, 'introjs-fixParent'); + } + + parentElm = parentElm.parentNode; + } + } + + /** + * Iterates arrays + * + * @param {Array} arr + * @param {Function} forEachFnc + * @param {Function} completeFnc + * @return {Null} + */ + function _forEach(arr, forEachFnc, completeFnc) { + // in case arr is an empty query selector node list + if (arr) { + for (var i = 0, len = arr.length; i < len; i++) { + forEachFnc(arr[i], i); + } + } + + if (typeof(completeFnc) === 'function') { + completeFnc(); + } + } + + /** + * Mark any object with an incrementing number + * used for keeping track of objects + * + * @param Object obj Any object or DOM Element + * @param String key + * @return Object + */ + var _stamp = (function () { + var keys = {}; + return function stamp (obj, key) { + + // get group key + key = key || 'introjs-stamp'; + + // each group increments from 0 + keys[key] = keys[key] || 0; + + // stamp only once per object + if (obj[key] === undefined) { + // increment key for each new object + obj[key] = keys[key]++; + } + + return obj[key]; + }; + })(); + + /** + * DOMEvent Handles all DOM events + * + * methods: + * + * on - add event handler + * off - remove event + */ + var DOMEvent = (function () { + function DOMEvent () { + var events_key = 'introjs_event'; + + /** + * Gets a unique ID for an event listener + * + * @param Object obj + * @param String type event type + * @param Function listener + * @param Object context + * @return String + */ + this._id = function (obj, type, listener, context) { + return type + _stamp(listener) + (context ? '_' + _stamp(context) : ''); + }; + + /** + * Adds event listener + * + * @param Object obj + * @param String type event type + * @param Function listener + * @param Object context + * @param Boolean useCapture + * @return null + */ + this.on = function (obj, type, listener, context, useCapture) { + var id = this._id.apply(this, arguments), + handler = function (e) { + return listener.call(context || obj, e || window.event); + }; + + if ('addEventListener' in obj) { + obj.addEventListener(type, handler, useCapture); + } else if ('attachEvent' in obj) { + obj.attachEvent('on' + type, handler); + } + + obj[events_key] = obj[events_key] || {}; + obj[events_key][id] = handler; + }; + + /** + * Removes event listener + * + * @param Object obj + * @param String type event type + * @param Function listener + * @param Object context + * @param Boolean useCapture + * @return null + */ + this.off = function (obj, type, listener, context, useCapture) { + var id = this._id.apply(this, arguments), + handler = obj[events_key] && obj[events_key][id]; + + if (!handler) { + return; + } + + if ('removeEventListener' in obj) { + obj.removeEventListener(type, handler, useCapture); + } else if ('detachEvent' in obj) { + obj.detachEvent('on' + type, handler); + } + + obj[events_key][id] = null; + }; + } + + return new DOMEvent(); + })(); + + /** + * Append a class to an element + * + * @api private + * @method _addClass + * @param {Object} element + * @param {String} className + * @returns null + */ + function _addClass(element, className) { + if (element instanceof SVGElement) { + // svg + var pre = element.getAttribute('class') || ''; + + element.setAttribute('class', pre + ' ' + className); + } else { + if (element.classList !== undefined) { + // check for modern classList property + var classes = className.split(' '); + _forEach(classes, function (cls) { + element.classList.add( cls ); + }); + } else if (!element.className.match( className )) { + // check if element doesn't already have className + element.className += ' ' + className; + } + } + } + + /** + * Remove a class from an element + * + * @api private + * @method _removeClass + * @param {Object} element + * @param {RegExp|String} classNameRegex can be regex or string + * @returns null + */ + function _removeClass(element, classNameRegex) { + if (element instanceof SVGElement) { + var pre = element.getAttribute('class') || ''; + + element.setAttribute('class', pre.replace(classNameRegex, '').replace(/^\s+|\s+$/g, '')); + } else { + element.className = element.className.replace(classNameRegex, '').replace(/^\s+|\s+$/g, ''); + } + } + + /** + * Get an element CSS property on the page + * Thanks to JavaScript Kit: http://www.javascriptkit.com/dhtmltutors/dhtmlcascade4.shtml + * + * @api private + * @method _getPropValue + * @param {Object} element + * @param {String} propName + * @returns Element's property value + */ + function _getPropValue (element, propName) { + var propValue = ''; + if (element.currentStyle) { //IE + propValue = element.currentStyle[propName]; + } else if (document.defaultView && document.defaultView.getComputedStyle) { //Others + propValue = document.defaultView.getComputedStyle(element, null).getPropertyValue(propName); + } + + //Prevent exception in IE + if (propValue && propValue.toLowerCase) { + return propValue.toLowerCase(); + } else { + return propValue; + } + } + + /** + * Checks to see if target element (or parents) position is fixed or not + * + * @api private + * @method _isFixed + * @param {Object} element + * @returns Boolean + */ + function _isFixed (element) { + var p = element.parentNode; + + if (!p || p.nodeName === 'HTML') { + return false; + } + + if (_getPropValue(element, 'position') === 'fixed') { + return true; + } + + return _isFixed(p); + } + + /** + * Provides a cross-browser way to get the screen dimensions + * via: http://stackoverflow.com/questions/5864467/internet-explorer-innerheight + * + * @api private + * @method _getWinSize + * @returns {Object} width and height attributes + */ + function _getWinSize() { + if (window.innerWidth !== undefined) { + return { width: window.innerWidth, height: window.innerHeight }; + } else { + var D = document.documentElement; + return { width: D.clientWidth, height: D.clientHeight }; + } + } + + /** + * Check to see if the element is in the viewport or not + * http://stackoverflow.com/questions/123999/how-to-tell-if-a-dom-element-is-visible-in-the-current-viewport + * + * @api private + * @method _elementInViewport + * @param {Object} el + */ + function _elementInViewport(el) { + var rect = el.getBoundingClientRect(); + + return ( + rect.top >= 0 && + rect.left >= 0 && + (rect.bottom+80) <= window.innerHeight && // add 80 to get the text right + rect.right <= window.innerWidth + ); + } + + /** + * Add overlay layer to the page + * + * @api private + * @method _addOverlayLayer + * @param {Object} targetElm + */ + function _addOverlayLayer(targetElm) { + var overlayLayer = document.createElement('div'), + styleText = '', + self = this; + + //set css class name + overlayLayer.className = 'introjs-overlay'; + + //check if the target element is body, we should calculate the size of overlay layer in a better way + if (!targetElm.tagName || targetElm.tagName.toLowerCase() === 'body') { + styleText += 'top: 0;bottom: 0; left: 0;right: 0;position: fixed;'; + overlayLayer.style.cssText = styleText; + } else { + //set overlay layer position + var elementPosition = _getOffset(targetElm); + if (elementPosition) { + styleText += 'width: ' + elementPosition.width + 'px; height:' + elementPosition.height + 'px; top:' + elementPosition.top + 'px;left: ' + elementPosition.left + 'px;'; + overlayLayer.style.cssText = styleText; + } + } + + targetElm.appendChild(overlayLayer); + + overlayLayer.onclick = function() { + if (self._options.exitOnOverlayClick === true) { + _exitIntro.call(self, targetElm); + } + }; + + window.setTimeout(function() { + styleText += 'opacity: ' + self._options.overlayOpacity.toString() + ';'; + overlayLayer.style.cssText = styleText; + }, 10); + + return true; + } + + /** + * Removes open hint (tooltip hint) + * + * @api private + * @method _removeHintTooltip + */ + function _removeHintTooltip() { + var tooltip = document.querySelector('.introjs-hintReference'); + + if (tooltip) { + var step = tooltip.getAttribute('data-step'); + tooltip.parentNode.removeChild(tooltip); + return step; + } + } + + /** + * Start parsing hint items + * + * @api private + * @param {Object} targetElm + * @method _startHint + */ + function _populateHints(targetElm) { + + this._introItems = []; + + if (this._options.hints) { + _forEach(this._options.hints, function (hint) { + var currentItem = _cloneObject(hint); + + if (typeof(currentItem.element) === 'string') { + //grab the element with given selector from the page + currentItem.element = document.querySelector(currentItem.element); + } + + currentItem.hintPosition = currentItem.hintPosition || this._options.hintPosition; + currentItem.hintAnimation = currentItem.hintAnimation || this._options.hintAnimation; + + if (currentItem.element !== null) { + this._introItems.push(currentItem); + } + }.bind(this)); + } else { + var hints = targetElm.querySelectorAll('*[data-hint]'); + + if (!hints || !hints.length) { + return false; + } + + //first add intro items with data-step + _forEach(hints, function (currentElement) { + // hint animation + var hintAnimation = currentElement.getAttribute('data-hintanimation'); + + if (hintAnimation) { + hintAnimation = (hintAnimation === 'true'); + } else { + hintAnimation = this._options.hintAnimation; + } + + this._introItems.push({ + element: currentElement, + hint: currentElement.getAttribute('data-hint'), + hintPosition: currentElement.getAttribute('data-hintposition') || this._options.hintPosition, + hintAnimation: hintAnimation, + tooltipClass: currentElement.getAttribute('data-tooltipclass'), + position: currentElement.getAttribute('data-position') || this._options.tooltipPosition + }); + }.bind(this)); + } + + _addHints.call(this); + + /* + todo: + these events should be removed at some point + */ + DOMEvent.on(document, 'click', _removeHintTooltip, this, false); + DOMEvent.on(window, 'resize', _reAlignHints, this, true); + } + + /** + * Re-aligns all hint elements + * + * @api private + * @method _reAlignHints + */ + function _reAlignHints() { + _forEach(this._introItems, function (item) { + if (typeof(item.targetElement) === 'undefined') { + return; + } + + _alignHintPosition.call(this, item.hintPosition, item.element, item.targetElement); + }.bind(this)); + } + + /** + * Get a queryselector within the hint wrapper + * + * @param {String} selector + * @return {NodeList|Array} + */ + function _hintQuerySelectorAll(selector) { + var hintsWrapper = document.querySelector('.introjs-hints'); + return (hintsWrapper) ? hintsWrapper.querySelectorAll(selector) : []; + } + + /** + * Hide a hint + * + * @api private + * @method _hideHint + */ + function _hideHint(stepId) { + var hint = _hintQuerySelectorAll('.introjs-hint[data-step="' + stepId + '"]')[0]; + + _removeHintTooltip.call(this); + + if (hint) { + _addClass(hint, 'introjs-hidehint'); + } + + // call the callback function (if any) + if (typeof (this._hintCloseCallback) !== 'undefined') { + this._hintCloseCallback.call(this, stepId); + } + } + + /** + * Hide all hints + * + * @api private + * @method _hideHints + */ + function _hideHints() { + var hints = _hintQuerySelectorAll('.introjs-hint'); + + _forEach(hints, function (hint) { + _hideHint.call(this, hint.getAttribute('data-step')); + }.bind(this)); + } + + /** + * Show all hints + * + * @api private + * @method _showHints + */ + function _showHints() { + var hints = _hintQuerySelectorAll('.introjs-hint'); + + if (hints && hints.length) { + _forEach(hints, function (hint) { + _showHint.call(this, hint.getAttribute('data-step')); + }.bind(this)); + } else { + _populateHints.call(this, this._targetElement); + } + } + + /** + * Show a hint + * + * @api private + * @method _showHint + */ + function _showHint(stepId) { + var hint = _hintQuerySelectorAll('.introjs-hint[data-step="' + stepId + '"]')[0]; + + if (hint) { + _removeClass(hint, /introjs-hidehint/g); + } + } + + /** + * Removes all hint elements on the page + * Useful when you want to destroy the elements and add them again (e.g. a modal or popup) + * + * @api private + * @method _removeHints + */ + function _removeHints() { + var hints = _hintQuerySelectorAll('.introjs-hint'); + + _forEach(hints, function (hint) { + _removeHint.call(this, hint.getAttribute('data-step')); + }.bind(this)); + } + + /** + * Remove one single hint element from the page + * Useful when you want to destroy the element and add them again (e.g. a modal or popup) + * Use removeHints if you want to remove all elements. + * + * @api private + * @method _removeHint + */ + function _removeHint(stepId) { + var hint = _hintQuerySelectorAll('.introjs-hint[data-step="' + stepId + '"]')[0]; + + if (hint) { + hint.parentNode.removeChild(hint); + } + } + + /** + * Add all available hints to the page + * + * @api private + * @method _addHints + */ + function _addHints() { + var self = this; + + var hintsWrapper = document.querySelector('.introjs-hints'); + + if (hintsWrapper === null) { + hintsWrapper = document.createElement('div'); + hintsWrapper.className = 'introjs-hints'; + } + + /** + * Returns an event handler unique to the hint iteration + * + * @param {Integer} i + * @return {Function} + */ + var getHintClick = function (i) { + return function(e) { + var evt = e ? e : window.event; + + if (evt.stopPropagation) { + evt.stopPropagation(); + } + + if (evt.cancelBubble !== null) { + evt.cancelBubble = true; + } + + _showHintDialog.call(self, i); + }; + }; + + _forEach(this._introItems, function(item, i) { + // avoid append a hint twice + if (document.querySelector('.introjs-hint[data-step="' + i + '"]')) { + return; + } + + var hint = document.createElement('a'); + _setAnchorAsButton(hint); + + hint.onclick = getHintClick(i); + + hint.className = 'introjs-hint'; + + if (!item.hintAnimation) { + _addClass(hint, 'introjs-hint-no-anim'); + } + + // hint's position should be fixed if the target element's position is fixed + if (_isFixed(item.element)) { + _addClass(hint, 'introjs-fixedhint'); + } + + var hintDot = document.createElement('div'); + hintDot.className = 'introjs-hint-dot'; + var hintPulse = document.createElement('div'); + hintPulse.className = 'introjs-hint-pulse'; + + hint.appendChild(hintDot); + hint.appendChild(hintPulse); + hint.setAttribute('data-step', i); + + // we swap the hint element with target element + // because _setHelperLayerPosition uses `element` property + item.targetElement = item.element; + item.element = hint; + + // align the hint position + _alignHintPosition.call(this, item.hintPosition, hint, item.targetElement); + + hintsWrapper.appendChild(hint); + }.bind(this)); + + // adding the hints wrapper + document.body.appendChild(hintsWrapper); + + // call the callback function (if any) + if (typeof (this._hintsAddedCallback) !== 'undefined') { + this._hintsAddedCallback.call(this); + } + } + + /** + * Aligns hint position + * + * @api private + * @method _alignHintPosition + * @param {String} position + * @param {Object} hint + * @param {Object} element + */ + function _alignHintPosition(position, hint, element) { + // get/calculate offset of target element + var offset = _getOffset.call(this, element); + var iconWidth = 20; + var iconHeight = 20; + + // align the hint element + switch (position) { + default: + case 'top-left': + hint.style.left = offset.left + 'px'; + hint.style.top = offset.top + 'px'; + break; + case 'top-right': + hint.style.left = (offset.left + offset.width - iconWidth) + 'px'; + hint.style.top = offset.top + 'px'; + break; + case 'bottom-left': + hint.style.left = offset.left + 'px'; + hint.style.top = (offset.top + offset.height - iconHeight) + 'px'; + break; + case 'bottom-right': + hint.style.left = (offset.left + offset.width - iconWidth) + 'px'; + hint.style.top = (offset.top + offset.height - iconHeight) + 'px'; + break; + case 'middle-left': + hint.style.left = offset.left + 'px'; + hint.style.top = (offset.top + (offset.height - iconHeight) / 2) + 'px'; + break; + case 'middle-right': + hint.style.left = (offset.left + offset.width - iconWidth) + 'px'; + hint.style.top = (offset.top + (offset.height - iconHeight) / 2) + 'px'; + break; + case 'middle-middle': + hint.style.left = (offset.left + (offset.width - iconWidth) / 2) + 'px'; + hint.style.top = (offset.top + (offset.height - iconHeight) / 2) + 'px'; + break; + case 'bottom-middle': + hint.style.left = (offset.left + (offset.width - iconWidth) / 2) + 'px'; + hint.style.top = (offset.top + offset.height - iconHeight) + 'px'; + break; + case 'top-middle': + hint.style.left = (offset.left + (offset.width - iconWidth) / 2) + 'px'; + hint.style.top = offset.top + 'px'; + break; + } + } + + /** + * Triggers when user clicks on the hint element + * + * @api private + * @method _showHintDialog + * @param {Number} stepId + */ + function _showHintDialog(stepId) { + var hintElement = document.querySelector('.introjs-hint[data-step="' + stepId + '"]'); + var item = this._introItems[stepId]; + + // call the callback function (if any) + if (typeof (this._hintClickCallback) !== 'undefined') { + this._hintClickCallback.call(this, hintElement, item, stepId); + } + + // remove all open tooltips + var removedStep = _removeHintTooltip.call(this); + + // to toggle the tooltip + if (parseInt(removedStep, 10) === stepId) { + return; + } + + var tooltipLayer = document.createElement('div'); + var tooltipTextLayer = document.createElement('div'); + var arrowLayer = document.createElement('div'); + var referenceLayer = document.createElement('div'); + + tooltipLayer.className = 'introjs-tooltip'; + + tooltipLayer.onclick = function (e) { + //IE9 & Other Browsers + if (e.stopPropagation) { + e.stopPropagation(); + } + //IE8 and Lower + else { + e.cancelBubble = true; + } + }; + + tooltipTextLayer.className = 'introjs-tooltiptext'; + + var tooltipWrapper = document.createElement('p'); + tooltipWrapper.innerHTML = item.hint; + + var closeButton = document.createElement('a'); + closeButton.className = this._options.buttonClass; + closeButton.setAttribute('role', 'button'); + closeButton.innerHTML = this._options.hintButtonLabel; + closeButton.onclick = _hideHint.bind(this, stepId); + + tooltipTextLayer.appendChild(tooltipWrapper); + tooltipTextLayer.appendChild(closeButton); + + arrowLayer.className = 'introjs-arrow'; + tooltipLayer.appendChild(arrowLayer); + + tooltipLayer.appendChild(tooltipTextLayer); + + // set current step for _placeTooltip function + this._currentStep = hintElement.getAttribute('data-step'); + + // align reference layer position + referenceLayer.className = 'introjs-tooltipReferenceLayer introjs-hintReference'; + referenceLayer.setAttribute('data-step', hintElement.getAttribute('data-step')); + _setHelperLayerPosition.call(this, referenceLayer); + + referenceLayer.appendChild(tooltipLayer); + document.body.appendChild(referenceLayer); + + //set proper position + _placeTooltip.call(this, hintElement, tooltipLayer, arrowLayer, null, true); + } + + /** + * Get an element position on the page + * Thanks to `meouw`: http://stackoverflow.com/a/442474/375966 + * + * @api private + * @method _getOffset + * @param {Object} element + * @returns Element's position info + */ + function _getOffset(element) { + var body = document.body; + var docEl = document.documentElement; + var scrollTop = window.pageYOffset || docEl.scrollTop || body.scrollTop; + var scrollLeft = window.pageXOffset || docEl.scrollLeft || body.scrollLeft; + var x = element.getBoundingClientRect(); + return { + top: x.top + scrollTop, + width: x.width, + height: x.height, + left: x.left + scrollLeft + }; + } + + /** + * Find the nearest scrollable parent + * copied from https://stackoverflow.com/questions/35939886/find-first-scrollable-parent + * + * @param Element element + * @return Element + */ + function _getScrollParent(element) { + var style = window.getComputedStyle(element); + var excludeStaticParent = (style.position === "absolute"); + var overflowRegex = /(auto|scroll)/; + + if (style.position === "fixed") return document.body; + + for (var parent = element; (parent = parent.parentElement);) { + style = window.getComputedStyle(parent); + if (excludeStaticParent && style.position === "static") { + continue; + } + if (overflowRegex.test(style.overflow + style.overflowY + style.overflowX)) return parent; + } + + return document.body; + } + + /** + * scroll a scrollable element to a child element + * + * @param Element parent + * @param Element element + * @return Null + */ + function _scrollParentToElement (parent, element) { + parent.scrollTop = element.offsetTop - parent.offsetTop; + } + + /** + * Gets the current progress percentage + * + * @api private + * @method _getProgress + * @returns current progress percentage + */ + function _getProgress() { + // Steps are 0 indexed + var currentStep = parseInt((this._currentStep + 1), 10); + return ((currentStep / this._introItems.length) * 100); + } + + /** + * Overwrites obj1's values with obj2's and adds obj2's if non existent in obj1 + * via: http://stackoverflow.com/questions/171251/how-can-i-merge-properties-of-two-javascript-objects-dynamically + * + * @param obj1 + * @param obj2 + * @returns obj3 a new object based on obj1 and obj2 + */ + function _mergeOptions(obj1,obj2) { + var obj3 = {}, + attrname; + for (attrname in obj1) { obj3[attrname] = obj1[attrname]; } + for (attrname in obj2) { obj3[attrname] = obj2[attrname]; } + return obj3; + } + + var introJs = function (targetElm) { + var instance; + + if (typeof (targetElm) === 'object') { + //Ok, create a new instance + instance = new IntroJs(targetElm); + + } else if (typeof (targetElm) === 'string') { + //select the target element with query selector + var targetElement = document.querySelector(targetElm); + + if (targetElement) { + instance = new IntroJs(targetElement); + } else { + throw new Error('There is no element with given selector.'); + } + } else { + instance = new IntroJs(document.body); + } + // add instance to list of _instances + // passing group to _stamp to increment + // from 0 onward somewhat reliably + introJs.instances[ _stamp(instance, 'introjs-instance') ] = instance; + + return instance; + }; + + /** + * Current IntroJs version + * + * @property version + * @type String + */ + introJs.version = VERSION; + + /** + * key-val object helper for introJs instances + * + * @property instances + * @type Object + */ + introJs.instances = {}; + + //Prototype + introJs.fn = IntroJs.prototype = { + clone: function () { + return new IntroJs(this); + }, + setOption: function(option, value) { + this._options[option] = value; + return this; + }, + setOptions: function(options) { + this._options = _mergeOptions(this._options, options); + return this; + }, + start: function (group) { + _introForElement.call(this, this._targetElement, group); + return this; + }, + goToStep: function(step) { + _goToStep.call(this, step); + return this; + }, + addStep: function(options) { + if (!this._options.steps) { + this._options.steps = []; + } + + this._options.steps.push(options); + + return this; + }, + addSteps: function(steps) { + if (!steps.length) return; + + for(var index = 0; index < steps.length; index++) { + this.addStep(steps[index]); + } + + return this; + }, + goToStepNumber: function(step) { + _goToStepNumber.call(this, step); + + return this; + }, + nextStep: function() { + _nextStep.call(this); + return this; + }, + previousStep: function() { + _previousStep.call(this); + return this; + }, + exit: function(force) { + _exitIntro.call(this, this._targetElement, force); + return this; + }, + refresh: function() { + _refresh.call(this); + return this; + }, + onbeforechange: function(providedCallback) { + if (typeof (providedCallback) === 'function') { + this._introBeforeChangeCallback = providedCallback; + } else { + throw new Error('Provided callback for onbeforechange was not a function'); + } + return this; + }, + onchange: function(providedCallback) { + if (typeof (providedCallback) === 'function') { + this._introChangeCallback = providedCallback; + } else { + throw new Error('Provided callback for onchange was not a function.'); + } + return this; + }, + onafterchange: function(providedCallback) { + if (typeof (providedCallback) === 'function') { + this._introAfterChangeCallback = providedCallback; + } else { + throw new Error('Provided callback for onafterchange was not a function'); + } + return this; + }, + oncomplete: function(providedCallback) { + if (typeof (providedCallback) === 'function') { + this._introCompleteCallback = providedCallback; + } else { + throw new Error('Provided callback for oncomplete was not a function.'); + } + return this; + }, + onhintsadded: function(providedCallback) { + if (typeof (providedCallback) === 'function') { + this._hintsAddedCallback = providedCallback; + } else { + throw new Error('Provided callback for onhintsadded was not a function.'); + } + return this; + }, + onhintclick: function(providedCallback) { + if (typeof (providedCallback) === 'function') { + this._hintClickCallback = providedCallback; + } else { + throw new Error('Provided callback for onhintclick was not a function.'); + } + return this; + }, + onhintclose: function(providedCallback) { + if (typeof (providedCallback) === 'function') { + this._hintCloseCallback = providedCallback; + } else { + throw new Error('Provided callback for onhintclose was not a function.'); + } + return this; + }, + onexit: function(providedCallback) { + if (typeof (providedCallback) === 'function') { + this._introExitCallback = providedCallback; + } else { + throw new Error('Provided callback for onexit was not a function.'); + } + return this; + }, + onskip: function(providedCallback) { + if (typeof (providedCallback) === 'function') { + this._introSkipCallback = providedCallback; + } else { + throw new Error('Provided callback for onskip was not a function.'); + } + return this; + }, + onbeforeexit: function(providedCallback) { + if (typeof (providedCallback) === 'function') { + this._introBeforeExitCallback = providedCallback; + } else { + throw new Error('Provided callback for onbeforeexit was not a function.'); + } + return this; + }, + addHints: function() { + _populateHints.call(this, this._targetElement); + return this; + }, + hideHint: function (stepId) { + _hideHint.call(this, stepId); + return this; + }, + hideHints: function () { + _hideHints.call(this); + return this; + }, + showHint: function (stepId) { + _showHint.call(this, stepId); + return this; + }, + showHints: function () { + _showHints.call(this); + return this; + }, + removeHints: function () { + _removeHints.call(this); + return this; + }, + removeHint: function (stepId) { + _removeHint.call(this, stepId); + return this; + }, + showHintDialog: function (stepId) { + _showHintDialog.call(this, stepId); + return this; + } + }; + + return introJs; +}); diff --git a/public/static/js/langs/zh-cn.js b/public/static/js/langs/zh-cn.js index 4bb7736..a31566a 100644 --- a/public/static/js/langs/zh-cn.js +++ b/public/static/js/langs/zh-cn.js @@ -1 +1 @@ -var tpl_lang={"appname":"\u84dd\u5929\u91c7\u96c6\u5668","user_error_username":"\u7528\u6237\u540d\u5fc5\u987b\u75313-15\u4e2a\u5b57\u7b26\u7ec4\u6210\uff01","user_error_has_username":"\u7528\u6237\u540d\u5df2\u7ecf\u5b58\u5728\uff01","user_error_password":"\u5bc6\u7801\u5fc5\u987b\u75316-20\u4f4d\u5b57\u6bcd\u3001\u6570\u5b57\u548c\u7279\u6b8a\u5b57\u7b26\u7ec4\u6210\uff01","user_error_repassword":"\u786e\u8ba4\u5bc6\u7801\u548c\u5bc6\u7801\u4e0d\u4e00\u81f4\uff01","admincp":"\u540e\u53f0","sign_wildcard":"(*)","sign_match":"[\u5185\u5bb9{:num}]","tips_sign_wildcard":"\u901a\u914d\u7b26\u53ef\u5339\u914d\u4efb\u610f\u5b57\u7b26","tips_sign_match":"\u5339\u914d\u4efb\u610f\u5b57\u7b26\u5e76\u4fdd\u5b58\u4e3a\u6807\u7b7e\u4ee5\u4f9b\u8c03\u7528\uff0c\u7b49\u540c\u4e8e\u6355\u83b7\u7ec4\uff1a(?<content\u7f16\u53f7>.*?)","tips_sign_match_only":"\u5339\u914d\u4efb\u610f\u5b57\u7b26\u5e76\u4fdd\u5b58\u4e3a\u6807\u7b7e\u4ee5\u4f9b\u8c03\u7528\uff0c\u7b49\u540c\u4e8e\u6355\u83b7\u7ec4\uff1a(?<content>.*?)","tips_sign_group":"\u6355\u83b7\u7ec4\uff1a(?<content\u7f16\u53f7>[\\s\\S]*?)\uff0c\u5339\u914d\u6b63\u5219\u5e76\u4fdd\u5b58\u4e3a[\u5185\u5bb9]\u6807\u7b7e\u4ee5\u4f9b\u8c03\u7528","tips_sign_group_only":"\u6355\u83b7\u7ec4\uff1a(?<content>[\\s\\S]*?)\uff0c\u5339\u914d\u6b63\u5219\u5e76\u4fdd\u5b58\u4e3a[\u5185\u5bb9]\u6807\u7b7e\u4ee5\u4f9b\u8c03\u7528","tips_regular":"\u53ef\u4f7f\u7528\u6b63\u5219\u8868\u8fbe\u5f0f","setting":"\u8bbe\u7f6e","setting_site":"\u7ad9\u70b9\u8bbe\u7f6e","set_site_verifycode":"\u5f00\u542f\u56fe\u7247\u9a8c\u8bc1\u7801","setting_caiji":"\u91c7\u96c6\u8bbe\u7f6e","set_caiji_auto":"\u5f00\u542f\u81ea\u52a8\u91c7\u96c6","set_caiji_run":"\u81ea\u52a8\u91c7\u96c6\u8fd0\u884c\u65b9\u5f0f","set_caiji_interval":"\u6bcf\u6b21\u91c7\u96c6\u95f4\u9694\u65f6\u95f4","set_caiji_num":"\u6bcf\u6b21\u91c7\u96c6\u6570\u91cf","set_caiji_timeout":"\u6700\u5927\u6267\u884c\u65f6\u95f4","setting_email":"\u90ae\u4ef6\u53d1\u9001\u8bbe\u7f6e","set_email_sender":"\u53d1\u4ef6\u4eba\u540d\u79f0","set_email_email":"\u53d1\u4ef6\u4eba\u90ae\u7bb1\u8d26\u53f7","set_email_pwd":"\u53d1\u4ef6\u4eba\u90ae\u7bb1\u5bc6\u7801","set_email_smtp":"SMTP\u670d\u52a1\u5668","set_email_port":"SMTP\u7aef\u53e3","set_email_port_tips":"TLS\u4e00\u822c\u4e3a25\uff0cSSL\u4e00\u822c\u4e3a465\uff0c\u54a8\u8be2\u90ae\u7bb1\u670d\u52a1\u5546\u83b7\u53d6","set_email_type":"SMTP\u7aef\u53e3\u7c7b\u578b","set_email_test_subject":"\u6d4b\u8bd5\u53d1\u9001\u90ae\u4ef6","set_email_test_body":"\u606d\u559c\uff0c\u53d1\u9001\u90ae\u4ef6\u6210\u529f\uff01","config_error_none_email":"\u6ca1\u6709\u90ae\u7bb1\u670d\u52a1\u5668\u914d\u7f6e\uff0c\u8bf7\u5728\u540e\u53f0\u8bbe\u7f6e\uff01","user":"\u7528\u6237","user_list":"\u7528\u6237\u5217\u8868","user_add":"\u6dfb\u52a0\u7528\u6237","user_edit":"\u7f16\u8f91\u7528\u6237","user_username":"\u7528\u6237\u540d","user_password":"\u5bc6\u7801","user_repassword":"\u786e\u8ba4\u5bc6\u7801","user_newpwd_tips":"\u5982\u679c\u60a8\u60f3\u4fee\u6539\u5bc6\u7801\uff0c\u8bf7\u5728\u6b64\u8f93\u5165\u65b0\u5bc6\u7801\uff0c\u5426\u5219\u7559\u7a7a","user_email":"\u90ae\u7bb1","user_email_tips":"\u7528\u4e8e\u627e\u56de\u8d26\u53f7\u5bc6\u7801","user_groupid":"\u7528\u6237\u7ec4","user_error_edit_not_allow":"\u53ea\u6709\u521b\u59cb\u4eba\u624d\u80fd\u7f16\u8f91\u4ed6\u4eba\u7684\u8d26\u53f7\uff01","user_error_delete_not_allow":"\u53ea\u6709\u521b\u59cb\u4eba\u624d\u80fd\u5220\u9664\u8d26\u53f7\uff01","user_error_email":"\u90ae\u7bb1\u683c\u5f0f\u9519\u8bef\uff01","user_error_groupid":"\u4e0d\u662f\u5141\u8bb8\u7684\u7528\u6237\u7ec4\uff01","user_error_del_founder":"\u4e0d\u80fd\u5220\u9664\u521b\u59cb\u4eba\u8d26\u53f7\uff01","user_error_null_uid":"UID\u4e0d\u80fd\u4e3a\u7a7a","user_error_empty_user":"\u7528\u6237\u4e0d\u5b58\u5728","user_error_login":"\u7528\u6237\u540d\u6216\u5bc6\u7801\u4e0d\u6b63\u786e\uff01","user_error_sublogin":"\u767b\u5f55\u63d0\u4ea4\u5931\u8d25","user_error_is_not_admin":"\u62b1\u6b49\uff0c\u8bf7\u767b\u5f55\u7ba1\u7406\u5458\u8d26\u53f7\uff01","user_login_in":"\u767b\u5f55\u4e2d...","user_auto_login":"\u6b63\u5728\u81ea\u52a8\u767b\u5f55...","usertoken_error":"\u7528\u6237token\u9519\u8bef\uff0c\u8bf7\u5237\u65b0\u9875\u9762\u91cd\u65b0\u83b7\u53d6\uff01","task":"\u4efb\u52a1","task_add":"\u6dfb\u52a0\u4efb\u52a1","task_edit":"\u7f16\u8f91\u4efb\u52a1","task_list":"\u4efb\u52a1\u5217\u8868","task_change_list":"\u5207\u6362\u5217\u8868\u6a21\u5f0f","task_change_folder":"\u5207\u6362\u5206\u7ec4\u6a21\u5f0f","task_name":"\u4efb\u52a1\u540d\u79f0","task_tg":"\u4efb\u52a1\u5206\u7ec4","task_sort":"\u6392\u5e8f","task_sort_help":"\u6570\u5b57\u8d8a\u5927\u8d8a\u9760\u524d","task_module":"\u91c7\u96c6\u6a21\u5757","task_module_":"\u65e0","task_module_pattern":"\u89c4\u5219\u91c7\u96c6","task_module_keyword":"\u5173\u952e\u8bcd\u91c7\u96c6","task_module_weixin":"\u5fae\u4fe1\u91c7\u96c6","task_auto":"\u81ea\u52a8\u91c7\u96c6","task_addtime":"\u6dfb\u52a0\u65f6\u95f4","task_caijitime":"\u91c7\u96c6\u65f6\u95f4","task_edit_collector":"\u4e0b\u4e00\u6b65\uff1a\u7f16\u8f91\u91c7\u96c6\u5668","task_root":"\u6839\u76ee\u5f55","task_loading":"\u6b63\u5728\u8f7d\u5165\u6570\u636e","task_none_data":"\u65e0\u6570\u636e","task_caiji_ing":"\u6b63\u5728\u91c7\u96c6","task_set_task":"\u4efb\u52a1\u8bbe\u7f6e","task_set_collector":"\u91c7\u96c6\u5668\u8bbe\u7f6e","task_set_release":"\u53d1\u5e03\u8bbe\u7f6e","task_error_null_id":"\u8bf7\u8f93\u5165\u4efb\u52a1id","task_error_empty_task":"\u4e0d\u5b58\u5728\u4efb\u52a1","task_error_null_tgid":"\u8bf7\u8f93\u5165\u5206\u7ec4id","task_error_empty_tg":"\u4e0d\u5b58\u5728\u5206\u7ec4","task_error_null_name":"\u8bf7\u8f93\u5165\u540d\u79f0\uff01","task_error_has_name":"\u540d\u79f0\u5df2\u7ecf\u5b58\u5728\uff01","task_error_null_module":"\u672a\u8bbe\u7f6e\u91c7\u96c6\u6a21\u5757","taskgroup_add":"\u6dfb\u52a0\u5206\u7ec4","taskgroup_edit":"\u7f16\u8f91\u5206\u7ec4","taskgroup":"\u4efb\u52a1\u5206\u7ec4","taskgroup_list":"\u5206\u7ec4\u5217\u8868","taskgroup_name":"\u5206\u7ec4\u540d\u79f0","taskgroup_sort":"\u6392\u5e8f","taskgroup_sort_help":"\u6570\u5b57\u8d8a\u5927\u8d8a\u9760\u524d","taskgroup_parent_id":"\u7236\u5206\u7ec4","tg_add_sub":"\u6dfb\u52a0\u5b50\u5206\u7ec4","tg_move":"\u79fb\u52a8\u5206\u7ec4","tg_exist_sub":"\u5b58\u5728\u5b50\u5206\u7ec4\uff01","tg_none":"\u5206\u7ec4\u4e0d\u5b58\u5728\uff01","tg_is_parent":"\u5b58\u5728\u5b50\u5206\u7ec4\uff0c\u8bf7\u5148\u6e05\u7a7a\u5b50\u5206\u7ec4\uff0c\u624d\u80fd\u79fb\u52a8\u5206\u7ec4\uff01","tg_deleteall_has_sub":"\u6709\u5b50\u5206\u7ec4\u7684\u4e0d\u80fd\u5220\u9664\uff0c\u9700\u5148\u6e05\u7a7a\u5b50\u5206\u7ec4\uff01","tg_no_checked":"\u6ca1\u6709\u9009\u4e2d\u7684\u8bb0\u5f55\uff01","tg_error_null_name":"\u8bf7\u8f93\u5165\u540d\u79f0\uff01","tg_error_has_name":"\u540d\u79f0\u5df2\u7ecf\u5b58\u5728\uff01","coll_set":"\u91c7\u96c6\u5668\u8bbe\u7f6e","coll_edit_task":"\u4e0a\u4e00\u6b65\uff1a\u7f16\u8f91\u4efb\u52a1","coll_name":"\u91c7\u96c6\u89c4\u5219\u540d\u79f0","coll_error_invalid_module":"\u65e0\u6548\u7684\u91c7\u96c6\u6a21\u5757","coll_error_empty_coll":"\u4e0d\u5b58\u5728\u91c7\u96c6\u5668","coll_error_empty_effective":"\u9875\u9762\u811a\u672c\u4e0d\u53ef\u7528\uff0c\u4fdd\u5b58\u5931\u8d25\uff01","field_module_rule":"\u89c4\u5219\u5339\u914d","field_module_auto":"\u81ea\u52a8\u83b7\u53d6","field_module_xpath":"XPath\u5339\u914d","field_module_words":"\u56fa\u5b9a\u6587\u5b57","field_module_num":"\u968f\u673a\u6570\u5b57","field_module_time":"\u65f6\u95f4","field_module_list":"\u968f\u673a\u62bd\u53d6","field_module_json":"JSON\u63d0\u53d6","field_module_merge":"\u5b57\u6bb5\u7ec4\u5408","field_module_extract":"\u5b57\u6bb5\u63d0\u53d6","process_module_html":"html\u6807\u7b7e\u8fc7\u6ee4","process_module_replace":"\u5185\u5bb9\u66ff\u6362","process_module_filter":"\u5173\u952e\u8bcd\u8fc7\u6ee4","process_module_if":"\u6761\u4ef6\u5224\u65ad","process_module_translate":"\u7ffb\u8bd1","process_module_tool":"\u5de5\u5177\u7bb1","process_module_batch":"\u6279\u91cf\u66ff\u6362","process_module_substr":"\u622a\u53d6\u5b57\u7b26\u4e32","process_module_func":"\u4f7f\u7528\u51fd\u6570","p_m_if_1":"\u6ee1\u8db3\u6761\u4ef6\u91c7\u96c6","p_m_if_2":"\u6ee1\u8db3\u6761\u4ef6\u4e0d\u91c7\u96c6","p_m_if_3":"\u4e0d\u6ee1\u8db3\u6761\u4ef6\u91c7\u96c6","p_m_if_4":"\u4e0d\u6ee1\u8db3\u6761\u4ef6\u4e0d\u91c7\u96c6","rele_set":"\u53d1\u5e03\u8bbe\u7f6e","rele_error_detect_null":"\u6ca1\u6709\u68c0\u6d4b\u5230\u672c\u5730CMS\u7a0b\u5e8f\uff0c\u60a8\u53ef\u4ee5\u624b\u52a8\u7ed1\u5b9a\u6570\u636e","rele_error_empty_rele":"\u53d1\u5e03\u8bbe\u7f6e\u4e0d\u5b58\u5728","rele_error_null_module":"\u8bf7\u9009\u62e9\u53d1\u5e03\u65b9\u5f0f","rele_error_db":"\u6570\u636e\u5e93\u9519\u8bef\uff1a","rele_error_no_table":"\u8be5\u6570\u636e\u5e93\u6ca1\u6709\u8868","rele_success_db_ok":"\u6570\u636e\u5e93\u8fde\u63a5\u6210\u529f","rele_module":"\u53d1\u5e03\u65b9\u5f0f","rele_module_cms":"\u672c\u5730CMS\u7a0b\u5e8f","rele_module_db":"\u6570\u636e\u5e93","rele_module_api":"API\u63a5\u53e3","rele_module_file":"\u6587\u4ef6\u5b58\u50a8","rele_module_diy":"\u81ea\u5b9a\u4e49\u63d2\u4ef6","rele_btn_detect":"\u5f00\u59cb\u68c0\u6d4b","rele_cms_path":"CMS\u8def\u5f84","rele_db_type":"\u6570\u636e\u5e93\u7c7b\u578b","rele_db_host":"\u6570\u636e\u5e93\u4e3b\u673a","rele_db_name":"\u6570\u636e\u5e93\u540d\u79f0","rele_db_charset":"\u6570\u636e\u5e93\u7f16\u7801","rele_db_port":"\u6570\u636e\u5e93\u7aef\u53e3","rele_db_user":"\u6570\u636e\u5e93\u7528\u6237","rele_db_pwd":"\u6570\u636e\u5e93\u5bc6\u7801","error_unknown_database":"\u672a\u77e5\u7684\u6570\u636e\u5e93","error_null_input":"\u8bf7\u8f93\u5165{:str}","collected":"\u5df2\u91c7\u96c6\u6570\u636e","collected_list":"\u5df2\u91c7\u96c6\u6570\u636e\u5217\u8868","COLLECTED_RELE_":"\u65e0","collected_rele_cms":"CMS","collected_rele_db":"\u6570\u636e\u5e93","collected_rele_file":"\u6587\u4ef6","collected_rele_api":"API","collected_rele_diy":"\u63d2\u4ef6","verifycode":"\u9a8c\u8bc1\u7801","verifycode_error":"\u9a8c\u8bc1\u7801\u9519\u8bef\uff01","find_password":"\u627e\u56de\u5bc6\u7801","find_pwd_username":"\u8bf7\u8f93\u5165\u90ae\u7bb1\/\u7528\u6237\u540d","find_pwd_yzm":"\u8bf7\u8f93\u5165\u6fc0\u6d3b\u7801","find_pwd_resend":"\u91cd\u65b0\u53d1\u9001","find_pwd_next_step":"\u4e0b\u4e00\u6b65","find_pwd_pwd":"\u8bf7\u8f93\u5165\u65b0\u5bc6\u7801","find_pwd_repwd":"\u786e\u8ba4\u65b0\u5bc6\u7801","find_pwd_sended":"\u5df2\u5411\u90ae\u7bb1{:email}\u53d1\u9001\u4e86\u6fc0\u6d3b\u7801\uff01","find_pwd_email_failed":"\u53d1\u9001\u90ae\u4ef6\u5931\u8d25\uff0c\u8bf7\u68c0\u67e5\u540e\u53f0\u53d1\u9001\u90ae\u4ef6\u914d\u7f6e\uff01","find_pwd_email_wait":"\u9700\u7b49\u5f85{:seconds}\u79d2\u624d\u80fd\u518d\u6b21\u53d1\u9001","find_pwd_email_subject":"\u627e\u56de\u5bc6\u7801 - \u84dd\u5929\u91c7\u96c6\u5668","find_pwd_email_body":"\u60a8\u7684\u6fc0\u6d3b\u7801\u4e3a\uff1a{:yzm}\uff0c\u6709\u6548\u65f6\u95f4{:minutes}\u5206\u949f","find_pwd_error_username":"\u8bf7\u8f93\u5165\u90ae\u7bb1\/\u7528\u6237\u540d","find_pwd_error_step":"\u6b65\u9aa4\u9519\u8bef\uff0c\u8bf7\u91cd\u65b0\u64cd\u4f5c\uff01","find_pwd_error_post":"\u8868\u5355\u63d0\u4ea4\u5931\u8d25","find_pwd_error_none_email":"\u90ae\u7bb1\u4e0d\u5b58\u5728\uff01","find_pwd_error_multiple_emails":"\u5b58\u5728\u591a\u4e2a\u7528\u6237\u4f7f\u7528\u6b64\u90ae\u7bb1\uff0c\u8bf7\u8f93\u5165\u7528\u6237\u540d\uff01","find_pwd_error_none_user":"\u7528\u6237\u4e0d\u5b58\u5728\uff01","find_pwd_success":"\u5bc6\u7801\u4fee\u6539\u6210\u529f","yzm_error_please_send":"\u8bf7\u53d1\u9001\u6fc0\u6d3b\u7801","yzm_error_please_input":"\u8bf7\u8f93\u5165\u6fc0\u6d3b\u7801","yzm_error_timeout":"\u6fc0\u6d3b\u7801\u5df2\u8fc7\u671f\uff01\u8bf7\u91cd\u65b0\u53d1\u9001","yzm_error_yzm":"\u6fc0\u6d3b\u7801\u9519\u8bef","admincp_style":"\u754c\u9762","admincp_sidebar_mini":"\u83dc\u5355\u6700\u5c0f\u5316","admincp_skins":"\u8bbe\u7f6e\u76ae\u80a4","skin_blue":"\u84dd","skin_black":"\u9ed1","skin_purple":"\u7d2b","skin_green":"\u7eff","skin_red":"\u7ea2","skin_yellow":"\u9ec4","skin_blue_light":"\u84dd\u4eae","skin_black_light":"\u9ed1\u4eae","skin_purple_light":"\u7d2b\u4eae","skin_green_light":"\u7eff\u4eae","skin_red_light":"\u7ea2\u4eae","skin_yellow_light":"\u9ec4\u4eae","store":"\u4e91\u5e73\u53f0","rule_collect":"\u91c7\u96c6\u89c4\u5219","empty_data":"\u6570\u636e\u4e0d\u5b58\u5728","invalid_op":"\u65e0\u6548\u7684\u64cd\u4f5c\uff01","submit":"\u63d0\u4ea4","search":"\u641c\u7d22","op_success":"\u64cd\u4f5c\u6210\u529f","op_failed":"\u64cd\u4f5c\u5931\u8d25","sort":"\u6392\u5e8f","select":"\u9009\u62e9","select_all":"\u5168\u9009","select_first":"\u8bf7\u9009\u62e9","save":"\u4fdd\u5b58","op":"\u64cd\u4f5c","delete":"\u5220\u9664","deleted":"\u5df2\u5220\u9664","edit":"\u7f16\u8f91","test":"\u6d4b\u8bd5","confirm_delete":"\u786e\u5b9a\u5220\u9664\uff1f","delete_success":"\u5220\u9664\u6210\u529f","none":"\u65e0","caiji":"\u91c7\u96c6","more":"\u66f4\u591a","yes":"\u662f","no":"\u5426","all":"\u5168\u90e8","login":"\u767b\u5f55","logout":"\u9000\u51fa","login_auto":"\u4e0b\u6b21\u81ea\u52a8\u767b\u5f55","separator":"\uff1a","redirecting":"\u8df3\u8f6c\u4e2d...","close":"\u5173\u95ed","return_home":"{:time}\u79d2\u949f\u540e\u8fd4\u56de\u9875\u9762<\/a>","tips_match":"\u793a\u4f8b\uff1a<div id="a">[\u5185\u5bb91]<\/div>(*)<div id="b">[\u5185\u5bb92]<\/div>","tips_matchn":"\u793a\u4f8b\uff1a[\u5185\u5bb91] [\u5185\u5bb92]","tips_match_only":"\u793a\u4f8b\uff1a<div id="content">[\u5185\u5bb9]<\/div>","tips_match_url":"\u793a\u4f8b\uff1a<a href="http:\/\/demo.com\/[\u5185\u5bb91]\/[\u5185\u5bb92]">(*)<\/a>","tips_matchn_url":"\u793a\u4f8b\uff1ahttp:\/\/www.demo.com\/[\u5185\u5bb91]-[\u5185\u5bb92].html","release_upgrade":"\u63d2\u4ef6\u7248\u672c\u8fc7\u4f4e\uff0c\u8bf7\u5347\u7ea7\u63d2\u4ef6 \u5347\u7ea7\u6559\u7a0b<\/a>"}; \ No newline at end of file +var tpl_lang={"appname":"\u84dd\u5929\u91c7\u96c6\u5668","user_error_username":"\u7528\u6237\u540d\u5fc5\u987b\u75313-15\u4e2a\u5b57\u7b26\u7ec4\u6210\uff01","user_error_has_username":"\u7528\u6237\u540d\u5df2\u7ecf\u5b58\u5728\uff01","user_error_password":"\u5bc6\u7801\u5fc5\u987b\u75316-30\u4f4d\u5b57\u6bcd\u3001\u6570\u5b57\u6216\u7279\u6b8a\u5b57\u7b26\u7ec4\u6210\uff01","user_error_repassword":"\u786e\u8ba4\u5bc6\u7801\u548c\u5bc6\u7801\u4e0d\u4e00\u81f4\uff01","admincp":"\u540e\u53f0","sign_wildcard":"(*)","sign_match":"[\u5185\u5bb9{:num}]","tips_sign_wildcard":"\u901a\u914d\u7b26\u53ef\u5339\u914d\u4efb\u610f\u5b57\u7b26","tips_sign_match":"\u5339\u914d\u4efb\u610f\u5b57\u7b26\u5e76\u4fdd\u5b58\u4e3a\u6807\u7b7e\u4ee5\u4f9b\u8c03\u7528\uff0c\u7b49\u540c\u4e8e\u6355\u83b7\u7ec4\uff1a(?<content\u7f16\u53f7>.*?)","tips_sign_match_only":"\u5339\u914d\u4efb\u610f\u5b57\u7b26\u5e76\u4fdd\u5b58\u4e3a\u6807\u7b7e\u4ee5\u4f9b\u8c03\u7528\uff0c\u7b49\u540c\u4e8e\u6355\u83b7\u7ec4\uff1a(?<content>.*?)","tips_sign_group":"\u6355\u83b7\u7ec4\uff1a(?<content\u7f16\u53f7>[\\s\\S]*?)\uff0c\u5339\u914d\u6b63\u5219\u5e76\u4fdd\u5b58\u4e3a[\u5185\u5bb9]\u6807\u7b7e\u4ee5\u4f9b\u8c03\u7528","tips_sign_group_only":"\u6355\u83b7\u7ec4\uff1a(?<content>[\\s\\S]*?)\uff0c\u5339\u914d\u6b63\u5219\u5e76\u4fdd\u5b58\u4e3a[\u5185\u5bb9]\u6807\u7b7e\u4ee5\u4f9b\u8c03\u7528","tips_regular":"\u53ef\u4f7f\u7528\u6b63\u5219\u8868\u8fbe\u5f0f","setting":"\u8bbe\u7f6e","setting_site":"\u7ad9\u70b9\u8bbe\u7f6e","set_site_verifycode":"\u5f00\u542f\u56fe\u7247\u9a8c\u8bc1\u7801","setting_caiji":"\u91c7\u96c6\u8bbe\u7f6e","set_caiji_auto":"\u5f00\u542f\u81ea\u52a8\u91c7\u96c6","set_caiji_run":"\u81ea\u52a8\u91c7\u96c6\u8fd0\u884c\u65b9\u5f0f","set_caiji_interval":"\u6bcf\u6b21\u91c7\u96c6\u95f4\u9694\u65f6\u95f4","set_caiji_num":"\u6bcf\u6b21\u91c7\u96c6\u6570\u91cf","set_caiji_timeout":"\u6700\u5927\u6267\u884c\u65f6\u95f4","setting_email":"\u90ae\u4ef6\u53d1\u9001\u8bbe\u7f6e","set_email_sender":"\u53d1\u4ef6\u4eba\u540d\u79f0","set_email_email":"\u53d1\u4ef6\u4eba\u90ae\u7bb1\u8d26\u53f7","set_email_pwd":"\u53d1\u4ef6\u4eba\u90ae\u7bb1\u5bc6\u7801","set_email_smtp":"SMTP\u670d\u52a1\u5668","set_email_port":"SMTP\u7aef\u53e3","set_email_port_tips":"TLS\u4e00\u822c\u4e3a25\uff0cSSL\u4e00\u822c\u4e3a465\uff0c\u54a8\u8be2\u90ae\u7bb1\u670d\u52a1\u5546\u83b7\u53d6","set_email_type":"SMTP\u7aef\u53e3\u7c7b\u578b","set_email_test_subject":"\u6d4b\u8bd5\u53d1\u9001\u90ae\u4ef6","set_email_test_body":"\u606d\u559c\uff0c\u53d1\u9001\u90ae\u4ef6\u6210\u529f\uff01","config_error_none_email":"\u6ca1\u6709\u90ae\u7bb1\u670d\u52a1\u5668\u914d\u7f6e\uff0c\u8bf7\u5728\u540e\u53f0\u8bbe\u7f6e\uff01","user":"\u7528\u6237","user_list":"\u7528\u6237\u5217\u8868","user_add":"\u6dfb\u52a0\u7528\u6237","user_edit":"\u7f16\u8f91\u7528\u6237","user_username":"\u7528\u6237\u540d","user_password":"\u5bc6\u7801","user_repassword":"\u786e\u8ba4\u5bc6\u7801","user_newpwd_tips":"\u5982\u679c\u60a8\u60f3\u4fee\u6539\u5bc6\u7801\uff0c\u8bf7\u5728\u6b64\u8f93\u5165\u65b0\u5bc6\u7801\uff0c\u5426\u5219\u7559\u7a7a","user_email":"\u90ae\u7bb1","user_email_tips":"\u7528\u4e8e\u627e\u56de\u8d26\u53f7\u5bc6\u7801","user_groupid":"\u7528\u6237\u7ec4","user_error_edit_not_allow":"\u53ea\u6709\u521b\u59cb\u4eba\u624d\u80fd\u7f16\u8f91\u4ed6\u4eba\u7684\u8d26\u53f7\uff01","user_error_delete_not_allow":"\u53ea\u6709\u521b\u59cb\u4eba\u624d\u80fd\u5220\u9664\u8d26\u53f7\uff01","user_error_email":"\u90ae\u7bb1\u683c\u5f0f\u9519\u8bef\uff01","user_error_groupid":"\u4e0d\u662f\u5141\u8bb8\u7684\u7528\u6237\u7ec4\uff01","user_error_del_founder":"\u4e0d\u80fd\u5220\u9664\u521b\u59cb\u4eba\u8d26\u53f7\uff01","user_error_null_uid":"UID\u4e0d\u80fd\u4e3a\u7a7a","user_error_empty_user":"\u7528\u6237\u4e0d\u5b58\u5728","user_error_login":"\u7528\u6237\u540d\u6216\u5bc6\u7801\u4e0d\u6b63\u786e\uff01","user_error_sublogin":"\u767b\u5f55\u63d0\u4ea4\u5931\u8d25","user_error_is_not_admin":"\u62b1\u6b49\uff0c\u8bf7\u767b\u5f55\u7ba1\u7406\u5458\u8d26\u53f7\uff01","user_login_in":"\u767b\u5f55\u4e2d...","user_auto_login":"\u6b63\u5728\u81ea\u52a8\u767b\u5f55...","usertoken_error":"\u7528\u6237token\u9519\u8bef\uff0c\u8bf7\u5237\u65b0\u754c\u9762\u91cd\u65b0\u83b7\u53d6\u6216\u6e05\u9664\u6d4f\u89c8\u5668\u7f13\u5b58\uff01","task":"\u4efb\u52a1","task_add":"\u6dfb\u52a0\u4efb\u52a1","task_edit":"\u7f16\u8f91\u4efb\u52a1","task_list":"\u4efb\u52a1\u5217\u8868","task_change_list":"\u5207\u6362\u5217\u8868\u6a21\u5f0f","task_change_folder":"\u5207\u6362\u5206\u7ec4\u6a21\u5f0f","task_name":"\u4efb\u52a1\u540d\u79f0","task_tg":"\u4efb\u52a1\u5206\u7ec4","task_sort":"\u6392\u5e8f","task_sort_help":"\u6570\u5b57\u8d8a\u5927\u8d8a\u9760\u524d","task_module":"\u91c7\u96c6\u6a21\u5757","task_module_":"\u65e0","task_module_pattern":"\u89c4\u5219\u91c7\u96c6","task_module_keyword":"\u5173\u952e\u8bcd\u91c7\u96c6","task_module_weixin":"\u5fae\u4fe1\u91c7\u96c6","task_auto":"\u81ea\u52a8\u91c7\u96c6","task_addtime":"\u6dfb\u52a0\u65f6\u95f4","task_caijitime":"\u91c7\u96c6\u65f6\u95f4","task_edit_collector":"\u4e0b\u4e00\u6b65\uff1a\u7f16\u8f91\u91c7\u96c6\u5668","task_root":"\u6839\u76ee\u5f55","task_loading":"\u6b63\u5728\u8f7d\u5165\u6570\u636e","task_none_data":"\u65e0\u6570\u636e","task_caiji_ing":"\u6b63\u5728\u91c7\u96c6","task_set_task":"\u4efb\u52a1\u8bbe\u7f6e","task_set_collector":"\u91c7\u96c6\u5668\u8bbe\u7f6e","task_set_release":"\u53d1\u5e03\u8bbe\u7f6e","task_error_null_id":"\u8bf7\u8f93\u5165\u4efb\u52a1id","task_error_empty_task":"\u4e0d\u5b58\u5728\u4efb\u52a1","task_error_null_tgid":"\u8bf7\u8f93\u5165\u5206\u7ec4id","task_error_empty_tg":"\u4e0d\u5b58\u5728\u5206\u7ec4","task_error_null_name":"\u8bf7\u8f93\u5165\u540d\u79f0\uff01","task_error_has_name":"\u540d\u79f0\u5df2\u7ecf\u5b58\u5728\uff01","task_error_null_module":"\u672a\u8bbe\u7f6e\u91c7\u96c6\u6a21\u5757","taskgroup_add":"\u6dfb\u52a0\u5206\u7ec4","taskgroup_edit":"\u7f16\u8f91\u5206\u7ec4","taskgroup":"\u4efb\u52a1\u5206\u7ec4","taskgroup_list":"\u5206\u7ec4\u5217\u8868","taskgroup_name":"\u5206\u7ec4\u540d\u79f0","taskgroup_sort":"\u6392\u5e8f","taskgroup_sort_help":"\u6570\u5b57\u8d8a\u5927\u8d8a\u9760\u524d","taskgroup_parent_id":"\u7236\u5206\u7ec4","tg_add_sub":"\u6dfb\u52a0\u5b50\u5206\u7ec4","tg_move":"\u79fb\u52a8\u5206\u7ec4","tg_exist_sub":"\u5b58\u5728\u5b50\u5206\u7ec4\uff01","tg_none":"\u5206\u7ec4\u4e0d\u5b58\u5728\uff01","tg_is_parent":"\u5b58\u5728\u5b50\u5206\u7ec4\uff0c\u8bf7\u5148\u6e05\u7a7a\u5b50\u5206\u7ec4\uff0c\u624d\u80fd\u79fb\u52a8\u5206\u7ec4\uff01","tg_deleteall_has_sub":"\u6709\u5b50\u5206\u7ec4\u7684\u4e0d\u80fd\u5220\u9664\uff0c\u9700\u5148\u6e05\u7a7a\u5b50\u5206\u7ec4\uff01","tg_no_checked":"\u6ca1\u6709\u9009\u4e2d\u7684\u8bb0\u5f55\uff01","tg_error_null_name":"\u8bf7\u8f93\u5165\u540d\u79f0\uff01","tg_error_has_name":"\u540d\u79f0\u5df2\u7ecf\u5b58\u5728\uff01","coll_set":"\u91c7\u96c6\u5668\u8bbe\u7f6e","coll_edit_task":"\u4e0a\u4e00\u6b65\uff1a\u7f16\u8f91\u4efb\u52a1","coll_name":"\u91c7\u96c6\u89c4\u5219\u540d\u79f0","coll_error_invalid_module":"\u65e0\u6548\u7684\u91c7\u96c6\u6a21\u5757","coll_error_empty_coll":"\u4e0d\u5b58\u5728\u91c7\u96c6\u5668","coll_error_empty_effective":"\u9875\u9762\u811a\u672c\u4e0d\u53ef\u7528\uff0c\u4fdd\u5b58\u5931\u8d25\uff01","field_module_rule":"\u89c4\u5219\u5339\u914d","field_module_auto":"\u81ea\u52a8\u83b7\u53d6","field_module_xpath":"XPath\u5339\u914d","field_module_words":"\u56fa\u5b9a\u6587\u5b57","field_module_num":"\u968f\u673a\u6570\u5b57","field_module_time":"\u65f6\u95f4","field_module_list":"\u968f\u673a\u62bd\u53d6","field_module_json":"JSON\u63d0\u53d6","field_module_merge":"\u5b57\u6bb5\u7ec4\u5408","field_module_extract":"\u5b57\u6bb5\u63d0\u53d6","process_module_html":"html\u6807\u7b7e\u8fc7\u6ee4","process_module_replace":"\u5185\u5bb9\u66ff\u6362","process_module_filter":"\u5173\u952e\u8bcd\u8fc7\u6ee4","process_module_if":"\u6761\u4ef6\u5224\u65ad","process_module_translate":"\u7ffb\u8bd1","process_module_tool":"\u5de5\u5177\u7bb1","process_module_batch":"\u6279\u91cf\u66ff\u6362","process_module_substr":"\u622a\u53d6\u5b57\u7b26\u4e32","process_module_func":"\u4f7f\u7528\u51fd\u6570","process_module_api":"\u8c03\u7528\u63a5\u53e3","p_m_if_1":"\u6ee1\u8db3\u6761\u4ef6\u91c7\u96c6","p_m_if_2":"\u6ee1\u8db3\u6761\u4ef6\u4e0d\u91c7\u96c6","p_m_if_3":"\u4e0d\u6ee1\u8db3\u6761\u4ef6\u91c7\u96c6","p_m_if_4":"\u4e0d\u6ee1\u8db3\u6761\u4ef6\u4e0d\u91c7\u96c6","rele_set":"\u53d1\u5e03\u8bbe\u7f6e","rele_error_detect_null":"\u6ca1\u6709\u68c0\u6d4b\u5230\u672c\u5730CMS\u7a0b\u5e8f\uff0c\u60a8\u53ef\u4ee5\u624b\u52a8\u7ed1\u5b9a\u6570\u636e","rele_error_empty_rele":"\u53d1\u5e03\u8bbe\u7f6e\u4e0d\u5b58\u5728","rele_error_null_module":"\u8bf7\u9009\u62e9\u53d1\u5e03\u65b9\u5f0f","rele_error_db":"\u6570\u636e\u5e93\u9519\u8bef\uff1a","rele_error_no_table":"\u8be5\u6570\u636e\u5e93\u6ca1\u6709\u8868","rele_success_db_ok":"\u6570\u636e\u5e93\u8fde\u63a5\u6210\u529f","rele_module":"\u53d1\u5e03\u65b9\u5f0f","rele_module_cms":"\u672c\u5730CMS\u7a0b\u5e8f","rele_module_db":"\u6570\u636e\u5e93","rele_module_api":"\u751f\u6210API","rele_module_toapi":"\u8c03\u7528\u63a5\u53e3","rele_module_file":"\u6587\u4ef6\u5b58\u50a8","rele_module_diy":"\u81ea\u5b9a\u4e49\u63d2\u4ef6","rele_btn_detect":"\u5f00\u59cb\u68c0\u6d4b","rele_cms_path":"CMS\u8def\u5f84","rele_db_type":"\u6570\u636e\u5e93\u7c7b\u578b","rele_db_host":"\u6570\u636e\u5e93\u4e3b\u673a","rele_db_name":"\u6570\u636e\u5e93\u540d\u79f0","rele_db_charset":"\u6570\u636e\u5e93\u7f16\u7801","rele_db_port":"\u6570\u636e\u5e93\u7aef\u53e3","rele_db_user":"\u6570\u636e\u5e93\u7528\u6237","rele_db_pwd":"\u6570\u636e\u5e93\u5bc6\u7801","error_unknown_database":"\u672a\u77e5\u7684\u6570\u636e\u5e93","error_null_input":"\u8bf7\u8f93\u5165{:str}","collected":"\u5df2\u91c7\u96c6\u6570\u636e","collected_list":"\u5df2\u91c7\u96c6\u6570\u636e\u5217\u8868","COLLECTED_RELE_":"\u65e0","collected_rele_cms":"CMS","collected_rele_db":"\u6570\u636e\u5e93","collected_rele_file":"\u6587\u4ef6","collected_rele_toapi":"\u63a5\u53e3","collected_rele_api":"API","collected_rele_diy":"\u63d2\u4ef6","verifycode":"\u9a8c\u8bc1\u7801","verifycode_error":"\u9a8c\u8bc1\u7801\u9519\u8bef\uff01","find_password":"\u627e\u56de\u5bc6\u7801","find_pwd_username":"\u8bf7\u8f93\u5165\u90ae\u7bb1\/\u7528\u6237\u540d","find_pwd_yzm":"\u8bf7\u8f93\u5165\u6fc0\u6d3b\u7801","find_pwd_resend":"\u91cd\u65b0\u53d1\u9001","find_pwd_next_step":"\u4e0b\u4e00\u6b65","find_pwd_pwd":"\u8bf7\u8f93\u5165\u65b0\u5bc6\u7801","find_pwd_repwd":"\u786e\u8ba4\u65b0\u5bc6\u7801","find_pwd_sended":"\u5df2\u5411\u90ae\u7bb1{:email}\u53d1\u9001\u4e86\u6fc0\u6d3b\u7801\uff01","find_pwd_email_failed":"\u53d1\u9001\u90ae\u4ef6\u5931\u8d25\uff0c\u8bf7\u68c0\u67e5\u540e\u53f0\u53d1\u9001\u90ae\u4ef6\u914d\u7f6e\uff01","find_pwd_email_wait":"\u9700\u7b49\u5f85{:seconds}\u79d2\u624d\u80fd\u518d\u6b21\u53d1\u9001","find_pwd_email_subject":"\u627e\u56de\u5bc6\u7801 - \u84dd\u5929\u91c7\u96c6\u5668","find_pwd_email_body":"\u60a8\u7684\u6fc0\u6d3b\u7801\u4e3a\uff1a{:yzm}\uff0c\u6709\u6548\u65f6\u95f4{:minutes}\u5206\u949f","find_pwd_error_username":"\u8bf7\u8f93\u5165\u90ae\u7bb1\/\u7528\u6237\u540d","find_pwd_error_step":"\u6b65\u9aa4\u9519\u8bef\uff0c\u8bf7\u91cd\u65b0\u64cd\u4f5c\uff01","find_pwd_error_post":"\u8868\u5355\u63d0\u4ea4\u5931\u8d25","find_pwd_error_none_email":"\u90ae\u7bb1\u4e0d\u5b58\u5728\uff01","find_pwd_error_multiple_emails":"\u5b58\u5728\u591a\u4e2a\u7528\u6237\u4f7f\u7528\u6b64\u90ae\u7bb1\uff0c\u8bf7\u8f93\u5165\u7528\u6237\u540d\uff01","find_pwd_error_none_user":"\u7528\u6237\u4e0d\u5b58\u5728\uff01","find_pwd_success":"\u5bc6\u7801\u4fee\u6539\u6210\u529f","yzm_error_please_send":"\u8bf7\u53d1\u9001\u6fc0\u6d3b\u7801","yzm_error_please_input":"\u8bf7\u8f93\u5165\u6fc0\u6d3b\u7801","yzm_error_timeout":"\u6fc0\u6d3b\u7801\u5df2\u8fc7\u671f\uff01\u8bf7\u91cd\u65b0\u53d1\u9001","yzm_error_yzm":"\u6fc0\u6d3b\u7801\u9519\u8bef","admincp_style":"\u754c\u9762","admincp_sidebar_mini":"\u83dc\u5355\u6700\u5c0f\u5316","admincp_skins":"\u8bbe\u7f6e\u76ae\u80a4","skin_blue":"\u84dd","skin_black":"\u9ed1","skin_purple":"\u7d2b","skin_green":"\u7eff","skin_red":"\u7ea2","skin_yellow":"\u9ec4","skin_blue_light":"\u84dd\u4eae","skin_black_light":"\u9ed1\u4eae","skin_purple_light":"\u7d2b\u4eae","skin_green_light":"\u7eff\u4eae","skin_red_light":"\u7ea2\u4eae","skin_yellow_light":"\u9ec4\u4eae","store":"\u4e91\u5e73\u53f0","rule_collect":"\u91c7\u96c6\u89c4\u5219","empty_data":"\u6570\u636e\u4e0d\u5b58\u5728","invalid_op":"\u65e0\u6548\u7684\u64cd\u4f5c\uff01","submit":"\u63d0\u4ea4","search":"\u641c\u7d22","op_success":"\u64cd\u4f5c\u6210\u529f","op_failed":"\u64cd\u4f5c\u5931\u8d25","sort":"\u6392\u5e8f","select":"\u9009\u62e9","select_all":"\u5168\u9009","select_first":"\u8bf7\u9009\u62e9","save":"\u4fdd\u5b58","op":"\u64cd\u4f5c","delete":"\u5220\u9664","deleted":"\u5df2\u5220\u9664","edit":"\u7f16\u8f91","test":"\u6d4b\u8bd5","confirm_delete":"\u786e\u5b9a\u5220\u9664\uff1f","delete_success":"\u5220\u9664\u6210\u529f","none":"\u65e0","caiji":"\u91c7\u96c6","more":"\u66f4\u591a","yes":"\u662f","no":"\u5426","all":"\u5168\u90e8","login":"\u767b\u5f55","logout":"\u9000\u51fa","login_auto":"\u4e0b\u6b21\u81ea\u52a8\u767b\u5f55","separator":"\uff1a","redirecting":"\u8df3\u8f6c\u4e2d...","close":"\u5173\u95ed","return_home":"{:time}\u79d2\u949f\u540e\u8fd4\u56de\u9875\u9762<\/a>","tips_match":"\u793a\u4f8b\uff1a<div id="a">[\u5185\u5bb91]<\/div>(*)<div id="b">[\u5185\u5bb92]<\/div>","tips_matchn":"\u793a\u4f8b\uff1a[\u5185\u5bb91] [\u5185\u5bb92]","tips_match_only":"\u793a\u4f8b\uff1a<div id="content">[\u5185\u5bb9]<\/div>","tips_match_url":"\u793a\u4f8b\uff1a<a href="http:\/\/demo.com\/[\u5185\u5bb91]\/[\u5185\u5bb92]">(*)<\/a>","tips_matchn_url":"\u793a\u4f8b\uff1ahttp:\/\/www.demo.com\/[\u5185\u5bb91]-[\u5185\u5bb92].html","release_upgrade":"\u63d2\u4ef6\u7248\u672c\u8fc7\u4f4e\uff0c\u8bf7\u5347\u7ea7\u63d2\u4ef6 \u5347\u7ea7\u6559\u7a0b<\/a>"}; \ No newline at end of file diff --git a/runtime/index.html b/runtime/index.html new file mode 100644 index 0000000..e69de29 diff --git a/vendor/composer/autoload_namespaces.php b/vendor/composer/autoload_namespaces.php index faee5f8..96c8b83 100644 --- a/vendor/composer/autoload_namespaces.php +++ b/vendor/composer/autoload_namespaces.php @@ -6,6 +6,5 @@ $vendorDir = dirname(dirname(__FILE__)); $baseDir = dirname($vendorDir); return array( - 'Requests' => array($vendorDir . '/rmccue/requests/library'), 'PHPExcel' => array($vendorDir . '/phpoffice/phpexcel/Classes'), ); diff --git a/vendor/composer/autoload_static.php b/vendor/composer/autoload_static.php index 7e2df4b..f88a617 100644 --- a/vendor/composer/autoload_static.php +++ b/vendor/composer/autoload_static.php @@ -97,13 +97,6 @@ class ComposerStaticInit34a41e2841af1a67f3ddef099fc7b348 ); public static $prefixesPsr0 = array ( - 'R' => - array ( - 'Requests' => - array ( - 0 => __DIR__ . '/..' . '/rmccue/requests/library', - ), - ), 'P' => array ( 'PHPExcel' => diff --git a/vendor/composer/installed.json b/vendor/composer/installed.json index 86c149c..2999844 100644 --- a/vendor/composer/installed.json +++ b/vendor/composer/installed.json @@ -42,57 +42,6 @@ } ] }, - { - "name": "rmccue/requests", - "version": "v1.7.0", - "version_normalized": "1.7.0.0", - "source": { - "type": "git", - "url": "https://github.com/rmccue/Requests.git", - "reference": "87932f52ffad70504d93f04f15690cf16a089546" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/rmccue/Requests/zipball/87932f52ffad70504d93f04f15690cf16a089546", - "reference": "87932f52ffad70504d93f04f15690cf16a089546", - "shasum": "" - }, - "require": { - "php": ">=5.2" - }, - "require-dev": { - "requests/test-server": "dev-master" - }, - "time": "2016-10-13 00:11:37", - "type": "library", - "installation-source": "dist", - "autoload": { - "psr-0": { - "Requests": "library/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "ISC" - ], - "authors": [ - { - "name": "Ryan McCue", - "homepage": "http://ryanmccue.info" - } - ], - "description": "A HTTP library written in PHP, for human beings.", - "homepage": "http://github.com/rmccue/Requests", - "keywords": [ - "curl", - "fsockopen", - "http", - "idna", - "ipv6", - "iri", - "sockets" - ] - }, { "name": "phpmailer/phpmailer", "version": "v5.2.27",
    {$Think.lang.select}
    +
    {$pagenav}{$pagenav}
    '+infoStr+'