diff --git a/README.md b/README.md index d797a2f..c4db86f 100755 --- a/README.md +++ b/README.md @@ -66,15 +66,29 @@ RewriteRule config/(.*).(php)$ – [F]
点击查看2.0版更新日志 -* 2021年10月30日 v2.3.1 +* 2021-11-5 v2.4.0 +- 增加统计缓存 +- 增加最近30天上传统计与占用空间图表 +- 增加初始化安装(可能会不支持二级目录安装,可删除install文件夹初始化) +- 增加在线编辑配置(之前是需要修改config.php文件,现在可以直接网站端修改了) +- 删除广场会导致浏览速度变慢的代码 +- 删除快捷配置会导致浏览速度变慢的代码 + + +* 2021-11-3 v2.3.2 +- 增加广场图片缓存 +- 重构广场样式 + +* 2021-11-3 v2.3.1 - 增加监黄接口 -- 增加对php5.6-php8.0的支持 -- 修复二级目录无法使用 +- 增加审核违规图片 +- 修复对php5.6的支持 +- 修复二级目录的安装 * 2021-10-24 v2.3.0 - 将服务器环境监测改为第一次打开时自动检测(如需再次展示需删除config目录下的EasyImage.lock) - 增加快捷操作中心显示服务信息 -- 增加自定义上传文件的命名方式(详见config.php文件里的注释) +- 增加对上传文件的命名方式(详见config.php文件里的注释) - 增加隐私政策、服务条款、DMCA - 增加自定义静态文件CDN源 - 增加dns-prefetch @@ -134,7 +148,7 @@ RewriteRule config/(.*).(php)$ – [F] deny all; } ``` -- - 或者参考:https://www.545141.com/981.html +- - 或者参考:https://www.545141.com/992.html https://www.545141.com/939.html - 一些精简 * 2021-4-14 v2.0.2.1 Dev1 @@ -200,7 +214,13 @@ RewriteRule config/(.*).(php)$ – [F] - 全新的压缩 将文件继续缩小 - 全新的目录系统,精简代码 - 设置仅允许在config.php修改,注释更加明了,即使没有代码基础也可以操作 -- 增加新的文件管理系统 +- 增加新的文件管理系统,感谢 tinyfilemanager +- ~~支持文字/图片水印 可自定义文字颜色~~ +- ~~支持文字水印背景颜色~~ +- ~~支持文字水印透明度~~ +- ~~支持删除远程上传文件~~ -> 不再支持删除远程文件 +- ~~(支持开启/关闭api自定义文字水印)~~ +- ~~支持删除自定义删除图片(仅管理员)~~
diff --git a/admin/admin.inc.php b/admin/admin.inc.php new file mode 100755 index 0000000..530c576 --- /dev/null +++ b/admin/admin.inc.php @@ -0,0 +1,431 @@ + new $.zui.Messager("请登录后再修改!", {type: "danger" // 定义颜色主题 + }).show();'; + exit(require_once APP_ROOT . '/application/login.php'); +} + +if (isset($_POST['form'])) { + $postArr = $_POST; + $new_config = array_replace($config, $postArr); + $config_file = APP_ROOT.'/config/config.php'; + cache_write($config_file,$new_config); + echo ' + + '; + header("refresh:1;"); +} + + +?> +
+
+ +
+
+
+
+
+ + +
+
+ + +
+
+ +
+ + + + +
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+ + > + +
+
+
+ + +
+
+ +
+ +
+
+
+
+
+
+ + > + +
+
+
+
+ + > + +
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+ + title=" 轻微有损压缩图片, 此压缩有可能使图片变大!特别是小图片 也有一定概率改变图片方向"> + +
+
+
+ + +
+
+ + + +
+
+ + + +
+
+ + + +
+
+ + + +
+
+
+ + > + +
+
+
+ + +
+
+ + +
+
+ +
+ +
+
+
+
+
+
+ + > + +
+
+
+ + +
+
+
+ + > + +
+
+
+ + +
+
+ + +
+
+ +
+ +
+
+
+
+
+ + +
+
+ +
+ +
+
+
+
+
+
+ + > + +
+
+
+ + + +
+
+
+ + > + +
+
+
+
+ + title="开启后会受服务器到https://moderatecontent.com速度影响,国内不建议开启!"> + +
+
+
+ + +
+
+ + + +
+
+

当前软件版本:

+
+
+ +
+ +
+
+
+
+
+
+ + + + +
@@ -79,21 +98,26 @@ if(isset($_GET['reimg'])){

访问者IP:

图床信息

-

-

当前版本:,Github版本:

https://tinypng.com/developers
'; } else { echo '压缩图片 TinyImag Key已填写
'; } - if (empty($moderatecontent['key'])) { - echo '图片检查 moderatecontent key未填写,申请地址: https://moderatecontent.com/'; + if (empty($config['moderatecontent_key'])) { + echo '图片检查 moderatecontent key未填写,申请地址: https://client.moderatecontent.com/'; } else { echo '图片检查 moderatecontent key已填写'; } - ?>

+ ?> +

+

当前版本:,Github版本:

+

统计时间: ' . read_total_json('total_time') . ';文件夹:' . read_total_json('dirnum') . '个;托管图片:' . read_total_json('filenum') . '张;占用:' . read_total_json('usage_space') . ';缓存周期:每小时。'; + ?> +

@@ -130,7 +154,7 @@ if(isset($_GET['reimg'])){
-
+
- +
@@ -192,11 +216,17 @@ if(isset($_GET['reimg'])){
-
+
+
+

上传统计(每日更新)

+ +

单位:上传/个 占用/Mb 统计时间:

+

+

@@ -204,8 +234,8 @@ if(isset($_GET['reimg'])){

-

为了服务器的稳定,仅显示最近20张图片;监黄需要在config.php中开启checkImg属性。

-

key申请地址:https://moderatecontent.com

+

为了访问速度,仅显示最近20张图片;监黄需要在config.php中开启checkImg属性。

+

key申请地址:https://client.moderatecontent.com/

获得key后填入/config/api_key.php->moderatecontent属性

@@ -214,7 +244,6 @@ if(isset($_GET['reimg'])){ - @@ -224,28 +253,26 @@ if(isset($_GET['reimg'])){ - - + '; @@ -253,7 +280,7 @@ if(isset($_GET['reimg'])){ echo ' 总数:' . $cache_num . '  - + '; @@ -263,11 +290,22 @@ if(isset($_GET['reimg'])){ +
+
+ 已缓存个文件 + 缓存占用 + + +
+ + - @@ -93,4 +93,4 @@ require_once APP_ROOT . '/libs/header.php'; - EasyImage2.0'; -require_once '../../libs/function.php'; -require_once APP_ROOT . '/libs/header.php'; +require_once '../../application/function.php'; +require_once APP_ROOT . '/application/header.php'; require_once APP_ROOT . '/config/api_key.php'; // 如果关闭Api上传并且没有登录的情况下关闭测试接口 @@ -10,7 +10,7 @@ if (!$config['apiStatus'] and !is_online()) { new $.zui.Messager("Api关闭,请登录", {type: "danger" // 定义颜色主题 }).show(); // 延时2s跳转 - window.setTimeout("window.location=\'/../libs/login.php \'",2000); + window.setTimeout("window.location=\'/../application/login.php \'",2000); '); } @@ -35,4 +35,4 @@ if (!$config['apiStatus'] and !is_online()) { -uploaded) { // 压缩图片 后压缩模式,不影响前台输出速度 if (!isAnimatedGif($handle->file_dst_pathname)) if ($config['compress']) { - require '../libs/compress/Imagick/class.Imgcompress.php'; + require '../application/compress/Imagick/class.Imgcompress.php'; $img = new Imgcompress($handle->file_dst_pathname, 1); $img->compressImg($handle->file_dst_pathname); // 释放 diff --git a/libs/WaterMask.php b/application/WaterMask.php similarity index 100% rename from libs/WaterMask.php rename to application/WaterMask.php diff --git a/application/chart.php b/application/chart.php new file mode 100755 index 0000000..a46194a --- /dev/null +++ b/application/chart.php @@ -0,0 +1,119 @@ + getFileNumber($total_contents . $count_day[$i])]; + } + + for ($i = 0; $i < count($count_day); $i++) { + // 统计每日占用空间 + $count_contents['chart_disk'][] = [$count_day[$i] => getDirectorySize($total_contents . $count_day[$i])]; + } + + $count_contents['total_time'] = date('Y-m-d H:i:s'); // 统计时间 + $count_contents['date'] = date('Ymd'); // 校对时间 + + $count_contents = json_encode($count_contents, true); // serialize存储文件 + file_put_contents($chart_total_file, $count_contents); // 存储文件 +} + +function read_chart_total() +{ + global $chart_total_file; + + if (is_file($chart_total_file)) { + + $chart_total_file = file_get_contents($chart_total_file); + $chart_total_file = json_decode($chart_total_file, true); + + + if ($chart_total_file['date'] !== date('Ymd')) { + write_chart_total(); + } else { + for ($i = 0; $i < count($chart_total_file['chart_data']); $i++) { + // 读取每日上传数量 + foreach ($chart_total_file['chart_data'][$i] as $key => $value) { + $chart_data_date[] = '"' . $key . '" ,'; + $chart_data_num[] = '"' . $value . '" ,'; + //echo $key . '
'; + //echo $value . '
'; + } + foreach ($chart_total_file['chart_disk'][$i] as $value) { + $value = round($value / 1024 / 1024, 2); + + $chart_total_disk[] = '"' . $value . '" ,'; + } + } + + return array('date' => $chart_data_date, 'number' => $chart_data_num, 'disk' => $chart_total_disk,'total_time'=>$chart_total_file['total_time']); + } + } else { + write_chart_total(); + } + + + /* + switch ($name) { + case 'date': + return $chart_data_date; + break; + case 'number': + return $chart_data_num; + case 'disk': + return $chart_total_disk; + break; + default: + return $chart_data_date; + } + */ +} +/* + +$char_data = read_chart_total(); +$chart_date = $char_data['date']; +$chart_number = $char_data['number']; +$chart_disk = $char_data['disk']; +var_dump($char_data['disk']); +var_dump($char_data['number']); + +*/ \ No newline at end of file diff --git a/libs/class.thumb.php b/application/class.thumb.php similarity index 89% rename from libs/class.thumb.php rename to application/class.thumb.php index 478eb85..6268381 100755 --- a/libs/class.thumb.php +++ b/application/class.thumb.php @@ -90,8 +90,12 @@ class Thumb{ # 创建新的背景画布 if($height>=$thumb_h){ $thumb=imagecreatetruecolor($width,$thumb_h); + @imagealphablending($thumb, false); //这里很重要,意思是不合并颜色,直接用$img图像颜色替换,包括透明色;2-1 + @imagesavealpha($thumb, true); //这里很重要,意思是不要丢了$thumb图像的透明色;2-2 EasyImage修改 }else{ $thumb=imagecreatetruecolor($width,$height); + @imagealphablending($thumb, false); //这里很重要,意思是不合并颜色,直接用$img图像颜色替换,包括透明色;2-1 + @imagesavealpha($thumb, true); //这里很重要,意思是不要丢了$thumb图像的透明色;2-2 EasyImage修改 $thumb_h=$height; } diff --git a/libs/class.upload.php b/application/class.upload.php similarity index 100% rename from libs/class.upload.php rename to application/class.upload.php diff --git a/libs/class.version.php b/application/class.version.php similarity index 81% rename from libs/class.version.php rename to application/class.version.php index 13cad38..3d8e696 100755 --- a/libs/class.version.php +++ b/application/class.version.php @@ -16,9 +16,9 @@ class getVerson public function readJson() { - if (file_exists(__DIR__ . '/verson.json')) { - $file = fopen(__DIR__ . '/verson.json', 'r'); - $test = fread($file, filesize(__DIR__ . '/verson.json')); + if (file_exists(__DIR__ . '/../i/cache/verson.json')) { + $file = fopen(__DIR__ . '/../i/cache/verson.json', 'r'); + $test = fread($file, filesize(__DIR__ . '/../i/cache/verson.json')); $verson = json_decode($test, true); return $verson['tag_name']; fclose($file); @@ -32,7 +32,7 @@ class getVerson $verson = $this->geturl($this->url); $verson = json_decode($verson, true); - $file = fopen(__DIR__ . '/verson.json', 'w+'); + $file = fopen(__DIR__ . '/../i/cache/verson.json', 'w+'); fwrite($file, $verson); fclose($file); } diff --git a/libs/compress/Imagick/class.Imgcompress.php b/application/compress/Imagick/class.Imgcompress.php similarity index 100% rename from libs/compress/Imagick/class.Imgcompress.php rename to application/compress/Imagick/class.Imgcompress.php diff --git a/libs/compress/TinyImg/TinyImg.php b/application/compress/TinyImg/TinyImg.php similarity index 100% rename from libs/compress/TinyImg/TinyImg.php rename to application/compress/TinyImg/TinyImg.php diff --git a/libs/compress/TinyImg/cacert.pem b/application/compress/TinyImg/cacert.pem similarity index 100% rename from libs/compress/TinyImg/cacert.pem rename to application/compress/TinyImg/cacert.pem diff --git a/libs/compress/function.compress.php b/application/compress/function.compress.php similarity index 82% rename from libs/compress/function.compress.php rename to application/compress/function.compress.php index 9d49e6e..581df79 100755 --- a/libs/compress/function.compress.php +++ b/application/compress/function.compress.php @@ -4,8 +4,8 @@ * 压缩文件函数调用位置 */ require_once __DIR__ . '/../function.php'; -require_once APP_ROOT . '/libs/compress/Imagick/class.Imgcompress.php'; -require_once APP_ROOT . '/libs/compress/TinyImg/TinyImg.php'; +require_once APP_ROOT . '/application/compress/Imagick/class.Imgcompress.php'; +require_once APP_ROOT . '/application/compress/TinyImg/TinyImg.php'; require_once APP_ROOT . '/config/api_key.php'; /** @@ -14,7 +14,6 @@ require_once APP_ROOT . '/config/api_key.php'; */ function compress($floder, $type = 'Imgcompress', $source = '') { - global $tinyImag_key; global $config; ini_set('max_execution_time', '0'); // 脚本运行的时间(以秒为单位)0不限制 @@ -35,12 +34,12 @@ function compress($floder, $type = 'Imgcompress', $source = '') } if ($type == 'TinyImg') { - if (empty($tinyImag_key['TinyImag'])) { + if (empty($config['TinyImag_key'])) { exit('请先申请TinyImag key并保存再试!'); } $folder = '..' . $config['path'] . $source; $tinyImg = new TinyImg(); - $key = $tinyImag_key['TinyImag']; + $key = $config['TinyImag_key']; $input = $folder; //这个文件夹下的文件会被压缩 $output = $folder; //压缩的结果会被保存到这个文件夹中 $tinyImg->compressImgsFolder($key, $input, $output); diff --git a/libs/compressing.php b/application/compressing.php similarity index 93% rename from libs/compressing.php rename to application/compressing.php index 4d2ab3b..d5eb6ea 100755 --- a/libs/compressing.php +++ b/application/compressing.php @@ -4,7 +4,7 @@ * 压缩状态页面 */ require_once 'header.php'; -require_once APP_ROOT . '/libs/compress/function.compress.php'; +require_once APP_ROOT . '/application/compress/function.compress.php'; // 检测登录 if (!is_online()) { diff --git a/api/del.php b/application/del.php similarity index 75% rename from api/del.php rename to application/del.php index 473b065..e4cb755 100755 --- a/api/del.php +++ b/application/del.php @@ -3,8 +3,7 @@ /** * 删除文件页面 */ -require_once '../libs/header.php'; -require_once APP_ROOT . '/libs/function.php'; +require_once './header.php'; echo '
@@ -22,18 +21,14 @@ if (empty($_REQUEST)) { '; - header("refresh:3;url=".$config['domain'].""); + //header("refresh:3;url=".$config['domain'].""); } elseif (isset($_GET['url'])) { $img = $_GET['url']; echo '
-
-
图片属性:'.@getimagesize($img)[0].'px X '.@getimagesize($img)[1].'px
简单图床-EasyImage
'; } @@ -57,14 +52,14 @@ if (is_online()) { new $.zui.Messager("请登录后再删除", {type: "danger" // 定义颜色主题 }).show(); // 延时2s跳转 - // window.setTimeout("window.location=\'/../libs/login.php \'",2000); + window.setTimeout("window.location=\'/../application/login.php \'",2000); '; - header("refresh:2;url=".$config['domain']."/libs/login.php"); + //header("refresh:2;url=".$config['domain']."/application/login.php"); } } -require_once APP_ROOT . '/libs/footer.php'; +require_once APP_ROOT . '/application/footer.php'; ?> diff --git a/libs/function.php b/application/function.php similarity index 61% rename from libs/function.php rename to application/function.php index 53ed100..7a783bc 100755 --- a/libs/function.php +++ b/application/function.php @@ -1,5 +1,17 @@ window.location.href="' . get_whole_url('/') . '/install/index.php"'; + exit; + } + global $config; if ($mode) { // 扩展检测 @@ -416,8 +435,7 @@ function checkEnv($mode) } } // 检测是否更改默认域名 - $url = preg_replace('#^(http(s?))?(://)#', '', 'http://192.168.2.100'); - if (strstr($url, $_SERVER['HTTP_HOST'])) { + if (strstr('localhost', $_SERVER['HTTP_HOST'])) { echo ' '; } - // 检查环境配置 + // 上部内容 if (!is_file(APP_ROOT . '/config/EasyIamge.lock')) { echo '
@@ -552,7 +561,7 @@ function getVersion() global $config; if ($config['checkEnv']) { - require_once APP_ROOT . '/libs/class.version.php'; + require_once APP_ROOT . '/application/class.version.php'; // 获取版本地址 $url = "https://api.github.com/repositories/188228357/releases/latest"; $getVersion = new getVerson($url); @@ -603,9 +612,13 @@ function deldir($dir) // curl访问网站并返回解码过的json信息 function get_json($img) { - global $moderatecontent; + global $config; - $url = $moderatecontent['url'] . $moderatecontent['key'] . '&url=' . $img; + if (empty($config['moderatecontent_key'])) { + exit; + } + + $url = 'https://api.moderatecontent.com/moderate/?key=' . $config['moderatecontent_key'] . '&url=' . $img; $headerArray = array("Content-type:application/json;", "Accept:application/json"); $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); @@ -620,19 +633,19 @@ function get_json($img) return $output; } // 检查图片是否违规 -function checkImg($imageUrl, $mode = true) +function checkImg($imageUrl) { global $config; $response = get_json($imageUrl); if ($response['rating_index'] == 3 or $response['predictions']['adult'] > $config['checkImg_value']) { // (1 = everyone, 2 = teen, 3 = adult) - $old_path = APP_ROOT . parse_url($imageUrl)['path']; // 提交网址中的文件路径 /i/2021/10/29/p8vypd.png - //$name = date('Y_m_d').'_'.basename($imageUrl); // 文件名 p8vypd.png + //$old_path = APP_ROOT . parse_url($imageUrl)['path']; // 提交网址中的文件路径 /i/2021/10/29/p8vypd.png + $old_path = APP_ROOT . str_replace($config['imgurl'], '', $imageUrl);; // 提交网址中的文件路径 /i/2021/10/29/p8vypd.png $name = date('Y_m_d') . '_' . basename($imageUrl); // 文件名 2021_10_30_p8vypd.png - $new_path = APP_ROOT . $config['path'] . 'cache/' . $name; // 新路径含文件名 - $cache_dir = APP_ROOT . $config['path'] . 'cache/'; // cache路径 + $new_path = APP_ROOT . $config['path'] . 'suspic/' . $name; // 新路径含文件名 + $cache_dir = APP_ROOT . $config['path'] . 'suspic/'; // suspic路径 - if (is_dir($cache_dir)) { // 创建cache目录并移动 + if (is_dir($cache_dir)) { // 创建suspic目录并移动 rename($old_path, $new_path); } else { mkdir($cache_dir, 0777, true); @@ -646,8 +659,114 @@ function re_checkImg($name) { global $config; - $now_file = str_replace('_', '/', $name); // 当前图片相对位置 2021/10/30/p8vypd.png - $now_path_file = APP_ROOT . $config['path'] . 'cache/' . $name; // 当前图片绝对位置 */i/cache/2021_10_30_p8vypd.png - $to_file = APP_ROOT . $config['path'] . $now_file; // 还原图片的绝对位置 */i/2021/10/30/p8vypd.png + $fileToPath = str_replace('_', '/', $name); // 将图片名称还原为带路径的名称,eg:2021_11_03_pbmn1a.jpg =>2021/11/03/pbmn1a.jpg + $now_path_file = APP_ROOT . $config['path'] . 'suspic/' . $name; // 当前图片绝对位置 */i/suspic/2021_10_30_p8vypd.png + $to_file = APP_ROOT . $config['path'] . $fileToPath; // 要还原图片的绝对位置 */i/2021/10/30/p8vypd.png rename($now_path_file, $to_file); } +// 创建缩略图 +function creat_cache_images($imgName) +{ + require_once __DIR__ . '/class.thumb.php'; + global $config; + + $old_img_path = APP_ROOT . config_path() . $imgName; // 获取要创建缩略图文件的绝对路径 + $cache_path = APP_ROOT . $config['path'] . 'cache/'; // cache目录的绝对路径 + if (!isAnimatedGif($old_img_path)) { // 仅针对非gif创建图片缩略图 + if (is_dir($cache_path)) { + $new_imgName = APP_ROOT . $config['path'] . 'cache/' . date('Y_m_d') . '_' . $imgName; // 缩略图缓存的绝对路径 + Thumb::out($old_img_path, $new_imgName, 300, 300); // 保存缩略图 + } else { + mkdir($cache_path, 0777, true); // 创建cache目录 + } + } +} + +// 根据请求网址路径返回缩略图网址 +function back_cache_images($url) +{ + global $config; + $cache_image_file = str_replace($config['imgurl'], '', $url); + + if (isAnimatedGif(APP_ROOT . $cache_image_file)) { // 仅读取非gif的缩略图 + return $url; // 如果是gif则直接返回url + } else { + $cache_image_file = str_replace($config['path'], '', $cache_image_file); // 将网址中的/i/去除 + $cache_image_file = str_replace('/', '_', $cache_image_file); // 将文件的/转换为_ + $isFile = APP_ROOT . $config['path'] . 'cache/' . $cache_image_file; // 缓存文件的绝对路径 + if (file_exists($isFile)) { // 缓存文件是否存在 + return $config['imgurl'] . $config['path'] . 'cache/' . $cache_image_file; // 存在则返回缓存文件 + } else { + return $url; // 不存在直接返回url + } + } +} + +/** + * 获取当前页面完整URL地址 + * 返回 http://localhost/ww/index.php + * https://www.php.cn/php-weizijiaocheng-28181.html + * $search 返回指定搜索文字之前的内容(不含搜索文字) + */ + +function get_whole_url($search = null) +{ + $sys_protocal = isset($_SERVER['SERVER_PORT']) && $_SERVER['SERVER_PORT'] == '443' ? 'https://' : 'http://'; + $php_self = $_SERVER['PHP_SELF'] ? $_SERVER['PHP_SELF'] : $_SERVER['SCRIPT_NAME']; + $path_info = isset($_SERVER['PATH_INFO']) ? $_SERVER['PATH_INFO'] : ''; + $relate_url = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : $php_self . (isset($_SERVER['QUERY_STRING']) ? '?' . $_SERVER['QUERY_STRING'] : $path_info); + $whole_domain = $sys_protocal . (isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : '') . $relate_url; + if ($search) { + // 返回指定符号之前 + return substr($whole_domain, 0, strrpos($whole_domain, $search)); + } else { + return $whole_domain; + } +} + +//写入 +function cache_write($filename, $values, $var = 'config', $format = false) +{ + $cachefile = $filename; + $cachetext = " $val) { + $key = is_string($key) ? '\'' . addcslashes($key, '\'\\') . '\'' : $key; + $val = !is_array($val) && (!preg_match('/^\-?\d+$/', $val) || strlen($val) > 12) ? '\'' . addcslashes($val, '\'\\') . '\'' : $val; + if (is_array($val)) { + $evaluate .= $comma . $key . '=>' . arrayeval($val, $format, $level + 1); + } else { + $evaluate .= $comma . $key . '=>' . $val; + } + $comma = ',' . $line . $space; + } + $evaluate .= $line . $space . ')'; + return $evaluate; +} + +//写入文件 +function writefile($filename, $writetext, $openmod = 'w') +{ + if (false !== $fp = fopen($filename, $openmod)) { + flock($fp, 2); + fwrite($fp, $writetext); + fclose($fp); + return true; + } else { + return false; + } +} diff --git a/application/header.php b/application/header.php new file mode 100755 index 0000000..9bf08a4 --- /dev/null +++ b/application/header.php @@ -0,0 +1,80 @@ + + + + + + + + <?php echo $config['title']; ?> + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/libs/lang/class.upload.xx_XX.php b/application/lang/class.upload.xx_XX.php similarity index 100% rename from libs/lang/class.upload.xx_XX.php rename to application/lang/class.upload.xx_XX.php diff --git a/libs/lang/class.upload.zh_CN.php b/application/lang/class.upload.zh_CN.php similarity index 100% rename from libs/lang/class.upload.zh_CN.php rename to application/lang/class.upload.zh_CN.php diff --git a/libs/lang/class.upload.zh_TW.php b/application/lang/class.upload.zh_TW.php similarity index 100% rename from libs/lang/class.upload.zh_TW.php rename to application/lang/class.upload.zh_TW.php diff --git a/application/list.php b/application/list.php new file mode 100755 index 0000000..afacee1 --- /dev/null +++ b/application/list.php @@ -0,0 +1,311 @@ +管理员关闭了预览哦~~'; +} else { + $path = isset($_GET['date']) ? $_GET['date'] : date('Y/m/d/'); + $path = preg_replace("/^d{4}-d{2}-d{2} d{2}:d{2}:d{2}$/s", "", trim($path)); // 过滤非日期,删除空格 + + $keyNum = isset($_GET['num']) ? $_GET['num'] : $config['listNumber']; + $keyNum = preg_replace("/[^0-9]/", "", trim($keyNum)); // 过滤非数字,删除空格 + + $fileArr = getFile(APP_ROOT . config_path($path)); // 统计当日上传数量 + + if ($fileArr[0]) { + echo '
'; + foreach ($fileArr as $key => $value) { + if ($key < $keyNum) { + $imgUrl = $config['imgurl'] . config_path($path) . $value; + // 会导致速度变慢 + // $re_img = str_replace($config['imgurl'], '', $imgUrl); // 图片相对路径 /i/2021/11/03/hg82t4.jpg + //

' . @getimagesize($imgUrl)[0] . 'x' . @getimagesize($imgUrl)[1] . 'px ' . getDistUsed(filesize(APP_ROOT . $re_img)) . '

+ if (is_online()) { + echo ' +
+
+ 简单图床-EasyImage +
+ + + + +
+
+
+ '; + } else { + echo ' +
+
+ 简单图床-EasyImage +
+ + + +
+
+
+ '; + } + } + } + echo '
'; + } else { + + echo '
今天还没有上传的图片哟~~
快来上传第一张吧~!
'; + } +} +/* +$yesterday = date("Y/m/d/", strtotime("-1 day")); +// 昨日日期 +$todayUpload = getFileNumber(APP_ROOT . config_path()); +// 今日上传数量 +$yesterdayUpload = getFileNumber(APP_ROOT . $config['path'] . $yesterday); +// 昨日上传数量 + +$spaceUsed = getDistUsed(disk_total_space(__DIR__) - disk_free_space(__DIR__)); +// 占用空间 +*/ +// 当前日期全部上传 +$allUploud = isset($_GET['date']) ? $_GET['date'] : date('Y/m/d/'); +$allUploud = getFileNumber(APP_ROOT . $config['path'] . $allUploud); +@$httpUrl = array('date' => $path, 'num' => getFileNumber(APP_ROOT . config_path($path))); +?> + + + + + +
+
+ 当前 + 今日">昨日 + ' . date('m月d日', strtotime("-{$x} day")) . ''; + } + if (is_online()) { + echo ' +
+ + + + +
+ '; + } + + ?> +
+
+
+
+ + +
+ + +
+
+ + + + + new $.zui.Messager("删除成功!", {type: "success" // 定义颜色主题 + }).show();'; + header("refresh:1;"); // 1s后刷新当前页面 +} else { + echo ' + '; + header("refresh:1;"); // 1s后刷新当前页面 +} \ No newline at end of file diff --git a/libs/thumb.php b/application/thumb.php similarity index 100% rename from libs/thumb.php rename to application/thumb.php diff --git a/application/total_files.php b/application/total_files.php new file mode 100755 index 0000000..a4408ee --- /dev/null +++ b/application/total_files.php @@ -0,0 +1,87 @@ + $totalJsonName, // 文件名称 + 'date' => date('YmdH', strtotime('-1 hour')), // 识别日期格式 + 'total_time' => date('Y-m-d H:i:s'), // 统计时间 + 'dirnum' => $dirn, // 文件夹数量 + 'filenum' => $filen, // 文件数量 + 'usage_space' => $usage_space, // 占用空间 + 'todayUpload' => $todayUpload, // 今日上传数量 + 'yestUpload' => $yestUpload // 昨日上传数量 + ]; + $totalJsonInfo = json_encode($totalJsonInfo, true); + if (is_dir($total_file_path . 'cache/')) { + $totalJsonFile = fopen($total_file_path . "cache/$totalJsonName.php", 'w+'); + fwrite($totalJsonFile, $totalJsonInfo); + fclose($totalJsonFile); + } else { + mkdir($total_file_path . 'cache/', 0777, true); // 创建cache目录 + } +} + +function read_total_json($total) // 读取json文件 +{ + global $config; + global $totalJsonFile; + global $totalJsonName; + + $totalJsonPath = APP_ROOT . $config['path'] . 'cache/' . $totalJsonName . '.php'; // 文件的路径 + + if (file_exists($totalJsonPath)) { + $totalJsonFile = file_get_contents($totalJsonPath); + $totalJsonFile = json_decode($totalJsonFile, true); + if ($totalJsonFile['date'] !== date('YmdH', strtotime('-1 hour'))) { + creat_json(); + } + } else { + creat_json(); + } + + return $totalJsonFile[$total]; +} diff --git a/config/api_key.php b/config/api_key.php index e9d561b..5cc2700 100755 --- a/config/api_key.php +++ b/config/api_key.php @@ -1,8 +1,8 @@ '8337effca0ddfcd9c5899f3509b23657', 1 => '1c17b11693cb5ec63859b091c5b9c1b2', -); - -$tinyImag_key = [//Api_Key - // 填写 TinyImag Key 申请地址:https://tinypng.com/developers - 'TinyImag' => '' - -]; - - - -/** -* moderatecontent key -* 图片监黄 key 从 https://moderatecontent.com/ 获取key并填入/config/api_key.php的图片检查key -*/ -$moderatecontent = array( - 'url' => 'https://api.moderatecontent.com/moderate/?key=', - 'key' => '' ); \ No newline at end of file diff --git a/config/config-sample.php b/config/config-sample.php new file mode 100755 index 0000000..26b1c10 --- /dev/null +++ b/config/config-sample.php @@ -0,0 +1,189 @@ + '简单图床 - EasyImage', + // 网站关键字 + 'keywords' => '简单图床,easyimage,无数据库图床,PHP多图长传程序,自适应页面,HTML5,markdown,bbscode,一键复制', + // 网站描述 + 'description' => '简单图床EasyImage是一款支持多文件上传的无数据库图床,可以完美替代PHP多图上传程序,最新html5自适应页面兼容手机电脑,上传后返回图片直链,markdown图片,论坛贴图bbscode链接,简单方便支持一键复制,支持多域名,api上传。', + // 网站公告 为空则不显示 + 'tips' => '本站仅做演示用,不定时清理图片,单文件≤5M,每次上传≤30张', + /** + * 网站域名与图片链接域名可以不同,比如A域名上传,可以返回B域名图片链接,A、B需绑定到同一空间下 + * 如果不变的话,下边2个填写成一样的! + */ + // 网站域名,末尾不加"/" + 'domain' => 'http://localhost', + // 图片链接域名,末尾不加"/" + 'imgurl' => 'http://localhost', + // 登录上传和后台管理密码,管理用户名为:admin + 'password' => 'admin@123', + // 是否开启登录上传 开启:true 关闭:false + 'mustLogin' => false, + // 是否开启API上传 开启:true 关闭:false + 'apiStatus' => false, + /** + * 存储路径 前后要加"/" + * 可根据Apache/Nginx配置安全,参考:https://www.545141.com/981.html 或 README.md + */ + 'path' => '/i/', + /** 文件的命名方式 更改后不影响之前上传的 + * date 以上传时间 例:192704 + * unix 以Unix时间 例:1635074840 + * uniqid 基于以微秒计的当前时间 例:6175436c73418 + * guid 全球唯一标识符 例:6EDAD0CC-AB0C-4F61-BCCA-05FAD65BF0FA + * md5 md5加密时间 例:3888aa69eb321a2b61fcc63520bf6c82 + * sha1 sha1加密微秒 例:654faac01499e0cb5fb0e9d78b21e234c63d842a + * default 将上传时间+随机数转换为36进制 例:vx77yu + */ + 'imgName' => 'default', + // 最大上传限制 默认为5M 请使用工具转换Mb http://www.bejson.com/convert/filesize/ + 'maxSize' => 5242880, + // 每次最多上传图片数 + 'maxUploadFiles' => 30, + // 是否开启水印:0关闭,1文字水印,2图片水印 不能使用动态gif添加水印 + 'watermark' => 1, + // 水印文字内容 + 'waterText' => 'img.545141.com', + /** + * 水印位置 + * 0:随机位置,在1-8之间随机选取一个位置 + * 1:顶部居左 2:顶部居中 3:顶部居右 4:左边居中 + * 5:图片中心 6:右边居中 7:底部居左 8:底部居中 9:底部居右 + */ + 'waterPosition' => 5, + // 水印文字颜色 rgba 末尾为透明度0-127 0为不透明 + 'textColor' => '47,79,79,0', + // 水印文字大小 + 'textSize' => 16, + // 字体路径 如果想改变字体,请选择支持中文的 GB/2312 字体 + 'textFont' => '/public/static/hkxzy.ttf', + // 图片水印路径 支持GIF,JPG,BMP,PNG和PNG alpha + 'waterImg' => 'public/images/watermark.png', + // 允许上传的图片扩展名 + 'extensions' => "bmp,jpg,png,tif,gif,pcx,tga,svg,webp,jpeg,tga,svg,ico", + /* 轻微有损压缩图片 开启:true 关闭:false + * 此压缩有可能使图片变大!特别是小图片 也有一定概率改变图片方向 + * 开启后会增加服务器负担 + */ + 'compress' => false, + // 转换图片为指定格式 可选:''|'png'|'jpeg'|'gif'|'bmp';默认值:'' + 'imgConvert' => '', + // 最大上传宽度 + 'maxWidth' => 10240, + // 最大上传高度 + 'maxHeight' => 10240, + // 允许上传的最小宽度 + 'minWidth' => 5, + // 允许上传的最小高度 + 'minHeight' => 5, + // 改变图片宽高 宽度和高度请设置 image_x image_y 开启:true 关闭:false 关闭下image_x和image_y设置不生效 + 'imgRatio' => false, + // 缩减的最大高度 + 'image_x' => 1000, + // 缩减的最大宽度 + 'image_y' => 800, + // 开启静态文件CDN 开启:true 关闭:false + 'static_cdn' => false, + // 静态文件CDN加速网址 末尾不加 / + 'static_cdn_url' => '//cdn.jsdelivr.net/gh/icret/EasyImages2.0', + // 开启顶部广告 开启:true 关闭:false + 'ad_top' => false, + // 顶部广告内容 支持html + 'ad_top_info' => ' + + ', + // 开启底部广告 开启:true 关闭:false + 'ad_bot' => false, + // 底部广告内容 支持html + 'ad_bot_info' => ' + + ', + // 开启游客预览(广场)开启:true 关闭:false + 'showSwitch' => true, + // 默认预览数量,可在网址后填写参数实时更改预览数量 如:https://img.545141.com/application/list.php?num=3 + 'listNumber' => 20, + // 上传框底部自定义信息,仅支持html格式 可以放置统计代码 下面是举例: + 'customize' => ' + + + + + + ', + // PHP插件检测-安全设置检测-版本检测 开启:true 关闭:false + 'checkEnv' => true, + /* 图片监黄 开启:true 关闭:false + * 从 https://moderatecontent.com/ 获取key并填入/config/api_key.php的图片检查key + * 开启后会受服务器到https://moderatecontent.com/ 速度影响,国内不建议开启! + */ + 'checkImg' => false, + // 设置是不良图片概率,概率越大准确率越高, + 'checkImg_value' => 50, + // 当前版本 + 'version' => '2.3.1' +); diff --git a/config/config.php b/config/config.php index f75147d..920b304 100755 --- a/config/config.php +++ b/config/config.php @@ -1,187 +1,81 @@ '简单图床 - EasyImage', - // 网站关键字 - 'keywords' => '简单图床,easyimage,无数据库图床,PHP多图长传程序,自适应页面,HTML5,markdown,bbscode,一键复制', - // 网站描述 - 'description' => '简单图床EasyImage是一款支持多文件上传的无数据库图床,可以完美替代PHP多图上传程序,最新html5自适应页面兼容手机电脑,上传后返回图片直链,markdown图片,论坛贴图bbscode链接,简单方便支持一键复制,支持多域名,api上传。', - // 网站公告 为空则不显示 - 'tips' => '本站仅做演示用,不定时清理图片,单文件≤5M,每次上传≤30张', - /** - * 网站域名与图片链接域名可以不同,比如A域名上传,可以返回B域名图片链接,A、B需绑定到同一空间下 - * 如果不变的话,下边2个填写成一样的! - */ - // 网站域名,末尾不加"/" - 'domain' => 'http://localhost', - // 图片链接域名,末尾不加"/" - 'imgurl' => 'http://localhost', - // 登录上传和后台管理密码,管理用户名为:admin - 'password' => 'admin@123', - // 是否开启登录上传 开启:true 关闭:false - 'mustLogin' => false, - // 是否开启API上传 开启:true 关闭:false - 'apiStatus' => false, - /** - * 存储路径 前后要加"/" - * 可根据Apache/Nginx配置安全,参考:https://www.545141.com/981.html 或 README.md - */ - 'path' => '/i/', - /** 文件的命名方式 更改后不影响之前上传的 - * date 以上传时间 例:192704 - * unix 以Unix时间 例:1635074840 - * uniqid 基于以微秒计的当前时间 例:6175436c73418 - * guid 全球唯一标识符 例:6EDAD0CC-AB0C-4F61-BCCA-05FAD65BF0FA - * md5 md5加密时间 例:3888aa69eb321a2b61fcc63520bf6c82 - * sha1 sha1加密微秒 例:654faac01499e0cb5fb0e9d78b21e234c63d842a - * default 将上传时间+随机数转换为36进制 例:vx77yu - */ - 'imgName' => 'default', - // 最大上传限制 默认为5M 请使用工具转换Mb http://www.bejson.com/convert/filesize/ - 'maxSize' => 5242880, - // 每次最多上传图片数 - 'maxUploadFiles' => 30, - // 是否开启水印:0关闭,1文字水印,2图片水印 不能使用动态gif添加水印 - 'watermark' => 0, - // 水印文字内容 - 'waterText' => '简单图床 img.545141.com', - /** - * 水印位置 - * 0:随机位置,在1-8之间随机选取一个位置 - * 1:顶部居左 2:顶部居中 3:顶部居右 4:左边居中 - * 5:图片中心 6:右边居中 7:底部居左 8:底部居中 9:底部居右 - */ - 'waterPosition' => 8, - // 水印文字颜色 rgba 末尾为透明度0-127 0为不透明 - 'textColor' => '47,79,79,0', - // 水印文字大小 - 'textSize' => 16, - // 字体路径 如果想改变字体,请选择支持中文的 GB/2312 字体 - 'textFont' => 'public/static/hkxzy.ttf', - // 图片水印路径 支持GIF,JPG,BMP,PNG和PNG alpha - 'waterImg' => 'public/images/watermark.png', - // 允许上传的图片扩展名 - 'extensions' => "'bmp,jpg,png,tif,gif,pcx,tga,svg,webp,jpeg,tga,svg,ico'", - /* 轻微有损压缩图片 开启:true 关闭:false - * 此压缩有可能使图片变大!特别是小图片 也有一定概率改变图片方向 - * 开启后会增加服务器负担 - */ - 'compress' => false, - // 转换图片为指定格式 可选:''|'png'|'jpeg'|'gif'|'bmp';默认值:'' - 'imgConvert' => '', - // 最大上传宽度 - 'maxWidth' => 10240, - // 最大上传高度 - 'maxHeight' => 10240, - // 允许上传的最小宽度 - 'minWidth' => 5, - // 允许上传的最小高度 - 'minHeight' => 5, - // 改变图片宽高 宽度和高度请设置 image_x image_y 开启:true 关闭:false 关闭下image_x和image_y设置不生效 - 'imgRatio' => false, - // 缩减的最大高度 - 'image_x' => 1000, - // 缩减的最大宽度 - 'image_y' => 800, - // 开启静态文件CDN 开启:true 关闭:false - 'static_cdn' => false, - // 静态文件CDN加速网址 末尾不加 / - 'static_cdn_url' => '//cdn.jsdelivr.net/gh/icret/EasyImages2.0', - // 开启顶部广告 开启:true 关闭:false 如果想添加或修改广告请到 - 'ad_top' => false, - // 顶部广告内容 支持html - 'ad_top_info' => ' - - ', - // 开启底部广告 开启:true 关闭:false 如果想添加或修改广告请到 - 'ad_bot' => false, - // 底部广告内容 支持html - 'ad_bot_info' => ' - - ', - // 开启游客预览(广场)开启:true 关闭:false - 'showSwitch' => true, - // 默认预览数量,可在网址后填写参数实时更改预览数量 如:https://img.545141.com/libs/list.php?num=3 - 'listNumber' => 20, - // 上传框底部自定义信息,仅支持html格式 可以放置统计代码 下面是举例: - 'customize' => ' - - - - - - ', - // PHP插件检测-安全设置检测-版本检测 开启:true 关闭:false - 'checkEnv' => true, - /* 图片监黄 开启:true 关闭:false - * 从 https://moderatecontent.com/ 获取key并填入/config/api_key.php的图片检查key - * 开启后会受服务器到https://moderatecontent.com/ 速度影响,国内不建议开启! - */ - 'checkImg' => false, - // 设置是不良图片概率,概率越大准确率越高, - 'checkImg_value' => 50, - // 当前版本 - 'version' => '2.3.1' -); +$config=Array + ( + 'title'=>'简单图床 - EasyImage', + 'keywords'=>'简单图床,easyimage,无数据库图床,PHP多图长传程序,自适应页面,HTML5,markdown,bbscode,一键复制', + 'description'=>'简单图床EasyImage是一款支持多文件上传的无数据库图床,可以完美替代PHP多图上传程序,最新html5自适应页面兼容手机电脑,上传后返回图片直链,markdown图片,论坛贴图bbscode链接,简单方便支持一键复制,支持多域名,api上传。', + 'tips'=>'本站仅做演示用,不定时清理图片,单文件≤5M,每次上传≤30张', + 'domain'=>'http://localhost', + 'imgurl'=>'http://localhost', + 'password'=>'admin@123', + 'mustLogin'=>0, + 'apiStatus'=>0, + 'path'=>'/i/', + 'imgName'=>'default', + 'maxSize'=>5242880, + 'maxUploadFiles'=>30, + 'watermark'=>0, + 'waterText'=>'简单图床 - img.545141.com', + 'waterPosition'=>0, + 'textColor'=>'255,0,0,1', + 'textSize'=>16, + 'textFont'=>'/public/static/hkxzy.ttf', + 'waterImg'=>'/public/images/watermark.png', + 'extensions'=>'bmp,jpg,png,tif,gif,pcx,tga,svg,webp,jpeg,tga,svg,ico', + 'compress'=>0, + 'imgConvert'=>'', + 'maxWidth'=>10240, + 'maxHeight'=>10240, + 'minWidth'=>5, + 'minHeight'=>5, + 'imgRatio'=>0, + 'image_x'=>1000, + 'image_y'=>800, + 'static_cdn'=>0, + 'static_cdn_url'=>'https://cdn.jsdelivr.net/gh/icret/EasyImages2.0', + 'ad_top'=>0, + 'ad_top_info'=>' + ', + 'ad_bot'=>0, + 'ad_bot_info'=>' + ', + 'showSwitch'=>1, + 'listNumber'=>20, + 'customize'=>' + + + + ', + 'checkEnv'=>1, + 'checkImg'=>0, + 'checkImg_value'=>50, + 'version'=>'2.4.0', + 'form'=>'', + 'TinyImag_key'=>'', + 'moderatecontent_key'=>'' + ); \ No newline at end of file diff --git a/file.php b/file.php index 9cfbdf8..28dcc30 100755 --- a/file.php +++ b/file.php @@ -1,8 +1,8 @@ uploaded) { 'pos' => $config['waterPosition'], # 不指定name(会覆盖原图,也就是保存成thumb.jpeg) 'name' => $handle->file_dst_pathname, - 'font' => $config['textFont'], + 'font' => APP_ROOT . $config['textFont'], 'fontSize' => $config['textSize'], 'color' => $config['textColor'], ]; @@ -58,7 +58,7 @@ if ($handle->uploaded) { if (isAnimatedGif($handle->file_src_pathname) === 0) { $arr = [ # 水印图片路径(如果不存在将会被当成是字符串水印) - 'res' => $config['waterImg'], + 'res' => APP_ROOT . $config['waterImg'], # 水印显示位置 'pos' => $config['waterPosition'], # 不指定name(会覆盖原图,也就是保存成thumb.jpeg) @@ -82,11 +82,14 @@ if ($handle->uploaded) { // 判断PHP版本启用删除 $ver = substr(PHP_VERSION, 0, 3); if ($ver >= '7.0') { - $delUrl = $config['domain'] . '/api/del.php?hash=' . urlHash(config_path() . $handle->file_dst_name, 0); + $delUrl = $config['domain'] . '/application/del.php?hash=' . urlHash(config_path() . $handle->file_dst_name, 0); } else { $delUrl = 'PHP≥7.0 才能启用删除!'; } + // 创建缩略图 + @creat_cache_images($handle->file_dst_name); + $reJson = array( "result" => 'success', "url" => $imageUrl, @@ -106,7 +109,7 @@ if ($handle->uploaded) { // 压缩图片 后压缩模式,不影响前台输出速度 if (!isAnimatedGif($handle->file_dst_pathname)) if ($config['compress']) { - require 'libs/compress/Imagick/class.Imgcompress.php'; + require 'application/compress/Imagick/class.Imgcompress.php'; $img = new Imgcompress($handle->file_dst_pathname, 1); $img->compressImg($handle->file_dst_pathname); // 释放 @@ -116,11 +119,10 @@ if ($handle->uploaded) { unset($handle); + // 图片违规检查 - - if($config['checkImg']){ + if ($config['checkImg']) { require_once APP_ROOT . '/config/api_key.php'; @checkImg($imageUrl); - } - + } } diff --git a/i/.htaccess b/i/.htaccess new file mode 100755 index 0000000..6560465 --- /dev/null +++ b/i/.htaccess @@ -0,0 +1,4 @@ + +Order allow,deny +Deny from all + \ No newline at end of file diff --git a/i/cache/d2efe4a4eedde82b566dc7eed5bb2513.php b/i/cache/d2efe4a4eedde82b566dc7eed5bb2513.php new file mode 100755 index 0000000..47ab618 --- /dev/null +++ b/i/cache/d2efe4a4eedde82b566dc7eed5bb2513.php @@ -0,0 +1 @@ +{"filename":"d2efe4a4eedde82b566dc7eed5bb2513","date":"2021110910","total_time":"2021-11-09 11:00:16","dirnum":6,"filenum":8,"usage_space":"2.13MB","todayUpload":0,"yestUpload":0} \ No newline at end of file diff --git a/i/cache/total_chart_total_chart_d2efe4a4eedde82b566dc7eed5bb2513.php b/i/cache/total_chart_total_chart_d2efe4a4eedde82b566dc7eed5bb2513.php new file mode 100755 index 0000000..67b1328 --- /dev/null +++ b/i/cache/total_chart_total_chart_d2efe4a4eedde82b566dc7eed5bb2513.php @@ -0,0 +1 @@ +{"chart_data":[{"2021\/11\/09\/":0},{"2021\/11\/08\/":0},{"2021\/11\/07\/":2},{"2021\/11\/06\/":0},{"2021\/11\/05\/":0},{"2021\/11\/04\/":2},{"2021\/11\/03\/":0},{"2021\/11\/02\/":0},{"2021\/11\/01\/":0},{"2021\/10\/31\/":0},{"2021\/10\/30\/":0},{"2021\/10\/29\/":0},{"2021\/10\/28\/":0},{"2021\/10\/27\/":0},{"2021\/10\/26\/":0},{"2021\/10\/25\/":0},{"2021\/10\/24\/":0},{"2021\/10\/23\/":0},{"2021\/10\/22\/":0},{"2021\/10\/21\/":0},{"2021\/10\/20\/":0},{"2021\/10\/19\/":0},{"2021\/10\/18\/":0},{"2021\/10\/17\/":0},{"2021\/10\/16\/":0},{"2021\/10\/15\/":0},{"2021\/10\/14\/":0},{"2021\/10\/13\/":0},{"2021\/10\/12\/":0},{"2021\/10\/11\/":0}],"chart_disk":[{"2021\/11\/09\/":0},{"2021\/11\/08\/":0},{"2021\/11\/07\/":1113326},{"2021\/11\/06\/":0},{"2021\/11\/05\/":0},{"2021\/11\/04\/":1113326},{"2021\/11\/03\/":0},{"2021\/11\/02\/":0},{"2021\/11\/01\/":0},{"2021\/10\/31\/":0},{"2021\/10\/30\/":0},{"2021\/10\/29\/":0},{"2021\/10\/28\/":0},{"2021\/10\/27\/":0},{"2021\/10\/26\/":0},{"2021\/10\/25\/":0},{"2021\/10\/24\/":0},{"2021\/10\/23\/":0},{"2021\/10\/22\/":0},{"2021\/10\/21\/":0},{"2021\/10\/20\/":0},{"2021\/10\/19\/":0},{"2021\/10\/18\/":0},{"2021\/10\/17\/":0},{"2021\/10\/16\/":0},{"2021\/10\/15\/":0},{"2021\/10\/14\/":0},{"2021\/10\/13\/":0},{"2021\/10\/12\/":0},{"2021\/10\/11\/":0}],"total_time":"2021-11-09 10:56:01","date":"20211109"} \ No newline at end of file diff --git a/i/cache/verson.json b/i/cache/verson.json new file mode 100755 index 0000000..d8e478f --- /dev/null +++ b/i/cache/verson.json @@ -0,0 +1,41 @@ +{ + "url": "https://api.github.com/repos/icret/EasyImages2.0/releases/52150405", + "assets_url": "https://api.github.com/repos/icret/EasyImages2.0/releases/52150405/assets", + "upload_url": "https://uploads.github.com/repos/icret/EasyImages2.0/releases/52150405/assets{?name,label}", + "html_url": "https://github.com/icret/EasyImages2.0/releases/tag/2.3.0", + "id": 52150405, + "author": { + "login": "icret", + "id": 16373024, + "node_id": "MDQ6VXNlcjE2MzczMDI0", + "avatar_url": "https://avatars.githubusercontent.com/u/16373024?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/icret", + "html_url": "https://github.com/icret", + "followers_url": "https://api.github.com/users/icret/followers", + "following_url": "https://api.github.com/users/icret/following{/other_user}", + "gists_url": "https://api.github.com/users/icret/gists{/gist_id}", + "starred_url": "https://api.github.com/users/icret/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/icret/subscriptions", + "organizations_url": "https://api.github.com/users/icret/orgs", + "repos_url": "https://api.github.com/users/icret/repos", + "events_url": "https://api.github.com/users/icret/events{/privacy}", + "received_events_url": "https://api.github.com/users/icret/received_events", + "type": "User", + "site_admin": false + }, + "node_id": "RE_kwDOCzgjBc4DG8CF", + "tag_name": "2.3.0", + "target_commitish": "master", + "name": "EasyImage2.0 简单图床 ver:2.3.0", + "draft": false, + "prerelease": false, + "created_at": "2021-10-27T12:48:26Z", + "published_at": "2021-10-27T14:54:09Z", + "assets": [ + + ], + "tarball_url": "https://api.github.com/repos/icret/EasyImages2.0/tarball/2.3.0", + "zipball_url": "https://api.github.com/repos/icret/EasyImages2.0/zipball/2.3.0", + "body": "* 2021-10-24 v2.3.0\r\n- 将服务器环境监测改为第一次打开时自动检测(如需再次展示需删除config目录下的EasyImage.lock)\r\n- 增加快捷操作中心显示服务信息\r\n- 增加自定义上传文件的命名方式(详见config.php文件里的注释)\r\n- 增加隐私政策、服务条款、DMCA\r\n- 增加自定义静态文件CDN源\r\n- 增加dns-prefetch\r\n- 删除了tinyfilemanager文件管理(感觉没什么用)\r\n- 一些bug得以修复" +} diff --git a/index.php b/index.php index 3998077..e04a24b 100755 --- a/index.php +++ b/index.php @@ -1,5 +1,5 @@ }, + {title: '图片',extensions:''}, {title: '图标',extensions: 'ico'} ], prevent_duplicates: true @@ -133,4 +133,4 @@ mustLogin(); window.alert("两次密码不一致请重新输入!");location.href="./index.php";'); + } +} + +if (isset($_POST['domain'])) { + $config['domain']= $_POST['domain']; + +} + +if (isset($_POST['imgurl'])) { + $config['imgurl']= $_POST['imgurl']; +} + +$config_file = APP_ROOT . '/config/config.php'; +cache_write($config_file, $config); + +file_put_contents(APP_ROOT . '/install/install.lock', '安装程序锁定文件。'); // 创建安装程序锁 + +// 跳转主页 +echo ' + +'; + +// 删除安装目录 +if (isset($_POST['del_install'])) { + if ($_POST['del_install'] == "del") { + deldir(APP_ROOT . "/install/"); + } +} + +//exit(header("Location:/../application/login.php")); // 跳转主页 \ No newline at end of file diff --git a/install/index.php b/install/index.php new file mode 100755 index 0000000..041f56a --- /dev/null +++ b/install/index.php @@ -0,0 +1,184 @@ += 5.6) ? true : false; +$fileinfo = extension_loaded('fileinfo') ? true : false; +$gd = extension_loaded('gd') ? true : false; +$openssl = extension_loaded('openssl') ? true : false; + +$file = substr(base_convert(fileperms(APP_ROOT . "/file.php"), 10, 8), 3); +if (IS_WIN) { + $file_php = true; + $i_wjj = true; +} +if (!IS_WIN) { + if ($file == '755') { + $file_php = true; + } else { + $file_php = false; + } + if (is_writable(APP_ROOT . '/i/')) { + $i_wjj = true; + } else { + $i_wjj = false; + } +} + +function checkPASS($name) +{ + if ($name) { + echo '

'; + } else { + echo '

'; + } +} + +?> + + + + + + EasyIamge 2.0 安装环境检测 + + + + + + + + + + + + + + + +

EasyIamge 2.0 安装环境检测

+
序号 缩略图 文件名长宽(像素) 大小 查看图片 还原图片
' . $i . ' ' . $filen_name . '' . $height . '*' . $width . ' ' . $file_size . ' 查看原图恢复图片恢复图片 删除图片
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
检查名称图床要求检测结果
PHPPHP >= 5.6
Fileinfo必须支持
GD必须支持
openssl建议支持(用于删除文件,PHP>7.0)
file.php0755可执行权限(非windows系统)
/i可写
+ + + '; + } else { + echo ' +
+ + +
+ '; + } + ?> + + + + + + + \ No newline at end of file diff --git a/install/install.php b/install/install.php new file mode 100755 index 0000000..9d1c732 --- /dev/null +++ b/install/install.php @@ -0,0 +1,175 @@ + + + + + + + EasyIamge 2.0 即将完成安装! + + + + + + + + + + + + + + +
+
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ + 请输入8~18位密码 +
+ +
+
+
+ +
+ +
+
+
+
+
+ +
+
+
+
+
+ +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/libs/header.php b/libs/header.php deleted file mode 100755 index 6d90fd5..0000000 --- a/libs/header.php +++ /dev/null @@ -1,59 +0,0 @@ - - - - - - - <?php echo $config['title']; ?> - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/libs/list.php b/libs/list.php deleted file mode 100755 index f4f2e22..0000000 --- a/libs/list.php +++ /dev/null @@ -1,203 +0,0 @@ -管理员关闭了预览哦~~
'; -} else { - $path = isset($_GET['date']) ?$_GET['date']: date('Y/m/d/'); - $keyNum =isset( $_GET['num'] )?$_GET['num']:$config['listNumber']; - $fileArr = getFile(APP_ROOT . config_path($path)); - if ($fileArr[0]) { - echo '
'; - foreach ($fileArr as $key => $value) { - if ($key < $keyNum) { - $imgUrl = $config['imgurl'] . config_path($path) . $value; - echo ' - - '; - } - } - echo '
'; - } else { - echo '
今天还没有上传的图片哟~~
快来上传第一张吧~!
'; - } -} -$yesterday = date("Y/m/d/", strtotime("-1 day")); -// 昨日日期 -$todayUpload = getFileNumber(APP_ROOT . config_path()); -// 今日上传数量 -$yesterdayUpload = getFileNumber(APP_ROOT . $config['path'] . $yesterday); -// 昨日上传数量 -$spaceUsed = getDistUsed(disk_total_space(__DIR__) - disk_free_space(__DIR__)); -// 占用空间 -// 当前日期全部上传 -$allUploud = isset($_GET['date'])?$_GET['date']:date('Y/m/d/'); -$allUploud = getFileNumber(APP_ROOT . $config['path'] . $allUploud); -@($httpUrl = array('date' => $path, 'num' => getFileNumber(APP_ROOT . config_path($path)))); -?> - - - - -
-
- 当前: - 今日: 昨日: - - ' . date('m月d日', strtotime("-{$x} day")) . '';}?> -
-
-
-
- - -
- -
-
-
- - - - - +Order allow,deny +Deny from all + \ No newline at end of file diff --git a/public/static/jscolor.js b/public/static/jscolor.js new file mode 100755 index 0000000..b614640 --- /dev/null +++ b/public/static/jscolor.js @@ -0,0 +1,3548 @@ +/** + * jscolor - JavaScript Color Picker + * + * @link http://jscolor.com + * @license For open source use: GPLv3 + * For commercial use: JSColor Commercial License + * @author Jan Odvarko - East Desire + * @version 2.4.6 + * + * See usage examples at http://jscolor.com/examples/ + */ + + +(function (global, factory) { + + 'use strict'; + + if (typeof module === 'object' && typeof module.exports === 'object') { + // Export jscolor as a module + module.exports = global.document ? + factory (global) : + function (win) { + if (!win.document) { + throw new Error('jscolor needs a window with document'); + } + return factory(win); + } + return; + } + + // Default use (no module export) + factory(global); + +})(typeof window !== 'undefined' ? window : this, function (window) { // BEGIN factory + +// BEGIN jscolor code + + +'use strict'; + + +var jscolor = (function () { // BEGIN jscolor + +var jsc = { + + + initialized : false, + + instances : [], // created instances of jscolor + + readyQueue : [], // functions waiting to be called after init + + + register : function () { + if (typeof window !== 'undefined' && window.document) { + window.document.addEventListener('DOMContentLoaded', jsc.pub.init, false); + } + }, + + + installBySelector : function (selector, rootNode) { + rootNode = rootNode ? jsc.node(rootNode) : window.document; + if (!rootNode) { + throw new Error('Missing root node'); + } + + var elms = rootNode.querySelectorAll(selector); + + // for backward compatibility with DEPRECATED installation/configuration using className + var matchClass = new RegExp('(^|\\s)(' + jsc.pub.lookupClass + ')(\\s*(\\{[^}]*\\})|\\s|$)', 'i'); + + for (var i = 0; i < elms.length; i += 1) { + + if (elms[i].jscolor && elms[i].jscolor instanceof jsc.pub) { + continue; // jscolor already installed on this element + } + + if (elms[i].type !== undefined && elms[i].type.toLowerCase() == 'color' && jsc.isColorAttrSupported) { + continue; // skips inputs of type 'color' if supported by the browser + } + + var dataOpts, m; + + if ( + (dataOpts = jsc.getDataAttr(elms[i], 'jscolor')) !== null || + (elms[i].className && (m = elms[i].className.match(matchClass))) // installation using className (DEPRECATED) + ) { + var targetElm = elms[i]; + + var optsStr = ''; + if (dataOpts !== null) { + optsStr = dataOpts; + + } else if (m) { // installation using className (DEPRECATED) + console.warn('Installation using class name is DEPRECATED. Use data-jscolor="" attribute instead.' + jsc.docsRef); + if (m[4]) { + optsStr = m[4]; + } + } + + var opts = null; + if (optsStr.trim()) { + try { + opts = jsc.parseOptionsStr(optsStr); + } catch (e) { + console.warn(e + '\n' + optsStr); + } + } + + try { + new jsc.pub(targetElm, opts); + } catch (e) { + console.warn(e); + } + } + } + }, + + + parseOptionsStr : function (str) { + var opts = null; + + try { + opts = JSON.parse(str); + + } catch (eParse) { + if (!jsc.pub.looseJSON) { + throw new Error('Could not parse jscolor options as JSON: ' + eParse); + } else { + // loose JSON syntax is enabled -> try to evaluate the options string as JavaScript object + try { + opts = (new Function ('var opts = (' + str + '); return typeof opts === "object" ? opts : {};'))(); + } catch (eEval) { + throw new Error('Could not evaluate jscolor options: ' + eEval); + } + } + } + return opts; + }, + + + getInstances : function () { + var inst = []; + for (var i = 0; i < jsc.instances.length; i += 1) { + // if the targetElement still exists, the instance is considered "alive" + if (jsc.instances[i] && jsc.instances[i].targetElement) { + inst.push(jsc.instances[i]); + } + } + return inst; + }, + + + createEl : function (tagName) { + var el = window.document.createElement(tagName); + jsc.setData(el, 'gui', true); + return el; + }, + + + node : function (nodeOrSelector) { + if (!nodeOrSelector) { + return null; + } + + if (typeof nodeOrSelector === 'string') { + // query selector + var sel = nodeOrSelector; + var el = null; + try { + el = window.document.querySelector(sel); + } catch (e) { + console.warn(e); + return null; + } + if (!el) { + console.warn('No element matches the selector: %s', sel); + } + return el; + } + + if (jsc.isNode(nodeOrSelector)) { + // DOM node + return nodeOrSelector; + } + + console.warn('Invalid node of type %s: %s', typeof nodeOrSelector, nodeOrSelector); + return null; + }, + + + // See https://stackoverflow.com/questions/384286/ + isNode : function (val) { + if (typeof Node === 'object') { + return val instanceof Node; + } + return val && typeof val === 'object' && typeof val.nodeType === 'number' && typeof val.nodeName === 'string'; + }, + + + nodeName : function (node) { + if (node && node.nodeName) { + return node.nodeName.toLowerCase(); + } + return false; + }, + + + removeChildren : function (node) { + while (node.firstChild) { + node.removeChild(node.firstChild); + } + }, + + + isTextInput : function (el) { + return el && jsc.nodeName(el) === 'input' && el.type.toLowerCase() === 'text'; + }, + + + isButton : function (el) { + if (!el) { + return false; + } + var n = jsc.nodeName(el); + return ( + (n === 'button') || + (n === 'input' && ['button', 'submit', 'reset'].indexOf(el.type.toLowerCase()) > -1) + ); + }, + + + isButtonEmpty : function (el) { + switch (jsc.nodeName(el)) { + case 'input': return (!el.value || el.value.trim() === ''); + case 'button': return (el.textContent.trim() === ''); + } + return null; // could not determine element's text + }, + + + // See https://github.com/WICG/EventListenerOptions/blob/gh-pages/explainer.md + isPassiveEventSupported : (function () { + var supported = false; + + try { + var opts = Object.defineProperty({}, 'passive', { + get: function () { supported = true; } + }); + window.addEventListener('testPassive', null, opts); + window.removeEventListener('testPassive', null, opts); + } catch (e) {} + + return supported; + })(), + + + isColorAttrSupported : (function () { + var elm = window.document.createElement('input'); + if (elm.setAttribute) { + elm.setAttribute('type', 'color'); + if (elm.type.toLowerCase() == 'color') { + return true; + } + } + return false; + })(), + + + dataProp : '_data_jscolor', + + + // usage: + // setData(obj, prop, value) + // setData(obj, {prop:value, ...}) + // + setData : function () { + var obj = arguments[0]; + + if (arguments.length === 3) { + // setting a single property + var data = obj.hasOwnProperty(jsc.dataProp) ? obj[jsc.dataProp] : (obj[jsc.dataProp] = {}); + var prop = arguments[1]; + var value = arguments[2]; + + data[prop] = value; + return true; + + } else if (arguments.length === 2 && typeof arguments[1] === 'object') { + // setting multiple properties + var data = obj.hasOwnProperty(jsc.dataProp) ? obj[jsc.dataProp] : (obj[jsc.dataProp] = {}); + var map = arguments[1]; + + for (var prop in map) { + if (map.hasOwnProperty(prop)) { + data[prop] = map[prop]; + } + } + return true; + } + + throw new Error('Invalid arguments'); + }, + + + // usage: + // removeData(obj, prop, [prop...]) + // + removeData : function () { + var obj = arguments[0]; + if (!obj.hasOwnProperty(jsc.dataProp)) { + return true; // data object does not exist + } + for (var i = 1; i < arguments.length; i += 1) { + var prop = arguments[i]; + delete obj[jsc.dataProp][prop]; + } + return true; + }, + + + getData : function (obj, prop, setDefault) { + if (!obj.hasOwnProperty(jsc.dataProp)) { + // data object does not exist + if (setDefault !== undefined) { + obj[jsc.dataProp] = {}; // create data object + } else { + return undefined; // no value to return + } + } + var data = obj[jsc.dataProp]; + + if (!data.hasOwnProperty(prop) && setDefault !== undefined) { + data[prop] = setDefault; + } + return data[prop]; + }, + + + getDataAttr : function (el, name) { + var attrName = 'data-' + name; + var attrValue = el.getAttribute(attrName); + return attrValue; + }, + + + setDataAttr : function (el, name, value) { + var attrName = 'data-' + name; + el.setAttribute(attrName, value); + }, + + + _attachedGroupEvents : {}, + + + attachGroupEvent : function (groupName, el, evnt, func) { + if (!jsc._attachedGroupEvents.hasOwnProperty(groupName)) { + jsc._attachedGroupEvents[groupName] = []; + } + jsc._attachedGroupEvents[groupName].push([el, evnt, func]); + el.addEventListener(evnt, func, false); + }, + + + detachGroupEvents : function (groupName) { + if (jsc._attachedGroupEvents.hasOwnProperty(groupName)) { + for (var i = 0; i < jsc._attachedGroupEvents[groupName].length; i += 1) { + var evt = jsc._attachedGroupEvents[groupName][i]; + evt[0].removeEventListener(evt[1], evt[2], false); + } + delete jsc._attachedGroupEvents[groupName]; + } + }, + + + preventDefault : function (e) { + if (e.preventDefault) { e.preventDefault(); } + e.returnValue = false; + }, + + + captureTarget : function (target) { + // IE + if (target.setCapture) { + jsc._capturedTarget = target; + jsc._capturedTarget.setCapture(); + } + }, + + + releaseTarget : function () { + // IE + if (jsc._capturedTarget) { + jsc._capturedTarget.releaseCapture(); + jsc._capturedTarget = null; + } + }, + + + triggerEvent : function (el, eventName, bubbles, cancelable) { + if (!el) { + return; + } + + var ev = null; + + if (typeof Event === 'function') { + ev = new Event(eventName, { + bubbles: bubbles, + cancelable: cancelable + }); + } else { + // IE + ev = window.document.createEvent('Event'); + ev.initEvent(eventName, bubbles, cancelable); + } + + if (!ev) { + return false; + } + + // so that we know that the event was triggered internally + jsc.setData(ev, 'internal', true); + + el.dispatchEvent(ev); + return true; + }, + + + triggerInputEvent : function (el, eventName, bubbles, cancelable) { + if (!el) { + return; + } + if (jsc.isTextInput(el)) { + jsc.triggerEvent(el, eventName, bubbles, cancelable); + } + }, + + + eventKey : function (ev) { + var keys = { + 9: 'Tab', + 13: 'Enter', + 27: 'Escape', + }; + if (typeof ev.code === 'string') { + return ev.code; + } else if (ev.keyCode !== undefined && keys.hasOwnProperty(ev.keyCode)) { + return keys[ev.keyCode]; + } + return null; + }, + + + strList : function (str) { + if (!str) { + return []; + } + return str.replace(/^\s+|\s+$/g, '').split(/\s+/); + }, + + + // The className parameter (str) can only contain a single class name + hasClass : function (elm, className) { + if (!className) { + return false; + } + if (elm.classList !== undefined) { + return elm.classList.contains(className); + } + // polyfill + return -1 != (' ' + elm.className.replace(/\s+/g, ' ') + ' ').indexOf(' ' + className + ' '); + }, + + + // The className parameter (str) can contain multiple class names separated by whitespace + addClass : function (elm, className) { + var classNames = jsc.strList(className); + + if (elm.classList !== undefined) { + for (var i = 0; i < classNames.length; i += 1) { + elm.classList.add(classNames[i]); + } + return; + } + // polyfill + for (var i = 0; i < classNames.length; i += 1) { + if (!jsc.hasClass(elm, classNames[i])) { + elm.className += (elm.className ? ' ' : '') + classNames[i]; + } + } + }, + + + // The className parameter (str) can contain multiple class names separated by whitespace + removeClass : function (elm, className) { + var classNames = jsc.strList(className); + + if (elm.classList !== undefined) { + for (var i = 0; i < classNames.length; i += 1) { + elm.classList.remove(classNames[i]); + } + return; + } + // polyfill + for (var i = 0; i < classNames.length; i += 1) { + var repl = new RegExp( + '^\\s*' + classNames[i] + '\\s*|' + + '\\s*' + classNames[i] + '\\s*$|' + + '\\s+' + classNames[i] + '(\\s+)', + 'g' + ); + elm.className = elm.className.replace(repl, '$1'); + } + }, + + + getCompStyle : function (elm) { + var compStyle = window.getComputedStyle ? window.getComputedStyle(elm) : elm.currentStyle; + + // Note: In Firefox, getComputedStyle returns null in a hidden iframe, + // that's why we need to check if the returned value is non-empty + if (!compStyle) { + return {}; + } + return compStyle; + }, + + + // Note: + // Setting a property to NULL reverts it to the state before it was first set + // with the 'reversible' flag enabled + // + setStyle : function (elm, styles, important, reversible) { + // using '' for standard priority (IE10 apparently doesn't like value undefined) + var priority = important ? 'important' : ''; + var origStyle = null; + + for (var prop in styles) { + if (styles.hasOwnProperty(prop)) { + var setVal = null; + + if (styles[prop] === null) { + // reverting a property value + + if (!origStyle) { + // get the original style object, but dont't try to create it if it doesn't exist + origStyle = jsc.getData(elm, 'origStyle'); + } + if (origStyle && origStyle.hasOwnProperty(prop)) { + // we have property's original value -> use it + setVal = origStyle[prop]; + } + + } else { + // setting a property value + + if (reversible) { + if (!origStyle) { + // get the original style object and if it doesn't exist, create it + origStyle = jsc.getData(elm, 'origStyle', {}); + } + if (!origStyle.hasOwnProperty(prop)) { + // original property value not yet stored -> store it + origStyle[prop] = elm.style[prop]; + } + } + setVal = styles[prop]; + } + + if (setVal !== null) { + elm.style.setProperty(prop, setVal, priority); + } + } + } + }, + + + hexColor : function (r, g, b) { + return '#' + ( + ('0' + Math.round(r).toString(16)).substr(-2) + + ('0' + Math.round(g).toString(16)).substr(-2) + + ('0' + Math.round(b).toString(16)).substr(-2) + ).toUpperCase(); + }, + + + hexaColor : function (r, g, b, a) { + return '#' + ( + ('0' + Math.round(r).toString(16)).substr(-2) + + ('0' + Math.round(g).toString(16)).substr(-2) + + ('0' + Math.round(b).toString(16)).substr(-2) + + ('0' + Math.round(a * 255).toString(16)).substr(-2) + ).toUpperCase(); + }, + + + rgbColor : function (r, g, b) { + return 'rgb(' + + Math.round(r) + ',' + + Math.round(g) + ',' + + Math.round(b) + + ')'; + }, + + + rgbaColor : function (r, g, b, a) { + return + + Math.round(r) + ',' + + Math.round(g) + ',' + + Math.round(b) + ',' + + (Math.round((a===undefined || a===null ? 1 : a) * 100) / 100); + }, + + + linearGradient : (function () { + + function getFuncName () { + var stdName = 'linear-gradient'; + var prefixes = ['', '-webkit-', '-moz-', '-o-', '-ms-']; + var helper = window.document.createElement('div'); + + for (var i = 0; i < prefixes.length; i += 1) { + var tryFunc = prefixes[i] + stdName; + var tryVal = tryFunc + '(to right, rgba(0,0,0,0), rgba(0,0,0,0))'; + + helper.style.background = tryVal; + if (helper.style.background) { // CSS background successfully set -> function name is supported + return tryFunc; + } + } + return stdName; // fallback to standard 'linear-gradient' without vendor prefix + } + + var funcName = getFuncName(); + + return function () { + return funcName + '(' + Array.prototype.join.call(arguments, ', ') + ')'; + }; + + })(), + + + setBorderRadius : function (elm, value) { + jsc.setStyle(elm, {'border-radius' : value || '0'}); + }, + + + setBoxShadow : function (elm, value) { + jsc.setStyle(elm, {'box-shadow': value || 'none'}); + }, + + + getElementPos : function (e, relativeToViewport) { + var x=0, y=0; + var rect = e.getBoundingClientRect(); + x = rect.left; + y = rect.top; + if (!relativeToViewport) { + var viewPos = jsc.getViewPos(); + x += viewPos[0]; + y += viewPos[1]; + } + return [x, y]; + }, + + + getElementSize : function (e) { + return [e.offsetWidth, e.offsetHeight]; + }, + + + // get pointer's X/Y coordinates relative to viewport + getAbsPointerPos : function (e) { + var x = 0, y = 0; + if (typeof e.changedTouches !== 'undefined' && e.changedTouches.length) { + // touch devices + x = e.changedTouches[0].clientX; + y = e.changedTouches[0].clientY; + } else if (typeof e.clientX === 'number') { + x = e.clientX; + y = e.clientY; + } + return { x: x, y: y }; + }, + + + // get pointer's X/Y coordinates relative to target element + getRelPointerPos : function (e) { + var target = e.target || e.srcElement; + var targetRect = target.getBoundingClientRect(); + + var x = 0, y = 0; + + var clientX = 0, clientY = 0; + if (typeof e.changedTouches !== 'undefined' && e.changedTouches.length) { + // touch devices + clientX = e.changedTouches[0].clientX; + clientY = e.changedTouches[0].clientY; + } else if (typeof e.clientX === 'number') { + clientX = e.clientX; + clientY = e.clientY; + } + + x = clientX - targetRect.left; + y = clientY - targetRect.top; + return { x: x, y: y }; + }, + + + getViewPos : function () { + var doc = window.document.documentElement; + return [ + (window.pageXOffset || doc.scrollLeft) - (doc.clientLeft || 0), + (window.pageYOffset || doc.scrollTop) - (doc.clientTop || 0) + ]; + }, + + + getViewSize : function () { + var doc = window.document.documentElement; + return [ + (window.innerWidth || doc.clientWidth), + (window.innerHeight || doc.clientHeight), + ]; + }, + + + // r: 0-255 + // g: 0-255 + // b: 0-255 + // + // returns: [ 0-360, 0-100, 0-100 ] + // + RGB_HSV : function (r, g, b) { + r /= 255; + g /= 255; + b /= 255; + var n = Math.min(Math.min(r,g),b); + var v = Math.max(Math.max(r,g),b); + var m = v - n; + if (m === 0) { return [ null, 0, 100 * v ]; } + var h = r===n ? 3+(b-g)/m : (g===n ? 5+(r-b)/m : 1+(g-r)/m); + return [ + 60 * (h===6?0:h), + 100 * (m/v), + 100 * v + ]; + }, + + + // h: 0-360 + // s: 0-100 + // v: 0-100 + // + // returns: [ 0-255, 0-255, 0-255 ] + // + HSV_RGB : function (h, s, v) { + var u = 255 * (v / 100); + + if (h === null) { + return [ u, u, u ]; + } + + h /= 60; + s /= 100; + + var i = Math.floor(h); + var f = i%2 ? h-i : 1-(h-i); + var m = u * (1 - s); + var n = u * (1 - s * f); + switch (i) { + case 6: + case 0: return [u,n,m]; + case 1: return [n,u,m]; + case 2: return [m,u,n]; + case 3: return [m,n,u]; + case 4: return [n,m,u]; + case 5: return [u,m,n]; + } + }, + + + parseColorString : function (str) { + var ret = { + rgba: null, + format: null // 'hex' | 'hexa' | 'rgb' | 'rgba' + }; + + var m; + + if (m = str.match(/^\W*([0-9A-F]{3,8})\W*$/i)) { + // HEX notation + + if (m[1].length === 8) { + // 8-char notation (= with alpha) + ret.format = 'hexa'; + ret.rgba = [ + parseInt(m[1].substr(0,2),16), + parseInt(m[1].substr(2,2),16), + parseInt(m[1].substr(4,2),16), + parseInt(m[1].substr(6,2),16) / 255 + ]; + + } else if (m[1].length === 6) { + // 6-char notation + ret.format = 'hex'; + ret.rgba = [ + parseInt(m[1].substr(0,2),16), + parseInt(m[1].substr(2,2),16), + parseInt(m[1].substr(4,2),16), + null + ]; + + } else if (m[1].length === 3) { + // 3-char notation + ret.format = 'hex'; + ret.rgba = [ + parseInt(m[1].charAt(0) + m[1].charAt(0),16), + parseInt(m[1].charAt(1) + m[1].charAt(1),16), + parseInt(m[1].charAt(2) + m[1].charAt(2),16), + null + ]; + + } else { + return false; + } + + return ret; + } + + if (m = str.match(/^\W*rgba?\(([^)]*)\)\W*$/i)) { + // rgb(...) or rgba(...) notation + + var par = m[1].split(','); + var re = /^\s*(\d+|\d*\.\d+|\d+\.\d*)\s*$/; + var mR, mG, mB, mA; + if ( + par.length >= 3 && + (mR = par[0].match(re)) && + (mG = par[1].match(re)) && + (mB = par[2].match(re)) + ) { + ret.format = 'rgb'; + ret.rgba = [ + parseFloat(mR[1]) || 0, + parseFloat(mG[1]) || 0, + parseFloat(mB[1]) || 0, + null + ]; + + if ( + par.length >= 4 && + (mA = par[3].match(re)) + ) { + ret.format = 'rgba'; + ret.rgba[3] = parseFloat(mA[1]) || 0; + } + return ret; + } + } + + return false; + }, + + + parsePaletteValue : function (mixed) { + var vals = []; + + if (typeof mixed === 'string') { // input is a string of space separated color values + // rgb() and rgba() may contain spaces too, so let's find all color values by regex + mixed.replace(/#[0-9A-F]{3}([0-9A-F]{3})?|rgba?\(([^)]*)\)/ig, function (val) { + vals.push(val); + }); + } else if (Array.isArray(mixed)) { // input is an array of color values + vals = mixed; + } + + // convert all values into uniform color format + + var colors = []; + + for (var i = 0; i < vals.length; i++) { + var color = jsc.parseColorString(vals[i]); + if (color) { + colors.push(color); + } + } + + return colors; + }, + + + containsTranparentColor : function (colors) { + for (var i = 0; i < colors.length; i++) { + var a = colors[i].rgba[3]; + if (a !== null && a < 1.0) { + return true; + } + } + return false; + }, + + + isAlphaFormat : function (format) { + switch (format.toLowerCase()) { + case 'hexa': + case 'rgba': + return true; + } + return false; + }, + + + // Canvas scaling for retina displays + // + // adapted from https://www.html5rocks.com/en/tutorials/canvas/hidpi/ + // + scaleCanvasForHighDPR : function (canvas) { + var dpr = window.devicePixelRatio || 1; + canvas.width *= dpr; + canvas.height *= dpr; + var ctx = canvas.getContext('2d'); + ctx.scale(dpr, dpr); + }, + + + genColorPreviewCanvas : function (color, separatorPos, specWidth, scaleForHighDPR) { + + var sepW = Math.round(jsc.pub.previewSeparator.length); + var sqSize = jsc.pub.chessboardSize; + var sqColor1 = jsc.pub.chessboardColor1; + var sqColor2 = jsc.pub.chessboardColor2; + + var cWidth = specWidth ? specWidth : sqSize * 2; + var cHeight = sqSize * 2; + + var canvas = jsc.createEl('canvas'); + var ctx = canvas.getContext('2d'); + + canvas.width = cWidth; + canvas.height = cHeight; + if (scaleForHighDPR) { + jsc.scaleCanvasForHighDPR(canvas); + } + + // transparency chessboard - background + ctx.fillStyle = sqColor1; + ctx.fillRect(0, 0, cWidth, cHeight); + + // transparency chessboard - squares + ctx.fillStyle = sqColor2; + for (var x = 0; x < cWidth; x += sqSize * 2) { + ctx.fillRect(x, 0, sqSize, sqSize); + ctx.fillRect(x + sqSize, sqSize, sqSize, sqSize); + } + + if (color) { + // actual color in foreground + ctx.fillStyle = color; + ctx.fillRect(0, 0, cWidth, cHeight); + } + + var start = null; + switch (separatorPos) { + case 'left': + start = 0; + ctx.clearRect(0, 0, sepW/2, cHeight); + break; + case 'right': + start = cWidth - sepW; + ctx.clearRect(cWidth - (sepW/2), 0, sepW/2, cHeight); + break; + } + if (start !== null) { + ctx.lineWidth = 1; + for (var i = 0; i < jsc.pub.previewSeparator.length; i += 1) { + ctx.beginPath(); + ctx.strokeStyle = jsc.pub.previewSeparator[i]; + ctx.moveTo(0.5 + start + i, 0); + ctx.lineTo(0.5 + start + i, cHeight); + ctx.stroke(); + } + } + + return { + canvas: canvas, + width: cWidth, + height: cHeight, + }; + }, + + + // if position or width is not set => fill the entire element (0%-100%) + genColorPreviewGradient : function (color, position, width) { + var params = []; + + if (position && width) { + params = [ + 'to ' + {'left':'right', 'right':'left'}[position], + color + ' 0%', + color + ' ' + width + 'px', + 'rgba(0,0,0,0) ' + (width + 1) + 'px', + 'rgba(0,0,0,0) 100%', + ]; + } else { + params = [ + 'to right', + color + ' 0%', + color + ' 100%', + ]; + } + + return jsc.linearGradient.apply(this, params); + }, + + + redrawPosition : function () { + + if (!jsc.picker || !jsc.picker.owner) { + return; // picker is not shown + } + + var thisObj = jsc.picker.owner; + + var tp, vp; + + if (thisObj.fixed) { + // Fixed elements are positioned relative to viewport, + // therefore we can ignore the scroll offset + tp = jsc.getElementPos(thisObj.targetElement, true); // target pos + vp = [0, 0]; // view pos + } else { + tp = jsc.getElementPos(thisObj.targetElement); // target pos + vp = jsc.getViewPos(); // view pos + } + + var ts = jsc.getElementSize(thisObj.targetElement); // target size + var vs = jsc.getViewSize(); // view size + var pd = jsc.getPickerDims(thisObj); + var ps = [pd.borderW, pd.borderH]; // picker outer size + var a, b, c; + switch (thisObj.position.toLowerCase()) { + case 'left': a=1; b=0; c=-1; break; + case 'right':a=1; b=0; c=1; break; + case 'top': a=0; b=1; c=-1; break; + default: a=0; b=1; c=1; break; + } + var l = (ts[b]+ps[b])/2; + + // compute picker position + if (!thisObj.smartPosition) { + var pp = [ + tp[a], + tp[b]+ts[b]-l+l*c + ]; + } else { + var pp = [ + -vp[a]+tp[a]+ps[a] > vs[a] ? + (-vp[a]+tp[a]+ts[a]/2 > vs[a]/2 && tp[a]+ts[a]-ps[a] >= 0 ? tp[a]+ts[a]-ps[a] : tp[a]) : + tp[a], + -vp[b]+tp[b]+ts[b]+ps[b]-l+l*c > vs[b] ? + (-vp[b]+tp[b]+ts[b]/2 > vs[b]/2 && tp[b]+ts[b]-l-l*c >= 0 ? tp[b]+ts[b]-l-l*c : tp[b]+ts[b]-l+l*c) : + (tp[b]+ts[b]-l+l*c >= 0 ? tp[b]+ts[b]-l+l*c : tp[b]+ts[b]-l-l*c) + ]; + } + + var x = pp[a]; + var y = pp[b]; + var positionValue = thisObj.fixed ? 'fixed' : 'absolute'; + var contractShadow = + (pp[0] + ps[0] > tp[0] || pp[0] < tp[0] + ts[0]) && + (pp[1] + ps[1] < tp[1] + ts[1]); + + jsc._drawPosition(thisObj, x, y, positionValue, contractShadow); + }, + + + _drawPosition : function (thisObj, x, y, positionValue, contractShadow) { + var vShadow = contractShadow ? 0 : thisObj.shadowBlur; // px + + jsc.picker.wrap.style.position = positionValue; + jsc.picker.wrap.style.left = x + 'px'; + jsc.picker.wrap.style.top = y + 'px'; + + jsc.setBoxShadow( + jsc.picker.boxS, + thisObj.shadow ? + new jsc.BoxShadow(0, vShadow, thisObj.shadowBlur, 0, thisObj.shadowColor) : + null); + }, + + + getPickerDims : function (thisObj) { + var w = 2 * thisObj.controlBorderWidth + thisObj.width; + var h = 2 * thisObj.controlBorderWidth + thisObj.height; + + var sliderSpace = 2 * thisObj.controlBorderWidth + 2 * jsc.getControlPadding(thisObj) + thisObj.sliderSize; + + if (jsc.getSliderChannel(thisObj)) { + w += sliderSpace; + } + if (thisObj.hasAlphaChannel()) { + w += sliderSpace; + } + + var pal = jsc.getPaletteDims(thisObj, w); + + if (pal.height) { + h += pal.height + thisObj.padding; + } + if (thisObj.closeButton) { + h += 2 * thisObj.controlBorderWidth + thisObj.padding + thisObj.buttonHeight; + } + + var pW = w + (2 * thisObj.padding); + var pH = h + (2 * thisObj.padding); + + return { + contentW: w, + contentH: h, + paddedW: pW, + paddedH: pH, + borderW: pW + (2 * thisObj.borderWidth), + borderH: pH + (2 * thisObj.borderWidth), + palette: pal, + }; + }, + + + getPaletteDims : function (thisObj, width) { + var cols = 0, rows = 0, cellW = 0, cellH = 0, height = 0; + var sampleCount = thisObj._palette ? thisObj._palette.length : 0; + + if (sampleCount) { + cols = thisObj.paletteCols; + rows = cols > 0 ? Math.ceil(sampleCount / cols) : 0; + + // color sample's dimensions (includes border) + cellW = Math.max(1, Math.floor((width - ((cols - 1) * thisObj.paletteSpacing)) / cols)); + cellH = thisObj.paletteHeight ? Math.min(thisObj.paletteHeight, cellW) : cellW; + } + + if (rows) { + height = + rows * cellH + + (rows - 1) * thisObj.paletteSpacing; + } + + return { + cols: cols, + rows: rows, + cellW: cellW, + cellH: cellH, + width: width, + height: height, + }; + }, + + + getControlPadding : function (thisObj) { + return Math.max( + thisObj.padding / 2, + (2 * thisObj.pointerBorderWidth + thisObj.pointerThickness) - thisObj.controlBorderWidth + ); + }, + + + getPadYChannel : function (thisObj) { + switch (thisObj.mode.charAt(1).toLowerCase()) { + case 'v': return 'v'; break; + } + return 's'; + }, + + + getSliderChannel : function (thisObj) { + if (thisObj.mode.length > 2) { + switch (thisObj.mode.charAt(2).toLowerCase()) { + case 's': return 's'; break; + case 'v': return 'v'; break; + } + } + return null; + }, + + + // calls function specified in picker's property + triggerCallback : function (thisObj, prop) { + if (!thisObj[prop]) { + return; // callback func not specified + } + var callback = null; + + if (typeof thisObj[prop] === 'string') { + // string with code + try { + callback = new Function (thisObj[prop]); + } catch (e) { + console.error(e); + } + } else { + // function + callback = thisObj[prop]; + } + + if (callback) { + callback.call(thisObj); + } + }, + + + // Triggers a color change related event(s) on all picker instances. + // It is possible to specify multiple events separated with a space. + triggerGlobal : function (eventNames) { + var inst = jsc.getInstances(); + for (var i = 0; i < inst.length; i += 1) { + inst[i].trigger(eventNames); + } + }, + + + _pointerMoveEvent : { + mouse: 'mousemove', + touch: 'touchmove' + }, + _pointerEndEvent : { + mouse: 'mouseup', + touch: 'touchend' + }, + + + _pointerOrigin : null, + _capturedTarget : null, + + + onDocumentKeyUp : function (e) { + if (['Tab', 'Escape'].indexOf(jsc.eventKey(e)) !== -1) { + if (jsc.picker && jsc.picker.owner) { + jsc.picker.owner.tryHide(); + } + } + }, + + + onWindowResize : function (e) { + jsc.redrawPosition(); + }, + + + onWindowScroll : function (e) { + jsc.redrawPosition(); + }, + + + onParentScroll : function (e) { + // hide the picker when one of the parent elements is scrolled + if (jsc.picker && jsc.picker.owner) { + jsc.picker.owner.tryHide(); + } + }, + + + onDocumentMouseDown : function (e) { + var target = e.target || e.srcElement; + + if (target.jscolor && target.jscolor instanceof jsc.pub) { // clicked targetElement -> show picker + if (target.jscolor.showOnClick && !target.disabled) { + target.jscolor.show(); + } + } else if (jsc.getData(target, 'gui')) { // clicked jscolor's GUI element + var control = jsc.getData(target, 'control'); + if (control) { + // jscolor's control + jsc.onControlPointerStart(e, target, jsc.getData(target, 'control'), 'mouse'); + } + } else { + // mouse is outside the picker's controls -> hide the color picker! + if (jsc.picker && jsc.picker.owner) { + jsc.picker.owner.tryHide(); + } + } + }, + + + onPickerTouchStart : function (e) { + var target = e.target || e.srcElement; + + if (jsc.getData(target, 'control')) { + jsc.onControlPointerStart(e, target, jsc.getData(target, 'control'), 'touch'); + } + }, + + + onControlPointerStart : function (e, target, controlName, pointerType) { + var thisObj = jsc.getData(target, 'instance'); + + jsc.preventDefault(e); + jsc.captureTarget(target); + + var registerDragEvents = function (doc, offset) { + jsc.attachGroupEvent('drag', doc, jsc._pointerMoveEvent[pointerType], + jsc.onDocumentPointerMove(e, target, controlName, pointerType, offset)); + jsc.attachGroupEvent('drag', doc, jsc._pointerEndEvent[pointerType], + jsc.onDocumentPointerEnd(e, target, controlName, pointerType)); + }; + + registerDragEvents(window.document, [0, 0]); + + if (window.parent && window.frameElement) { + var rect = window.frameElement.getBoundingClientRect(); + var ofs = [-rect.left, -rect.top]; + registerDragEvents(window.parent.window.document, ofs); + } + + var abs = jsc.getAbsPointerPos(e); + var rel = jsc.getRelPointerPos(e); + jsc._pointerOrigin = { + x: abs.x - rel.x, + y: abs.y - rel.y + }; + + switch (controlName) { + case 'pad': + // if the value slider is at the bottom, move it up + if (jsc.getSliderChannel(thisObj) === 'v' && thisObj.channels.v === 0) { + thisObj.fromHSVA(null, null, 100, null); + } + jsc.setPad(thisObj, e, 0, 0); + break; + + case 'sld': + jsc.setSld(thisObj, e, 0); + break; + + case 'asld': + jsc.setASld(thisObj, e, 0); + break; + } + thisObj.trigger('input'); + }, + + + onDocumentPointerMove : function (e, target, controlName, pointerType, offset) { + return function (e) { + var thisObj = jsc.getData(target, 'instance'); + switch (controlName) { + case 'pad': + jsc.setPad(thisObj, e, offset[0], offset[1]); + break; + + case 'sld': + jsc.setSld(thisObj, e, offset[1]); + break; + + case 'asld': + jsc.setASld(thisObj, e, offset[1]); + break; + } + thisObj.trigger('input'); + } + }, + + + onDocumentPointerEnd : function (e, target, controlName, pointerType) { + return function (e) { + var thisObj = jsc.getData(target, 'instance'); + jsc.detachGroupEvents('drag'); + jsc.releaseTarget(); + + // Always trigger changes AFTER detaching outstanding mouse handlers, + // in case some color change that occured in user-defined onChange/onInput handler + // intruded into current mouse events + thisObj.trigger('input'); + thisObj.trigger('change'); + }; + }, + + + onPaletteSampleClick : function (e) { + var target = e.currentTarget; + var thisObj = jsc.getData(target, 'instance'); + var color = jsc.getData(target, 'color'); + + // when format is flexible, use the original format of this color sample + if (thisObj.format.toLowerCase() === 'any') { + thisObj._setFormat(color.format); // adapt format + if (!jsc.isAlphaFormat(thisObj.getFormat())) { + color.rgba[3] = 1.0; // when switching to a format that doesn't support alpha, set full opacity + } + } + + // if this color doesn't specify alpha, use alpha of 1.0 (if applicable) + if (color.rgba[3] === null) { + if (thisObj.paletteSetsAlpha === true || (thisObj.paletteSetsAlpha === 'auto' && thisObj._paletteHasTransparency)) { + color.rgba[3] = 1.0; + } + } + + thisObj.fromRGBA.apply(thisObj, color.rgba); + + thisObj.trigger('input'); + thisObj.trigger('change'); + + if (thisObj.hideOnPaletteClick) { + thisObj.hide(); + } + }, + + + setPad : function (thisObj, e, ofsX, ofsY) { + var pointerAbs = jsc.getAbsPointerPos(e); + var x = ofsX + pointerAbs.x - jsc._pointerOrigin.x - thisObj.padding - thisObj.controlBorderWidth; + var y = ofsY + pointerAbs.y - jsc._pointerOrigin.y - thisObj.padding - thisObj.controlBorderWidth; + + var xVal = x * (360 / (thisObj.width - 1)); + var yVal = 100 - (y * (100 / (thisObj.height - 1))); + + switch (jsc.getPadYChannel(thisObj)) { + case 's': thisObj.fromHSVA(xVal, yVal, null, null); break; + case 'v': thisObj.fromHSVA(xVal, null, yVal, null); break; + } + }, + + + setSld : function (thisObj, e, ofsY) { + var pointerAbs = jsc.getAbsPointerPos(e); + var y = ofsY + pointerAbs.y - jsc._pointerOrigin.y - thisObj.padding - thisObj.controlBorderWidth; + var yVal = 100 - (y * (100 / (thisObj.height - 1))); + + switch (jsc.getSliderChannel(thisObj)) { + case 's': thisObj.fromHSVA(null, yVal, null, null); break; + case 'v': thisObj.fromHSVA(null, null, yVal, null); break; + } + }, + + + setASld : function (thisObj, e, ofsY) { + var pointerAbs = jsc.getAbsPointerPos(e); + var y = ofsY + pointerAbs.y - jsc._pointerOrigin.y - thisObj.padding - thisObj.controlBorderWidth; + var yVal = 1.0 - (y * (1.0 / (thisObj.height - 1))); + + if (yVal < 1.0) { + // if format is flexible and the current format doesn't support alpha, switch to a suitable one + var fmt = thisObj.getFormat(); + if (thisObj.format.toLowerCase() === 'any' && !jsc.isAlphaFormat(fmt)) { + thisObj._setFormat(fmt === 'hex' ? 'hexa' : 'rgba'); + } + } + + thisObj.fromHSVA(null, null, null, yVal); + }, + + + createPadCanvas : function () { + + var ret = { + elm: null, + draw: null + }; + + var canvas = jsc.createEl('canvas'); + var ctx = canvas.getContext('2d'); + + var drawFunc = function (width, height, type) { + canvas.width = width; + canvas.height = height; + + ctx.clearRect(0, 0, canvas.width, canvas.height); + + var hGrad = ctx.createLinearGradient(0, 0, canvas.width, 0); + hGrad.addColorStop(0 / 6, '#F00'); + hGrad.addColorStop(1 / 6, '#FF0'); + hGrad.addColorStop(2 / 6, '#0F0'); + hGrad.addColorStop(3 / 6, '#0FF'); + hGrad.addColorStop(4 / 6, '#00F'); + hGrad.addColorStop(5 / 6, '#F0F'); + hGrad.addColorStop(6 / 6, '#F00'); + + ctx.fillStyle = hGrad; + ctx.fillRect(0, 0, canvas.width, canvas.height); + + var vGrad = ctx.createLinearGradient(0, 0, 0, canvas.height); + switch (type.toLowerCase()) { + case 's': + vGrad.addColorStop(0, 'rgba(255,255,255,0)'); + vGrad.addColorStop(1, 'rgba(255,255,255,1)'); + break; + case 'v': + vGrad.addColorStop(0, 'rgba(0,0,0,0)'); + vGrad.addColorStop(1, 'rgba(0,0,0,1)'); + break; + } + ctx.fillStyle = vGrad; + ctx.fillRect(0, 0, canvas.width, canvas.height); + }; + + ret.elm = canvas; + ret.draw = drawFunc; + + return ret; + }, + + + createSliderGradient : function () { + + var ret = { + elm: null, + draw: null + }; + + var canvas = jsc.createEl('canvas'); + var ctx = canvas.getContext('2d'); + + var drawFunc = function (width, height, color1, color2) { + canvas.width = width; + canvas.height = height; + + ctx.clearRect(0, 0, canvas.width, canvas.height); + + var grad = ctx.createLinearGradient(0, 0, 0, canvas.height); + grad.addColorStop(0, color1); + grad.addColorStop(1, color2); + + ctx.fillStyle = grad; + ctx.fillRect(0, 0, canvas.width, canvas.height); + }; + + ret.elm = canvas; + ret.draw = drawFunc; + + return ret; + }, + + + createASliderGradient : function () { + + var ret = { + elm: null, + draw: null + }; + + var canvas = jsc.createEl('canvas'); + var ctx = canvas.getContext('2d'); + + var drawFunc = function (width, height, color) { + canvas.width = width; + canvas.height = height; + + ctx.clearRect(0, 0, canvas.width, canvas.height); + + var sqSize = canvas.width / 2; + var sqColor1 = jsc.pub.chessboardColor1; + var sqColor2 = jsc.pub.chessboardColor2; + + // dark gray background + ctx.fillStyle = sqColor1; + ctx.fillRect(0, 0, canvas.width, canvas.height); + + if (sqSize > 0) { // to avoid infinite loop + for (var y = 0; y < canvas.height; y += sqSize * 2) { + // light gray squares + ctx.fillStyle = sqColor2; + ctx.fillRect(0, y, sqSize, sqSize); + ctx.fillRect(sqSize, y + sqSize, sqSize, sqSize); + } + } + + var grad = ctx.createLinearGradient(0, 0, 0, canvas.height); + grad.addColorStop(0, color); + grad.addColorStop(1, 'rgba(0,0,0,0)'); + + ctx.fillStyle = grad; + ctx.fillRect(0, 0, canvas.width, canvas.height); + }; + + ret.elm = canvas; + ret.draw = drawFunc; + + return ret; + }, + + + BoxShadow : (function () { + var BoxShadow = function (hShadow, vShadow, blur, spread, color, inset) { + this.hShadow = hShadow; + this.vShadow = vShadow; + this.blur = blur; + this.spread = spread; + this.color = color; + this.inset = !!inset; + }; + + BoxShadow.prototype.toString = function () { + var vals = [ + Math.round(this.hShadow) + 'px', + Math.round(this.vShadow) + 'px', + Math.round(this.blur) + 'px', + Math.round(this.spread) + 'px', + this.color + ]; + if (this.inset) { + vals.push('inset'); + } + return vals.join(' '); + }; + + return BoxShadow; + })(), + + + flags : { + leaveValue : 1 << 0, + leaveAlpha : 1 << 1, + leavePreview : 1 << 2, + }, + + + enumOpts : { + format: ['auto', 'any', 'hex', 'hexa', 'rgb', 'rgba'], + previewPosition: ['left', 'right'], + mode: ['hsv', 'hvs', 'hs', 'hv'], + position: ['left', 'right', 'top', 'bottom'], + alphaChannel: ['auto', true, false], + paletteSetsAlpha: ['auto', true, false], + }, + + + deprecatedOpts : { + // : ( can be null) + 'styleElement': 'previewElement', + 'onFineChange': 'onInput', + 'overwriteImportant': 'forceStyle', + 'closable': 'closeButton', + 'insetWidth': 'controlBorderWidth', + 'insetColor': 'controlBorderColor', + 'refine': null, + }, + + + docsRef : ' ' + 'See https://jscolor.com/docs/', + + + // + // Usage: + // var myPicker = new JSColor( [, ]) + // + // (constructor is accessible via both 'jscolor' and 'JSColor' name) + // + + pub : function (targetElement, opts) { + + var THIS = this; + + if (!opts) { + opts = {}; + } + + this.channels = { + r: 255, // red [0-255] + g: 255, // green [0-255] + b: 255, // blue [0-255] + h: 0, // hue [0-360] + s: 0, // saturation [0-100] + v: 100, // value (brightness) [0-100] + a: 1.0, // alpha (opacity) [0.0 - 1.0] + }; + + // General options + // + this.format = 'auto'; // 'auto' | 'any' | 'hex' | 'hexa' | 'rgb' | 'rgba' - Format of the input/output value + this.value = undefined; // INITIAL color value in any supported format. To change it later, use method fromString(), fromHSVA(), fromRGBA() or channel() + this.alpha = undefined; // INITIAL alpha value. To change it later, call method channel('A', ) + this.random = false; // whether to randomize the initial color. Either true | false, or an array of ranges: [minV, maxV, minS, maxS, minH, maxH, minA, maxA] + this.onChange = undefined; // called when color changes. Value can be either a function or a string with JS code. + this.onInput = undefined; // called repeatedly as the color is being changed, e.g. while dragging a slider. Value can be either a function or a string with JS code. + this.valueElement = undefined; // element that will be used to display and input the color value + this.alphaElement = undefined; // element that will be used to display and input the alpha (opacity) value + this.previewElement = undefined; // element that will preview the picked color using CSS background + this.previewPosition = 'left'; // 'left' | 'right' - position of the color preview in previewElement + this.previewSize = 32; // (px) width of the color preview displayed in previewElement + this.previewPadding = 8; // (px) space between color preview and content of the previewElement + this.required = true; // whether the associated text input must always contain a color value. If false, the input can be left empty. + this.hash = true; // whether to prefix the HEX color code with # symbol (only applicable for HEX format) + this.uppercase = true; // whether to show the HEX color code in upper case (only applicable for HEX format) + this.forceStyle = true; // whether to overwrite CSS style of the previewElement using !important flag + + // Color Picker options + // + this.width = 181; // width of the color spectrum (in px) + this.height = 101; // height of the color spectrum (in px) + this.mode = 'HSV'; // 'HSV' | 'HVS' | 'HS' | 'HV' - layout of the color picker controls + this.alphaChannel = 'auto'; // 'auto' | true | false - if alpha channel is enabled, the alpha slider will be visible. If 'auto', it will be determined according to color format + this.position = 'bottom'; // 'left' | 'right' | 'top' | 'bottom' - position relative to the target element + this.smartPosition = true; // automatically change picker position when there is not enough space for it + this.showOnClick = true; // whether to show the picker when user clicks its target element + this.hideOnLeave = true; // whether to automatically hide the picker when user leaves its target element (e.g. upon clicking the document) + this.palette = []; // colors to be displayed in the palette, specified as an array or a string of space separated color values (in any supported format) + this.paletteCols = 10; // number of columns in the palette + this.paletteSetsAlpha = 'auto'; // 'auto' | true | false - if true, palette colors that don't specify alpha will set alpha to 1.0 + this.paletteHeight = 16; // maximum height (px) of a row in the palette + this.paletteSpacing = 4; // distance (px) between color samples in the palette + this.hideOnPaletteClick = false; // when set to true, clicking the palette will also hide the color picker + this.sliderSize = 16; // px + this.crossSize = 8; // px + this.closeButton = false; // whether to display the Close button + this.closeText = 'Close'; + this.buttonColor = 'rgba(0,0,0,1)'; // CSS color + this.buttonHeight = 18; // px + this.padding = 12; // px + this.backgroundColor = 'rgba(255,255,255,1)'; // CSS color + this.borderWidth = 1; // px + this.borderColor = 'rgba(187,187,187,1)'; // CSS color + this.borderRadius = 8; // px + this.controlBorderWidth = 1; // px + this.controlBorderColor = 'rgba(187,187,187,1)'; // CSS color + this.shadow = true; // whether to display a shadow + this.shadowBlur = 15; // px + this.shadowColor = 'rgba(0,0,0,0.2)'; // CSS color + this.pointerColor = 'rgba(76,76,76,1)'; // CSS color + this.pointerBorderWidth = 1; // px + this.pointerBorderColor = 'rgba(255,255,255,1)'; // CSS color + this.pointerThickness = 2; // px + this.zIndex = 5000; + this.container = undefined; // where to append the color picker (BODY element by default) + + // Experimental + // + this.minS = 0; // min allowed saturation (0 - 100) + this.maxS = 100; // max allowed saturation (0 - 100) + this.minV = 0; // min allowed value (brightness) (0 - 100) + this.maxV = 100; // max allowed value (brightness) (0 - 100) + this.minA = 0.0; // min allowed alpha (opacity) (0.0 - 1.0) + this.maxA = 1.0; // max allowed alpha (opacity) (0.0 - 1.0) + + + // Getter: option(name) + // Setter: option(name, value) + // option({name:value, ...}) + // + this.option = function () { + if (!arguments.length) { + throw new Error('No option specified'); + } + + if (arguments.length === 1 && typeof arguments[0] === 'string') { + // getting a single option + try { + return getOption(arguments[0]); + } catch (e) { + console.warn(e); + } + return false; + + } else if (arguments.length >= 2 && typeof arguments[0] === 'string') { + // setting a single option + try { + if (!setOption(arguments[0], arguments[1])) { + return false; + } + } catch (e) { + console.warn(e); + return false; + } + this.redraw(); // immediately redraws the picker, if it's displayed + this.exposeColor(); // in case some preview-related or format-related option was changed + return true; + + } else if (arguments.length === 1 && typeof arguments[0] === 'object') { + // setting multiple options + var opts = arguments[0]; + var success = true; + for (var opt in opts) { + if (opts.hasOwnProperty(opt)) { + try { + if (!setOption(opt, opts[opt])) { + success = false; + } + } catch (e) { + console.warn(e); + success = false; + } + } + } + this.redraw(); // immediately redraws the picker, if it's displayed + this.exposeColor(); // in case some preview-related or format-related option was changed + return success; + } + + throw new Error('Invalid arguments'); + } + + + // Getter: channel(name) + // Setter: channel(name, value) + // + this.channel = function (name, value) { + if (typeof name !== 'string') { + throw new Error('Invalid value for channel name: ' + name); + } + + if (value === undefined) { + // getting channel value + if (!this.channels.hasOwnProperty(name.toLowerCase())) { + console.warn('Getting unknown channel: ' + name); + return false; + } + return this.channels[name.toLowerCase()]; + + } else { + // setting channel value + var res = false; + switch (name.toLowerCase()) { + case 'r': res = this.fromRGBA(value, null, null, null); break; + case 'g': res = this.fromRGBA(null, value, null, null); break; + case 'b': res = this.fromRGBA(null, null, value, null); break; + case 'h': res = this.fromHSVA(value, null, null, null); break; + case 's': res = this.fromHSVA(null, value, null, null); break; + case 'v': res = this.fromHSVA(null, null, value, null); break; + case 'a': res = this.fromHSVA(null, null, null, value); break; + default: + console.warn('Setting unknown channel: ' + name); + return false; + } + if (res) { + this.redraw(); // immediately redraws the picker, if it's displayed + return true; + } + } + + return false; + } + + + // Triggers given input event(s) by: + // - executing on callback specified as picker's option + // - triggering standard DOM event listeners attached to the value element + // + // It is possible to specify multiple events separated with a space. + // + this.trigger = function (eventNames) { + var evs = jsc.strList(eventNames); + for (var i = 0; i < evs.length; i += 1) { + var ev = evs[i].toLowerCase(); + + // trigger a callback + var callbackProp = null; + switch (ev) { + case 'input': callbackProp = 'onInput'; break; + case 'change': callbackProp = 'onChange'; break; + } + if (callbackProp) { + jsc.triggerCallback(this, callbackProp); + } + + // trigger standard DOM event listeners on the value element + jsc.triggerInputEvent(this.valueElement, ev, true, true); + } + }; + + + // h: 0-360 + // s: 0-100 + // v: 0-100 + // a: 0.0-1.0 + // + this.fromHSVA = function (h, s, v, a, flags) { // null = don't change + if (h === undefined) { h = null; } + if (s === undefined) { s = null; } + if (v === undefined) { v = null; } + if (a === undefined) { a = null; } + + if (h !== null) { + if (isNaN(h)) { return false; } + this.channels.h = Math.max(0, Math.min(360, h)); + } + if (s !== null) { + if (isNaN(s)) { return false; } + this.channels.s = Math.max(0, Math.min(100, this.maxS, s), this.minS); + } + if (v !== null) { + if (isNaN(v)) { return false; } + this.channels.v = Math.max(0, Math.min(100, this.maxV, v), this.minV); + } + if (a !== null) { + if (isNaN(a)) { return false; } + this.channels.a = this.hasAlphaChannel() ? + Math.max(0, Math.min(1, this.maxA, a), this.minA) : + 1.0; // if alpha channel is disabled, the color should stay 100% opaque + } + + var rgb = jsc.HSV_RGB( + this.channels.h, + this.channels.s, + this.channels.v + ); + this.channels.r = rgb[0]; + this.channels.g = rgb[1]; + this.channels.b = rgb[2]; + + this.exposeColor(flags); + return true; + }; + + + // r: 0-255 + // g: 0-255 + // b: 0-255 + // a: 0.0-1.0 + // + this.fromRGBA = function (r, g, b, a, flags) { // null = don't change + if (r === undefined) { r = null; } + if (g === undefined) { g = null; } + if (b === undefined) { b = null; } + if (a === undefined) { a = null; } + + if (r !== null) { + if (isNaN(r)) { return false; } + r = Math.max(0, Math.min(255, r)); + } + if (g !== null) { + if (isNaN(g)) { return false; } + g = Math.max(0, Math.min(255, g)); + } + if (b !== null) { + if (isNaN(b)) { return false; } + b = Math.max(0, Math.min(255, b)); + } + if (a !== null) { + if (isNaN(a)) { return false; } + this.channels.a = this.hasAlphaChannel() ? + Math.max(0, Math.min(1, this.maxA, a), this.minA) : + 1.0; // if alpha channel is disabled, the color should stay 100% opaque + } + + var hsv = jsc.RGB_HSV( + r===null ? this.channels.r : r, + g===null ? this.channels.g : g, + b===null ? this.channels.b : b + ); + if (hsv[0] !== null) { + this.channels.h = Math.max(0, Math.min(360, hsv[0])); + } + if (hsv[2] !== 0) { // fully black color stays black through entire saturation range, so let's not change saturation + this.channels.s = Math.max(0, this.minS, Math.min(100, this.maxS, hsv[1])); + } + this.channels.v = Math.max(0, this.minV, Math.min(100, this.maxV, hsv[2])); + + // update RGB according to final HSV, as some values might be trimmed + var rgb = jsc.HSV_RGB(this.channels.h, this.channels.s, this.channels.v); + this.channels.r = rgb[0]; + this.channels.g = rgb[1]; + this.channels.b = rgb[2]; + + this.exposeColor(flags); + return true; + }; + + + // DEPRECATED. Use .fromHSVA() instead + // + this.fromHSV = function (h, s, v, flags) { + console.warn('fromHSV() method is DEPRECATED. Using fromHSVA() instead.' + jsc.docsRef); + return this.fromHSVA(h, s, v, null, flags); + }; + + + // DEPRECATED. Use .fromRGBA() instead + // + this.fromRGB = function (r, g, b, flags) { + console.warn('fromRGB() method is DEPRECATED. Using fromRGBA() instead.' + jsc.docsRef); + return this.fromRGBA(r, g, b, null, flags); + }; + + + this.fromString = function (str, flags) { + if (!this.required && str.trim() === '') { + // setting empty string to an optional color input + this.setPreviewElementBg(null); + this.setValueElementValue(''); + return true; + } + + var color = jsc.parseColorString(str); + if (!color) { + return false; // could not parse + } + if (this.format.toLowerCase() === 'any') { + this._setFormat(color.format); // adapt format + if (!jsc.isAlphaFormat(this.getFormat())) { + color.rgba[3] = 1.0; // when switching to a format that doesn't support alpha, set full opacity + } + } + this.fromRGBA( + color.rgba[0], + color.rgba[1], + color.rgba[2], + color.rgba[3], + flags + ); + return true; + }; + + + this.randomize = function (minV, maxV, minS, maxS, minH, maxH, minA, maxA) { + if (minV === undefined) { minV = 0; } + if (maxV === undefined) { maxV = 100; } + if (minS === undefined) { minS = 0; } + if (maxS === undefined) { maxS = 100; } + if (minH === undefined) { minH = 0; } + if (maxH === undefined) { maxH = 359; } + if (minA === undefined) { minA = 1; } + if (maxA === undefined) { maxA = 1; } + + this.fromHSVA( + minH + Math.floor(Math.random() * (maxH - minH + 1)), + minS + Math.floor(Math.random() * (maxS - minS + 1)), + minV + Math.floor(Math.random() * (maxV - minV + 1)), + ((100 * minA) + Math.floor(Math.random() * (100 * (maxA - minA) + 1))) / 100 + ); + }; + + + this.toString = function (format) { + if (format === undefined) { + format = this.getFormat(); // format not specified -> use the current format + } + switch (format.toLowerCase()) { + case 'hex': return this.toHEXString(); break; + case 'hexa': return this.toHEXAString(); break; + case 'rgb': return this.toRGBString(); break; + case 'rgba': return this.toRGBAString(); break; + } + return false; + }; + + + this.toHEXString = function () { + return jsc.hexColor( + this.channels.r, + this.channels.g, + this.channels.b + ); + }; + + + this.toHEXAString = function () { + return jsc.hexaColor( + this.channels.r, + this.channels.g, + this.channels.b, + this.channels.a + ); + }; + + + this.toRGBString = function () { + return jsc.rgbColor( + this.channels.r, + this.channels.g, + this.channels.b + ); + }; + + + this.toRGBAString = function () { + return jsc.rgbaColor( + this.channels.r, + this.channels.g, + this.channels.b, + this.channels.a + ); + }; + + + this.toGrayscale = function () { + return ( + 0.213 * this.channels.r + + 0.715 * this.channels.g + + 0.072 * this.channels.b + ); + }; + + + this.toCanvas = function () { + return jsc.genColorPreviewCanvas(this.toRGBAString()).canvas; + }; + + + this.toDataURL = function () { + return this.toCanvas().toDataURL(); + }; + + + this.toBackground = function () { + return jsc.pub.background(this.toRGBAString()); + }; + + + this.isLight = function () { + return this.toGrayscale() > 255 / 2; + }; + + + this.hide = function () { + if (isPickerOwner()) { + detachPicker(); + } + }; + + + this.show = function () { + drawPicker(); + }; + + + this.redraw = function () { + if (isPickerOwner()) { + drawPicker(); + } + }; + + + this.getFormat = function () { + return this._currentFormat; + }; + + + this._setFormat = function (format) { + this._currentFormat = format.toLowerCase(); + }; + + + this.hasAlphaChannel = function () { + if (this.alphaChannel === 'auto') { + return ( + this.format.toLowerCase() === 'any' || // format can change on the fly (e.g. from hex to rgba), so let's consider the alpha channel enabled + jsc.isAlphaFormat(this.getFormat()) || // the current format supports alpha channel + this.alpha !== undefined || // initial alpha value is set, so we're working with alpha channel + this.alphaElement !== undefined // the alpha value is redirected, so we're working with alpha channel + ); + } + + return this.alphaChannel; // the alpha channel is explicitly set + }; + + + this.processValueInput = function (str) { + if (!this.fromString(str)) { + // could not parse the color value - let's just expose the current color + this.exposeColor(); + } + }; + + + this.processAlphaInput = function (str) { + if (!this.fromHSVA(null, null, null, parseFloat(str))) { + // could not parse the alpha value - let's just expose the current color + this.exposeColor(); + } + }; + + + this.exposeColor = function (flags) { + var colorStr = this.toString(); + var fmt = this.getFormat(); + + // reflect current color in data- attribute + jsc.setDataAttr(this.targetElement, 'current-color', colorStr); + + if (!(flags & jsc.flags.leaveValue) && this.valueElement) { + if (fmt === 'hex' || fmt === 'hexa') { + if (!this.uppercase) { colorStr = colorStr.toLowerCase(); } + if (!this.hash) { colorStr = colorStr.replace(/^#/, ''); } + } + this.setValueElementValue(colorStr); + } + + if (!(flags & jsc.flags.leaveAlpha) && this.alphaElement) { + var alphaVal = Math.round(this.channels.a * 100) / 100; + this.setAlphaElementValue(alphaVal); + } + + if (!(flags & jsc.flags.leavePreview) && this.previewElement) { + var previewPos = null; // 'left' | 'right' (null -> fill the entire element) + + if ( + jsc.isTextInput(this.previewElement) || // text input + (jsc.isButton(this.previewElement) && !jsc.isButtonEmpty(this.previewElement)) // button with text + ) { + previewPos = this.previewPosition; + } + + this.setPreviewElementBg(this.toRGBAString()); + } + + if (isPickerOwner()) { + redrawPad(); + redrawSld(); + redrawASld(); + } + }; + + + this.setPreviewElementBg = function (color) { + if (!this.previewElement) { + return; + } + + var position = null; // color preview position: null | 'left' | 'right' + var width = null; // color preview width: px | null = fill the entire element + if ( + jsc.isTextInput(this.previewElement) || // text input + (jsc.isButton(this.previewElement) && !jsc.isButtonEmpty(this.previewElement)) // button with text + ) { + position = this.previewPosition; + width = this.previewSize; + } + + var backgrounds = []; + + if (!color) { + // there is no color preview to display -> let's remove any previous background image + backgrounds.push({ + image: 'none', + position: 'left top', + size: 'auto', + repeat: 'no-repeat', + origin: 'padding-box', + }); + } else { + // CSS gradient for background color preview + backgrounds.push({ + image: jsc.genColorPreviewGradient( + color, + position, + width ? width - jsc.pub.previewSeparator.length : null + ), + position: 'left top', + size: 'auto', + repeat: position ? 'repeat-y' : 'repeat', + origin: 'padding-box', + }); + + // data URL of generated PNG image with a gray transparency chessboard + var preview = jsc.genColorPreviewCanvas( + 'rgba(0,0,0,0)', + position ? {'left':'right', 'right':'left'}[position] : null, + width, + true + ); + backgrounds.push({ + image: 'url(\'' + preview.canvas.toDataURL() + '\')', + position: (position || 'left') + ' top', + size: preview.width + 'px ' + preview.height + 'px', + repeat: position ? 'repeat-y' : 'repeat', + origin: 'padding-box', + }); + } + + var bg = { + image: [], + position: [], + size: [], + repeat: [], + origin: [], + }; + for (var i = 0; i < backgrounds.length; i += 1) { + bg.image.push(backgrounds[i].image); + bg.position.push(backgrounds[i].position); + bg.size.push(backgrounds[i].size); + bg.repeat.push(backgrounds[i].repeat); + bg.origin.push(backgrounds[i].origin); + } + + // set previewElement's background-images + var sty = { + 'background-image': bg.image.join(', '), + 'background-position': bg.position.join(', '), + 'background-size': bg.size.join(', '), + 'background-repeat': bg.repeat.join(', '), + 'background-origin': bg.origin.join(', '), + }; + jsc.setStyle(this.previewElement, sty, this.forceStyle); + + + // set/restore previewElement's padding + var padding = { + left: null, + right: null, + }; + if (position) { + padding[position] = (this.previewSize + this.previewPadding) + 'px'; + } + + var sty = { + 'padding-left': padding.left, + 'padding-right': padding.right, + }; + jsc.setStyle(this.previewElement, sty, this.forceStyle, true); + }; + + + this.setValueElementValue = function (str) { + if (this.valueElement) { + if (jsc.nodeName(this.valueElement) === 'input') { + this.valueElement.value = str; + } else { + this.valueElement.innerHTML = str; + } + } + }; + + + this.setAlphaElementValue = function (str) { + if (this.alphaElement) { + if (jsc.nodeName(this.alphaElement) === 'input') { + this.alphaElement.value = str; + } else { + this.alphaElement.innerHTML = str; + } + } + }; + + + this._processParentElementsInDOM = function () { + if (this._parentElementsProcessed) { return; } + this._parentElementsProcessed = true; + + var elm = this.targetElement; + do { + // If the target element or one of its parent nodes has fixed position, + // then use fixed positioning instead + var compStyle = jsc.getCompStyle(elm); + if (compStyle.position && compStyle.position.toLowerCase() === 'fixed') { + this.fixed = true; + } + + if (elm !== this.targetElement) { + // Ensure to attach onParentScroll only once to each parent element + // (multiple targetElements can share the same parent nodes) + // + // Note: It's not just offsetParents that can be scrollable, + // that's why we loop through all parent nodes + if (!jsc.getData(elm, 'hasScrollListener')) { + elm.addEventListener('scroll', jsc.onParentScroll, false); + jsc.setData(elm, 'hasScrollListener', true); + } + } + } while ((elm = elm.parentNode) && jsc.nodeName(elm) !== 'body'); + }; + + + this.tryHide = function () { + if (this.hideOnLeave) { + this.hide(); + } + }; + + + this.set__palette = function (val) { + this.palette = val; + this._palette = jsc.parsePaletteValue(val); + this._paletteHasTransparency = jsc.containsTranparentColor(this._palette); + }; + + + function setOption (option, value) { + if (typeof option !== 'string') { + throw new Error('Invalid value for option name: ' + option); + } + + // enum option + if (jsc.enumOpts.hasOwnProperty(option)) { + if (typeof value === 'string') { // enum string values are case insensitive + value = value.toLowerCase(); + } + if (jsc.enumOpts[option].indexOf(value) === -1) { + throw new Error('Option \'' + option + '\' has invalid value: ' + value); + } + } + + // deprecated option + if (jsc.deprecatedOpts.hasOwnProperty(option)) { + var oldOpt = option; + var newOpt = jsc.deprecatedOpts[option]; + if (newOpt) { + // if we have a new name for this option, let's log a warning and use the new name + console.warn('Option \'%s\' is DEPRECATED, using \'%s\' instead.' + jsc.docsRef, oldOpt, newOpt); + option = newOpt; + } else { + // new name not available for the option + throw new Error('Option \'' + option + '\' is DEPRECATED'); + } + } + + var setter = 'set__' + option; + + if (typeof THIS[setter] === 'function') { // a setter exists for this option + THIS[setter](value); + return true; + + } else if (option in THIS) { // option exists as a property + THIS[option] = value; + return true; + } + + throw new Error('Unrecognized configuration option: ' + option); + } + + + function getOption (option) { + if (typeof option !== 'string') { + throw new Error('Invalid value for option name: ' + option); + } + + // deprecated option + if (jsc.deprecatedOpts.hasOwnProperty(option)) { + var oldOpt = option; + var newOpt = jsc.deprecatedOpts[option]; + if (newOpt) { + // if we have a new name for this option, let's log a warning and use the new name + console.warn('Option \'%s\' is DEPRECATED, using \'%s\' instead.' + jsc.docsRef, oldOpt, newOpt); + option = newOpt; + } else { + // new name not available for the option + throw new Error('Option \'' + option + '\' is DEPRECATED'); + } + } + + var getter = 'get__' + option; + + if (typeof THIS[getter] === 'function') { // a getter exists for this option + return THIS[getter](value); + + } else if (option in THIS) { // option exists as a property + return THIS[option]; + } + + throw new Error('Unrecognized configuration option: ' + option); + } + + + function detachPicker () { + jsc.removeClass(THIS.targetElement, jsc.pub.activeClassName); + jsc.picker.wrap.parentNode.removeChild(jsc.picker.wrap); + delete jsc.picker.owner; + } + + + function drawPicker () { + + // At this point, when drawing the picker, we know what the parent elements are + // and we can do all related DOM operations, such as registering events on them + // or checking their positioning + THIS._processParentElementsInDOM(); + + if (!jsc.picker) { + jsc.picker = { + owner: null, // owner picker instance + wrap : jsc.createEl('div'), + box : jsc.createEl('div'), + boxS : jsc.createEl('div'), // shadow area + boxB : jsc.createEl('div'), // border + pad : jsc.createEl('div'), + padB : jsc.createEl('div'), // border + padM : jsc.createEl('div'), // mouse/touch area + padCanvas : jsc.createPadCanvas(), + cross : jsc.createEl('div'), + crossBY : jsc.createEl('div'), // border Y + crossBX : jsc.createEl('div'), // border X + crossLY : jsc.createEl('div'), // line Y + crossLX : jsc.createEl('div'), // line X + sld : jsc.createEl('div'), // slider + sldB : jsc.createEl('div'), // border + sldM : jsc.createEl('div'), // mouse/touch area + sldGrad : jsc.createSliderGradient(), + sldPtrS : jsc.createEl('div'), // slider pointer spacer + sldPtrIB : jsc.createEl('div'), // slider pointer inner border + sldPtrMB : jsc.createEl('div'), // slider pointer middle border + sldPtrOB : jsc.createEl('div'), // slider pointer outer border + asld : jsc.createEl('div'), // alpha slider + asldB : jsc.createEl('div'), // border + asldM : jsc.createEl('div'), // mouse/touch area + asldGrad : jsc.createASliderGradient(), + asldPtrS : jsc.createEl('div'), // slider pointer spacer + asldPtrIB : jsc.createEl('div'), // slider pointer inner border + asldPtrMB : jsc.createEl('div'), // slider pointer middle border + asldPtrOB : jsc.createEl('div'), // slider pointer outer border + pal : jsc.createEl('div'), // palette + btn : jsc.createEl('div'), + btnT : jsc.createEl('span'), // text + }; + + jsc.picker.pad.appendChild(jsc.picker.padCanvas.elm); + jsc.picker.padB.appendChild(jsc.picker.pad); + jsc.picker.cross.appendChild(jsc.picker.crossBY); + jsc.picker.cross.appendChild(jsc.picker.crossBX); + jsc.picker.cross.appendChild(jsc.picker.crossLY); + jsc.picker.cross.appendChild(jsc.picker.crossLX); + jsc.picker.padB.appendChild(jsc.picker.cross); + jsc.picker.box.appendChild(jsc.picker.padB); + jsc.picker.box.appendChild(jsc.picker.padM); + + jsc.picker.sld.appendChild(jsc.picker.sldGrad.elm); + jsc.picker.sldB.appendChild(jsc.picker.sld); + jsc.picker.sldB.appendChild(jsc.picker.sldPtrOB); + jsc.picker.sldPtrOB.appendChild(jsc.picker.sldPtrMB); + jsc.picker.sldPtrMB.appendChild(jsc.picker.sldPtrIB); + jsc.picker.sldPtrIB.appendChild(jsc.picker.sldPtrS); + jsc.picker.box.appendChild(jsc.picker.sldB); + jsc.picker.box.appendChild(jsc.picker.sldM); + + jsc.picker.asld.appendChild(jsc.picker.asldGrad.elm); + jsc.picker.asldB.appendChild(jsc.picker.asld); + jsc.picker.asldB.appendChild(jsc.picker.asldPtrOB); + jsc.picker.asldPtrOB.appendChild(jsc.picker.asldPtrMB); + jsc.picker.asldPtrMB.appendChild(jsc.picker.asldPtrIB); + jsc.picker.asldPtrIB.appendChild(jsc.picker.asldPtrS); + jsc.picker.box.appendChild(jsc.picker.asldB); + jsc.picker.box.appendChild(jsc.picker.asldM); + + jsc.picker.box.appendChild(jsc.picker.pal); + + jsc.picker.btn.appendChild(jsc.picker.btnT); + jsc.picker.box.appendChild(jsc.picker.btn); + + jsc.picker.boxB.appendChild(jsc.picker.box); + jsc.picker.wrap.appendChild(jsc.picker.boxS); + jsc.picker.wrap.appendChild(jsc.picker.boxB); + + jsc.picker.wrap.addEventListener('touchstart', jsc.onPickerTouchStart, + jsc.isPassiveEventSupported ? {passive: false} : false); + } + + var p = jsc.picker; + + var displaySlider = !!jsc.getSliderChannel(THIS); + var displayAlphaSlider = THIS.hasAlphaChannel(); + var pickerDims = jsc.getPickerDims(THIS); + var crossOuterSize = (2 * THIS.pointerBorderWidth + THIS.pointerThickness + 2 * THIS.crossSize); + var controlPadding = jsc.getControlPadding(THIS); + var borderRadius = Math.min( + THIS.borderRadius, + Math.round(THIS.padding * Math.PI)); // px + var padCursor = 'crosshair'; + + // wrap + p.wrap.className = 'jscolor-picker-wrap'; + p.wrap.style.clear = 'both'; + p.wrap.style.width = pickerDims.borderW + 'px'; + p.wrap.style.height = pickerDims.borderH + 'px'; + p.wrap.style.zIndex = THIS.zIndex; + + // picker + p.box.className = 'jscolor-picker'; + p.box.style.width = pickerDims.paddedW + 'px'; + p.box.style.height = pickerDims.paddedH + 'px'; + p.box.style.position = 'relative'; + + // picker shadow + p.boxS.className = 'jscolor-picker-shadow'; + p.boxS.style.position = 'absolute'; + p.boxS.style.left = '0'; + p.boxS.style.top = '0'; + p.boxS.style.width = '100%'; + p.boxS.style.height = '100%'; + jsc.setBorderRadius(p.boxS, borderRadius + 'px'); + + // picker border + p.boxB.className = 'jscolor-picker-border'; + p.boxB.style.position = 'relative'; + p.boxB.style.border = THIS.borderWidth + 'px solid'; + p.boxB.style.borderColor = THIS.borderColor; + p.boxB.style.background = THIS.backgroundColor; + jsc.setBorderRadius(p.boxB, borderRadius + 'px'); + + // IE hack: + // If the element is transparent, IE will trigger the event on the elements under it, + // e.g. on Canvas or on elements with border + p.padM.style.background = 'rgba(255,0,0,.2)'; + p.sldM.style.background = 'rgba(0,255,0,.2)'; + p.asldM.style.background = 'rgba(0,0,255,.2)'; + + p.padM.style.opacity = + p.sldM.style.opacity = + p.asldM.style.opacity = + '0'; + + // pad + p.pad.style.position = 'relative'; + p.pad.style.width = THIS.width + 'px'; + p.pad.style.height = THIS.height + 'px'; + + // pad - color spectrum (HSV and HVS) + p.padCanvas.draw(THIS.width, THIS.height, jsc.getPadYChannel(THIS)); + + // pad border + p.padB.style.position = 'absolute'; + p.padB.style.left = THIS.padding + 'px'; + p.padB.style.top = THIS.padding + 'px'; + p.padB.style.border = THIS.controlBorderWidth + 'px solid'; + p.padB.style.borderColor = THIS.controlBorderColor; + + // pad mouse area + p.padM.style.position = 'absolute'; + p.padM.style.left = 0 + 'px'; + p.padM.style.top = 0 + 'px'; + p.padM.style.width = (THIS.padding + 2 * THIS.controlBorderWidth + THIS.width + controlPadding) + 'px'; + p.padM.style.height = (2 * THIS.controlBorderWidth + 2 * THIS.padding + THIS.height) + 'px'; + p.padM.style.cursor = padCursor; + jsc.setData(p.padM, { + instance: THIS, + control: 'pad', + }) + + // pad cross + p.cross.style.position = 'absolute'; + p.cross.style.left = + p.cross.style.top = + '0'; + p.cross.style.width = + p.cross.style.height = + crossOuterSize + 'px'; + + // pad cross border Y and X + p.crossBY.style.position = + p.crossBX.style.position = + 'absolute'; + p.crossBY.style.background = + p.crossBX.style.background = + THIS.pointerBorderColor; + p.crossBY.style.width = + p.crossBX.style.height = + (2 * THIS.pointerBorderWidth + THIS.pointerThickness) + 'px'; + p.crossBY.style.height = + p.crossBX.style.width = + crossOuterSize + 'px'; + p.crossBY.style.left = + p.crossBX.style.top = + (Math.floor(crossOuterSize / 2) - Math.floor(THIS.pointerThickness / 2) - THIS.pointerBorderWidth) + 'px'; + p.crossBY.style.top = + p.crossBX.style.left = + '0'; + + // pad cross line Y and X + p.crossLY.style.position = + p.crossLX.style.position = + 'absolute'; + p.crossLY.style.background = + p.crossLX.style.background = + THIS.pointerColor; + p.crossLY.style.height = + p.crossLX.style.width = + (crossOuterSize - 2 * THIS.pointerBorderWidth) + 'px'; + p.crossLY.style.width = + p.crossLX.style.height = + THIS.pointerThickness + 'px'; + p.crossLY.style.left = + p.crossLX.style.top = + (Math.floor(crossOuterSize / 2) - Math.floor(THIS.pointerThickness / 2)) + 'px'; + p.crossLY.style.top = + p.crossLX.style.left = + THIS.pointerBorderWidth + 'px'; + + + // slider + p.sld.style.overflow = 'hidden'; + p.sld.style.width = THIS.sliderSize + 'px'; + p.sld.style.height = THIS.height + 'px'; + + // slider gradient + p.sldGrad.draw(THIS.sliderSize, THIS.height, '#000', '#000'); + + // slider border + p.sldB.style.display = displaySlider ? 'block' : 'none'; + p.sldB.style.position = 'absolute'; + p.sldB.style.left = (THIS.padding + THIS.width + 2 * THIS.controlBorderWidth + 2 * controlPadding) + 'px'; + p.sldB.style.top = THIS.padding + 'px'; + p.sldB.style.border = THIS.controlBorderWidth + 'px solid'; + p.sldB.style.borderColor = THIS.controlBorderColor; + + // slider mouse area + p.sldM.style.display = displaySlider ? 'block' : 'none'; + p.sldM.style.position = 'absolute'; + p.sldM.style.left = (THIS.padding + THIS.width + 2 * THIS.controlBorderWidth + controlPadding) + 'px'; + p.sldM.style.top = 0 + 'px'; + p.sldM.style.width = ( + (THIS.sliderSize + 2 * controlPadding + 2 * THIS.controlBorderWidth) + + (displayAlphaSlider ? 0 : Math.max(0, THIS.padding - controlPadding)) // remaining padding to the right edge + ) + 'px'; + p.sldM.style.height = (2 * THIS.controlBorderWidth + 2 * THIS.padding + THIS.height) + 'px'; + p.sldM.style.cursor = 'default'; + jsc.setData(p.sldM, { + instance: THIS, + control: 'sld', + }); + + // slider pointer inner and outer border + p.sldPtrIB.style.border = + p.sldPtrOB.style.border = + THIS.pointerBorderWidth + 'px solid ' + THIS.pointerBorderColor; + + // slider pointer outer border + p.sldPtrOB.style.position = 'absolute'; + p.sldPtrOB.style.left = -(2 * THIS.pointerBorderWidth + THIS.pointerThickness) + 'px'; + p.sldPtrOB.style.top = '0'; + + // slider pointer middle border + p.sldPtrMB.style.border = THIS.pointerThickness + 'px solid ' + THIS.pointerColor; + + // slider pointer spacer + p.sldPtrS.style.width = THIS.sliderSize + 'px'; + p.sldPtrS.style.height = jsc.pub.sliderInnerSpace + 'px'; + + + // alpha slider + p.asld.style.overflow = 'hidden'; + p.asld.style.width = THIS.sliderSize + 'px'; + p.asld.style.height = THIS.height + 'px'; + + // alpha slider gradient + p.asldGrad.draw(THIS.sliderSize, THIS.height, '#000'); + + // alpha slider border + p.asldB.style.display = displayAlphaSlider ? 'block' : 'none'; + p.asldB.style.position = 'absolute'; + p.asldB.style.left = ( + (THIS.padding + THIS.width + 2 * THIS.controlBorderWidth + controlPadding) + + (displaySlider ? (THIS.sliderSize + 3 * controlPadding + 2 * THIS.controlBorderWidth) : 0) + ) + 'px'; + p.asldB.style.top = THIS.padding + 'px'; + p.asldB.style.border = THIS.controlBorderWidth + 'px solid'; + p.asldB.style.borderColor = THIS.controlBorderColor; + + // alpha slider mouse area + p.asldM.style.display = displayAlphaSlider ? 'block' : 'none'; + p.asldM.style.position = 'absolute'; + p.asldM.style.left = ( + (THIS.padding + THIS.width + 2 * THIS.controlBorderWidth + controlPadding) + + (displaySlider ? (THIS.sliderSize + 2 * controlPadding + 2 * THIS.controlBorderWidth) : 0) + ) + 'px'; + p.asldM.style.top = 0 + 'px'; + p.asldM.style.width = ( + (THIS.sliderSize + 2 * controlPadding + 2 * THIS.controlBorderWidth) + + Math.max(0, THIS.padding - controlPadding) // remaining padding to the right edge + ) + 'px'; + p.asldM.style.height = (2 * THIS.controlBorderWidth + 2 * THIS.padding + THIS.height) + 'px'; + p.asldM.style.cursor = 'default'; + jsc.setData(p.asldM, { + instance: THIS, + control: 'asld', + }) + + // alpha slider pointer inner and outer border + p.asldPtrIB.style.border = + p.asldPtrOB.style.border = + THIS.pointerBorderWidth + 'px solid ' + THIS.pointerBorderColor; + + // alpha slider pointer outer border + p.asldPtrOB.style.position = 'absolute'; + p.asldPtrOB.style.left = -(2 * THIS.pointerBorderWidth + THIS.pointerThickness) + 'px'; + p.asldPtrOB.style.top = '0'; + + // alpha slider pointer middle border + p.asldPtrMB.style.border = THIS.pointerThickness + 'px solid ' + THIS.pointerColor; + + // alpha slider pointer spacer + p.asldPtrS.style.width = THIS.sliderSize + 'px'; + p.asldPtrS.style.height = jsc.pub.sliderInnerSpace + 'px'; + + + // palette + p.pal.className = 'jscolor-palette'; + p.pal.style.display = pickerDims.palette.rows ? 'block' : 'none'; + p.pal.style.position = 'absolute'; + p.pal.style.left = THIS.padding + 'px'; + p.pal.style.top = (2 * THIS.controlBorderWidth + 2 * THIS.padding + THIS.height) + 'px'; + + // palette's color samples + + p.pal.innerHTML = ''; + + var chessboard = jsc.genColorPreviewCanvas('rgba(0,0,0,0)'); + + var si = 0; // color sample's index + for (var r = 0; r < pickerDims.palette.rows; r++) { + for (var c = 0; c < pickerDims.palette.cols && si < THIS._palette.length; c++, si++) { + var sampleColor = THIS._palette[si]; + var sampleCssColor = jsc.rgbaColor.apply(null, sampleColor.rgba); + + var sc = jsc.createEl('div'); // color sample's color + sc.style.width = (pickerDims.palette.cellW - 2 * THIS.controlBorderWidth) + 'px'; + sc.style.height = (pickerDims.palette.cellH - 2 * THIS.controlBorderWidth) + 'px'; + sc.style.backgroundColor = sampleCssColor; + + var sw = jsc.createEl('div'); // color sample's wrap + sw.className = 'jscolor-palette-sample'; + sw.style.display = 'block'; + sw.style.position = 'absolute'; + sw.style.left = ( + pickerDims.palette.cols <= 1 ? 0 : + Math.round(10 * (c * ((pickerDims.contentW - pickerDims.palette.cellW) / (pickerDims.palette.cols - 1)))) / 10 + ) + 'px'; + sw.style.top = (r * (pickerDims.palette.cellH + THIS.paletteSpacing)) + 'px'; + sw.style.border = THIS.controlBorderWidth + 'px solid'; + sw.style.borderColor = THIS.controlBorderColor; + sw.style.cursor = 'pointer'; + if (sampleColor.rgba[3] !== null && sampleColor.rgba[3] < 1.0) { // only create chessboard background if the sample has transparency + sw.style.backgroundImage = 'url(\'' + chessboard.canvas.toDataURL() + '\')'; + sw.style.backgroundRepeat = 'repeat'; + sw.style.backgroundPosition = 'center center'; + } + jsc.setData(sw, { + instance: THIS, + control: 'palette-sample', + color: sampleColor, + }) + sw.addEventListener('click', jsc.onPaletteSampleClick, false); + sw.appendChild(sc); + p.pal.appendChild(sw); + } + } + + + // the Close button + function setBtnBorder () { + var insetColors = THIS.controlBorderColor.split(/\s+/); + var outsetColor = insetColors.length < 2 ? insetColors[0] : insetColors[1] + ' ' + insetColors[0] + ' ' + insetColors[0] + ' ' + insetColors[1]; + p.btn.style.borderColor = outsetColor; + } + var btnPadding = 15; // px + p.btn.className = 'jscolor-btn-close'; + p.btn.style.display = THIS.closeButton ? 'block' : 'none'; + p.btn.style.position = 'absolute'; + p.btn.style.left = THIS.padding + 'px'; + p.btn.style.bottom = THIS.padding + 'px'; + p.btn.style.padding = '0 ' + btnPadding + 'px'; + p.btn.style.maxWidth = (pickerDims.contentW - 2 * THIS.controlBorderWidth - 2 * btnPadding) + 'px'; + p.btn.style.overflow = 'hidden'; + p.btn.style.height = THIS.buttonHeight + 'px'; + p.btn.style.whiteSpace = 'nowrap'; + p.btn.style.border = THIS.controlBorderWidth + 'px solid'; + setBtnBorder(); + p.btn.style.color = THIS.buttonColor; + p.btn.style.font = '12px sans-serif'; + p.btn.style.textAlign = 'center'; + p.btn.style.cursor = 'pointer'; + p.btn.onmousedown = function () { + THIS.hide(); + }; + p.btnT.style.lineHeight = THIS.buttonHeight + 'px'; + p.btnT.innerHTML = ''; + p.btnT.appendChild(window.document.createTextNode(THIS.closeText)); + + // reposition the pointers + redrawPad(); + redrawSld(); + redrawASld(); + + // If we are changing the owner without first closing the picker, + // make sure to first deal with the old owner + if (jsc.picker.owner && jsc.picker.owner !== THIS) { + jsc.removeClass(jsc.picker.owner.targetElement, jsc.pub.activeClassName); + } + + // Set a new picker owner + jsc.picker.owner = THIS; + + // The redrawPosition() method needs picker.owner to be set, that's why we call it here, + // after setting the owner + if (THIS.container === window.document.body) { + jsc.redrawPosition(); + } else { + jsc._drawPosition(THIS, 0, 0, 'relative', false); + } + + if (p.wrap.parentNode !== THIS.container) { + THIS.container.appendChild(p.wrap); + } + + jsc.addClass(THIS.targetElement, jsc.pub.activeClassName); + } + + + function redrawPad () { + // redraw the pad pointer + var yChannel = jsc.getPadYChannel(THIS); + var x = Math.round((THIS.channels.h / 360) * (THIS.width - 1)); + var y = Math.round((1 - THIS.channels[yChannel] / 100) * (THIS.height - 1)); + var crossOuterSize = (2 * THIS.pointerBorderWidth + THIS.pointerThickness + 2 * THIS.crossSize); + var ofs = -Math.floor(crossOuterSize / 2); + jsc.picker.cross.style.left = (x + ofs) + 'px'; + jsc.picker.cross.style.top = (y + ofs) + 'px'; + + // redraw the slider + switch (jsc.getSliderChannel(THIS)) { + case 's': + var rgb1 = jsc.HSV_RGB(THIS.channels.h, 100, THIS.channels.v); + var rgb2 = jsc.HSV_RGB(THIS.channels.h, 0, THIS.channels.v); + var color1 = 'rgb(' + + Math.round(rgb1[0]) + ',' + + Math.round(rgb1[1]) + ',' + + Math.round(rgb1[2]) + ')'; + var color2 = 'rgb(' + + Math.round(rgb2[0]) + ',' + + Math.round(rgb2[1]) + ',' + + Math.round(rgb2[2]) + ')'; + jsc.picker.sldGrad.draw(THIS.sliderSize, THIS.height, color1, color2); + break; + case 'v': + var rgb = jsc.HSV_RGB(THIS.channels.h, THIS.channels.s, 100); + var color1 = 'rgb(' + + Math.round(rgb[0]) + ',' + + Math.round(rgb[1]) + ',' + + Math.round(rgb[2]) + ')'; + var color2 = '#000'; + jsc.picker.sldGrad.draw(THIS.sliderSize, THIS.height, color1, color2); + break; + } + + // redraw the alpha slider + jsc.picker.asldGrad.draw(THIS.sliderSize, THIS.height, THIS.toHEXString()); + } + + + function redrawSld () { + var sldChannel = jsc.getSliderChannel(THIS); + if (sldChannel) { + // redraw the slider pointer + var y = Math.round((1 - THIS.channels[sldChannel] / 100) * (THIS.height - 1)); + jsc.picker.sldPtrOB.style.top = (y - (2 * THIS.pointerBorderWidth + THIS.pointerThickness) - Math.floor(jsc.pub.sliderInnerSpace / 2)) + 'px'; + } + + // redraw the alpha slider + jsc.picker.asldGrad.draw(THIS.sliderSize, THIS.height, THIS.toHEXString()); + } + + + function redrawASld () { + var y = Math.round((1 - THIS.channels.a) * (THIS.height - 1)); + jsc.picker.asldPtrOB.style.top = (y - (2 * THIS.pointerBorderWidth + THIS.pointerThickness) - Math.floor(jsc.pub.sliderInnerSpace / 2)) + 'px'; + } + + + function isPickerOwner () { + return jsc.picker && jsc.picker.owner === THIS; + } + + + function onValueKeyDown (ev) { + if (jsc.eventKey(ev) === 'Enter') { + if (THIS.valueElement) { + THIS.processValueInput(THIS.valueElement.value); + } + THIS.tryHide(); + } + } + + + function onAlphaKeyDown (ev) { + if (jsc.eventKey(ev) === 'Enter') { + if (THIS.alphaElement) { + THIS.processAlphaInput(THIS.alphaElement.value); + } + THIS.tryHide(); + } + } + + + function onValueChange (ev) { + if (jsc.getData(ev, 'internal')) { + return; // skip if the event was internally triggered by jscolor + } + + var oldVal = THIS.valueElement.value; + + THIS.processValueInput(THIS.valueElement.value); // this might change the value + + jsc.triggerCallback(THIS, 'onChange'); + + if (THIS.valueElement.value !== oldVal) { + // value was additionally changed -> let's trigger the change event again, even though it was natively dispatched + jsc.triggerInputEvent(THIS.valueElement, 'change', true, true); + } + } + + + function onAlphaChange (ev) { + if (jsc.getData(ev, 'internal')) { + return; // skip if the event was internally triggered by jscolor + } + + var oldVal = THIS.alphaElement.value; + + THIS.processAlphaInput(THIS.alphaElement.value); // this might change the value + + jsc.triggerCallback(THIS, 'onChange'); + + // triggering valueElement's onChange (because changing alpha changes the entire color, e.g. with rgba format) + jsc.triggerInputEvent(THIS.valueElement, 'change', true, true); + + if (THIS.alphaElement.value !== oldVal) { + // value was additionally changed -> let's trigger the change event again, even though it was natively dispatched + jsc.triggerInputEvent(THIS.alphaElement, 'change', true, true); + } + } + + + function onValueInput (ev) { + if (jsc.getData(ev, 'internal')) { + return; // skip if the event was internally triggered by jscolor + } + + if (THIS.valueElement) { + THIS.fromString(THIS.valueElement.value, jsc.flags.leaveValue); + } + + jsc.triggerCallback(THIS, 'onInput'); + + // triggering valueElement's onInput + // (not needed, it was dispatched normally by the browser) + } + + + function onAlphaInput (ev) { + if (jsc.getData(ev, 'internal')) { + return; // skip if the event was internally triggered by jscolor + } + + if (THIS.alphaElement) { + THIS.fromHSVA(null, null, null, parseFloat(THIS.alphaElement.value), jsc.flags.leaveAlpha); + } + + jsc.triggerCallback(THIS, 'onInput'); + + // triggering valueElement's onInput (because changing alpha changes the entire color, e.g. with rgba format) + jsc.triggerInputEvent(THIS.valueElement, 'input', true, true); + } + + + // let's process the DEPRECATED 'options' property (this will be later removed) + if (jsc.pub.options) { + // let's set custom default options, if specified + for (var opt in jsc.pub.options) { + if (jsc.pub.options.hasOwnProperty(opt)) { + try { + setOption(opt, jsc.pub.options[opt]); + } catch (e) { + console.warn(e); + } + } + } + } + + + // let's apply configuration presets + // + var presetsArr = []; + + if (opts.preset) { + if (typeof opts.preset === 'string') { + presetsArr = opts.preset.split(/\s+/); + } else if (Array.isArray(opts.preset)) { + presetsArr = opts.preset.slice(); // slice() to clone + } else { + console.warn('Unrecognized preset value'); + } + } + + // always use the 'default' preset. If it's not listed, append it to the end. + if (presetsArr.indexOf('default') === -1) { + presetsArr.push('default'); + } + + // let's apply the presets in reverse order, so that should there be any overlapping options, + // the formerly listed preset will override the latter + for (var i = presetsArr.length - 1; i >= 0; i -= 1) { + var pres = presetsArr[i]; + if (!pres) { + continue; // preset is empty string + } + if (!jsc.pub.presets.hasOwnProperty(pres)) { + console.warn('Unknown preset: %s', pres); + continue; + } + for (var opt in jsc.pub.presets[pres]) { + if (jsc.pub.presets[pres].hasOwnProperty(opt)) { + try { + setOption(opt, jsc.pub.presets[pres][opt]); + } catch (e) { + console.warn(e); + } + } + } + } + + + // let's set specific options for this color picker + var nonProperties = [ + // these options won't be set as instance properties + 'preset', + ]; + for (var opt in opts) { + if (opts.hasOwnProperty(opt)) { + if (nonProperties.indexOf(opt) === -1) { + try { + setOption(opt, opts[opt]); + } catch (e) { + console.warn(e); + } + } + } + } + + + // + // Install the color picker on chosen element(s) + // + + + // Determine picker's container element + if (this.container === undefined) { + this.container = window.document.body; // default container is BODY element + + } else { // explicitly set to custom element + this.container = jsc.node(this.container); + } + + if (!this.container) { + throw new Error('Cannot instantiate color picker without a container element'); + } + + + // Fetch the target element + this.targetElement = jsc.node(targetElement); + + if (!this.targetElement) { + // temporarily customized error message to help with migrating from versions prior to 2.2 + if (typeof targetElement === 'string' && /^[a-zA-Z][\w:.-]*$/.test(targetElement)) { + // targetElement looks like valid ID + var possiblyId = targetElement; + throw new Error('If \'' + possiblyId + '\' is supposed to be an ID, please use \'#' + possiblyId + '\' or any valid CSS selector.'); + } + + throw new Error('Cannot instantiate color picker without a target element'); + } + + if (this.targetElement.jscolor && this.targetElement.jscolor instanceof jsc.pub) { + throw new Error('Color picker already installed on this element'); + } + + + // link this instance with the target element + this.targetElement.jscolor = this; + jsc.addClass(this.targetElement, jsc.pub.className); + + // register this instance + jsc.instances.push(this); + + + // if target is BUTTON + if (jsc.isButton(this.targetElement)) { + + if (this.targetElement.type.toLowerCase() !== 'button') { + // on buttons, always force type to be 'button', e.g. in situations the target
");return n},a.prototype.createSizeMenu=function(){var e=this,i=this.state,n=t(''),o=e.options.pageSizeOptions;"string"==typeof o&&(o=o.split(","));for(var a=0;a'+r+"").toggleClass("active",r===i.recPerPage);n.append(s)}return t('
').addClass(e.options.menuDirection).append(n)},a.prototype.createElement=function(e,i,n){var o=this,a=o.createLinkItem.bind(o),r=o.lang;switch(e){case"prev":return a(n.prev,r.prev);case"prev_icon":return a(n.prev,'');case"next":return a(n.next,r.next);case"next_icon":return a(n.next,'');case"first":return a(1,r.first);case"first_icon":return a(1,'');case"last":return a(n.totalPage,r.last);case"last_icon":return a(n.totalPage,'');case"space":case"|":return t('
  • ');case"nav":case"pages":return void o.createNavItems();case"total_text":return t(('
    '+r.totalCount+"
    ").format(n));case"page_text":return t(('
    '+r.pageOf+"
    ").format(n));case"total_page_text":return t(('
    '+r.totalPage+"
    ").format(n));case"page_of_total_text":return t(('
    '+r.pageOfTotal+"
    ").format(n));case"page_size_text":return t(('
    '+r.pageSize+"
    ").format(n));case"items_range_text":return t(('
    '+r.itemsRange+"
    ").format(n));case"goto":return o.createGoto();case"size_menu":return o.createSizeMenu();default:return t("
  • ").html(e.format(n))}},a.prototype.createLink=function(i,n){i===e&&(i=this.state.page),n===e&&(n=this.state);var o=this.options.linkCreator;return"string"==typeof o?o.format(t.extend({},n,{page:i})):"function"==typeof o?o(i,n):"#page="+i},a.prototype.render=function(e){var i=this,n=i.state,o=i.options.elementCreator||i.createElement,a=t.isPlainObject(o);e=e||i.elements||i.options.elements,"string"==typeof e&&(e=e.split(",")),i.elements=e,i.$.empty();for(var r=0;r").append(d)),i.$.append(d))}var c=null;return i.$.children("li").each(function(){var e=t(this),i=!!e.children(".pager-item").length;c?c.toggleClass("pager-item-right",!i):i&&e.addClass("pager-item-left"),c=i?e:null}),c&&c.addClass("pager-item-right"),i.$.callComEvent(i,"onRender",[n]),i},a.DEFAULTS=t.extend({elements:["first_icon","prev_icon","pages","next_icon","last_icon","page_of_total_text","items_range_text","total_text"],prevIcon:"icon-double-angle-left",nextIcon:"icon-double-angle-right",firstIcon:"icon-step-backward",lastIcon:"icon-step-forward",navEllipsisItem:'',maxNavCount:10,menuDirection:"dropdown",pageSizeOptions:[10,20,30,50,100]},n),t.fn.pager=function(e){return this.each(function(){var n=t(this),o=n.data(i),r="object"==typeof e&&e;o||n.data(i,o=new a(this,r)),"string"==typeof e&&o[e]()})},a.NAME=i,a.LANG=o,t.fn.pager.Constructor=a,t(function(){t('[data-ride="pager"]').pager()})}(jQuery,void 0),+function(t){"use strict";var e="zui.tab",i=function(e){this.element=t(e)};i.prototype.show=function(){var i=this.element,n=i.closest("ul:not(.dropdown-menu)"),o=i.attr("data-target")||i.attr("data-tab");if(o||(o=i.attr("href"),o=o&&o.replace(/.*(?=#[^\s]*$)/,"")),!i.parent("li").hasClass("active")){var a=n.find(".active:last a")[0],r=t.Event("show."+e,{relatedTarget:a});if(i.trigger(r),!r.isDefaultPrevented()){var s=t(o);this.activate(i.parent("li"),n),this.activate(s,s.parent(),function(){i.trigger({type:"shown."+e,relatedTarget:a})})}}},i.prototype.activate=function(e,i,n){function o(){a.removeClass("active").find("> .dropdown-menu > .active").removeClass("active"),e.addClass("active"),r?(e[0].offsetWidth,e.addClass("in")):e.removeClass("fade"),e.parent(".dropdown-menu")&&e.closest("li.dropdown").addClass("active"),n&&n()}var a=i.find("> .active"),r=n&&t.support.transition&&a.hasClass("fade");r?a.one(t.support.transition.end,o).emulateTransitionEnd(150):o(),a.removeClass("in")};var n=t.fn.tab;t.fn.tab=function(n){return this.each(function(){var o=t(this),a=o.data(e);a||o.data(e,a=new i(this)),"string"==typeof n&&a[n]()})},t.fn.tab.Constructor=i,t.fn.tab.noConflict=function(){return t.fn.tab=n,this},t(document).on("click.zui.tab.data-api",'[data-toggle="tab"], [data-tab]',function(e){e.preventDefault(),t(this).tab("show")})}(window.jQuery),+function(t){"use strict";function e(){var t=document.createElement("bootstrap"),e={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(var i in e)if(void 0!==t.style[i])return{end:e[i]};return!1}t.fn.emulateTransitionEnd=function(e){var i=!1,n=this;t(this).one("bsTransitionEnd",function(){i=!0});var o=function(){i||t(n).trigger(t.support.transition.end)};return setTimeout(o,e),this},t(function(){t.support.transition=e(),t.support.transition&&(t.event.special.bsTransitionEnd={bindType:t.support.transition.end,delegateType:t.support.transition.end,handle:function(e){if(t(e.target).is(this))return e.handleObj.handler.apply(this,arguments)}})})}(jQuery),+function(t){"use strict";var e="zui.collapse",i=function(e,n){this.$element=t(e),this.options=t.extend({},i.DEFAULTS,n),this.transitioning=null,this.options.parent&&(this.$parent=t(this.options.parent)),this.options.toggle&&this.toggle()};i.DEFAULTS={toggle:!0},i.prototype.dimension=function(){var t=this.$element.hasClass("width");return t?"width":"height"},i.prototype.show=function(){if(!this.transitioning&&!this.$element.hasClass("in")){var i=t.Event("show."+e);if(this.$element.trigger(i),!i.isDefaultPrevented()){var n=this.$parent&&this.$parent.find(".in");if(n&&n.length){var o=n.data(e);if(o&&o.transitioning)return;n.collapse("hide"),o||n.data(e,null)}var a=this.dimension();this.$element.removeClass("collapse").addClass("collapsing")[a](0),this.transitioning=1;var r=function(){this.$element.removeClass("collapsing").addClass("in")[a]("auto"),this.transitioning=0,this.$element.trigger("shown."+e)};if(!t.support.transition)return r.call(this);var s=t.camelCase(["scroll",a].join("-"));this.$element.one(t.support.transition.end,r.bind(this)).emulateTransitionEnd(350)[a](this.$element[0][s])}}},i.prototype.hide=function(){if(!this.transitioning&&this.$element.hasClass("in")){var i=t.Event("hide."+e);if(this.$element.trigger(i),!i.isDefaultPrevented()){var n=this.dimension();this.$element[n](this.$element[n]())[0].offsetHeight,this.$element.addClass("collapsing").removeClass("collapse").removeClass("in"),this.transitioning=1;var o=function(){this.transitioning=0,this.$element.trigger("hidden."+e).removeClass("collapsing").addClass("collapse")};return t.support.transition?void this.$element[n](0).one(t.support.transition.end,o.bind(this)).emulateTransitionEnd(350):o.call(this)}}},i.prototype.toggle=function(){this[this.$element.hasClass("in")?"hide":"show"]()};var n=t.fn.collapse;t.fn.collapse=function(n){return this.each(function(){var o=t(this),a=o.data(e),r=t.extend({},i.DEFAULTS,o.data(),"object"==typeof n&&n);a||o.data(e,a=new i(this,r)),"string"==typeof n&&a[n]()})},t.fn.collapse.Constructor=i,t.fn.collapse.noConflict=function(){return t.fn.collapse=n,this},t(document).on("click."+e+".data-api","[data-toggle=collapse]",function(i){var n,o=t(this),a=o.attr("data-target")||i.preventDefault()||(n=o.attr("href"))&&n.replace(/.*(?=#[^\s]+$)/,""),r=t(a),s=r.data(e),l=s?"toggle":o.data(),d=o.attr("data-parent"),c=d&&t(d);s&&s.transitioning||(c&&c.find('[data-toggle=collapse][data-parent="'+d+'"]').not(o).addClass("collapsed"),o[r.hasClass("in")?"addClass":"removeClass"]("collapsed")),r.collapse(l)})}(window.jQuery),function(t,e){"use strict";var i=1200,n=992,o=768,a=e(t),r=function(){var t=a.width();e("html").toggleClass("screen-desktop",t>=n&&t=i).toggleClass("screen-tablet",t>=o&&t=n)},s="",l=navigator.userAgent;l.match(/(iPad|iPhone|iPod)/i)?s+=" os-ios":l.match(/android/i)?s+=" os-android":l.match(/Win/i)?s+=" os-windows":l.match(/Mac/i)?s+=" os-mac":l.match(/Linux/i)?s+=" os-linux":l.match(/X11/i)&&(s+=" os-unix"),"ontouchstart"in document.documentElement&&(s+=" is-touchable"),e("html").addClass(s),a.resize(r),r()}(window,jQuery),function(t){"use strict";var e={zh_cn:'您的浏览器版本过低,无法体验所有功能,建议升级或者更换浏览器。 了解更多...',zh_tw:'您的瀏覽器版本過低,無法體驗所有功能,建議升級或者更换瀏覽器。了解更多...',en:'Your browser is too old, it has been unable to experience the colorful internet. We strongly recommend that you upgrade a better one. Learn more...'},i=function(){for(var t=!1,e=11;e>5;e--)if(this.isIE(e)){t=e;break}this.ie=t,this.cssHelper()};i.prototype.cssHelper=function(){var e=this.ie,i=t("html");i.toggleClass("ie",e).removeClass("ie-6 ie-7 ie-8 ie-9 ie-10"),e&&i.addClass("ie-"+e).toggleClass("gt-ie-7 gte-ie-8 support-ie",e>=8).toggleClass("lte-ie-7 lt-ie-8 outdated-ie",e<8).toggleClass("gt-ie-8 gte-ie-9",e>=9).toggleClass("lte-ie-8 lt-ie-9",e<9).toggleClass("gt-ie-9 gte-ie-10",e>=10).toggleClass("lte-ie-9 lt-ie-10",e<10).toggleClass("gt-ie-10 gte-ie-11",e>=11).toggleClass("lte-ie-10 lt-ie-11",e<11)},i.prototype.tip=function(i){var n=t("#browseHappyTip");n.length||(n=t('
    '),n.prependTo("body")),i||(i=t.zui.getLangData("zui.browser",t.zui.clientLang(),e),"object"==typeof i&&(i=i.tip)),n.find(".content").html(i)},i.prototype.isIE=function(t){if(11===t)return this.isIE11();if(10===t)return this.isIE10();if(!t&&(this.isIE11()||this.isIE10()))return!0;var e=document.createElement("b");return e.innerHTML="",1===e.getElementsByTagName("i").length},i.prototype.isIE10=function(){return navigator.appVersion.indexOf("MSIE 10")!==-1},i.prototype.isIE11=function(){var t=navigator.userAgent;return t.indexOf("Trident")!==-1&&t.indexOf("rv:11")!==-1},t.zui({browser:new i}),t(function(){t("body").hasClass("disabled-browser-tip")||t.zui.browser.ie&&t.zui.browser.ie<8&&t.zui.browser.tip()})}(jQuery),function(t){"use strict";var e=864e5,i=function(t){return t instanceof Date||("number"==typeof t&&t<1e10&&(t*=1e3),t=new Date(t)),t},n=function(t){return i(t).getTime()},o=function(t,e){t=i(t),void 0===e&&(e="yyyy-MM-dd hh:mm:ss");var n={"M+":t.getMonth()+1,"d+":t.getDate(),"h+":t.getHours(),"m+":t.getMinutes(),"s+":t.getSeconds(),"q+":Math.floor((t.getMonth()+3)/3),"S+":t.getMilliseconds()};/(y+)/i.test(e)&&(e=e.replace(RegExp.$1,(t.getFullYear()+"").substr(4-RegExp.$1.length)));for(var o in n)new RegExp("("+o+")").test(e)&&(e=e.replace(RegExp.$1,1==RegExp.$1.length?n[o]:("00"+n[o]).substr((""+n[o]).length)));return e},a=function(t,e){return t.setTime(t.getTime()+e),t},r=function(t,i){return a(t,i*e)},s=function(t){return new Date(i(t).getTime())},l=function(t){return t%4===0&&t%100!==0||t%400===0},d=function(t,e){return[31,l(t)?29:28,31,30,31,30,31,31,30,31,30,31][e]},c=function(t){return d(t.getFullYear(),t.getMonth())},p=function(t){return t.setHours(0),t.setMinutes(0),t.setSeconds(0),t.setMilliseconds(0),t},u=function(t,e){var i=t.getDate();return t.setDate(1),t.setMonth(t.getMonth()+e),t.setDate(Math.min(i,c(t))),t},f=function(t,e){e=e||1;for(var i=new Date(t.getTime());i.getDay()!=e;)i=r(i,-1);return p(i)},h=function(t,e){return t.toDateString()===e.toDateString()},g=function(t,e){var i=f(t),n=r(s(i),7);return e>=i&&e1){var i;if(2==arguments.length&&"object"==typeof e)for(var n in e)void 0!==e[n]&&(i=new RegExp("({"+n+"})","g"),t=t.replace(i,e[n]));else for(var o=1;othis.length)&&(e=this.length),this.substring(e-t.length,e)===t}),String.prototype.startsWith||Object.defineProperty(String.prototype,"startsWith",{value:function(t,e){return e=!e||e<0?0:+e,this.substring(e,e+t.length)===t}}),String.prototype.includes||(String.prototype.includes=function(){return String.prototype.indexOf.apply(this,arguments)!==-1}))}(),/*! +!function(t,e,i){"use strict";if("undefined"==typeof t)throw new Error("ZUI requires jQuery");t.zui||(t.zui=function(e){t.isPlainObject(e)&&t.extend(t.zui,e)});var n={all:-1,left:0,middle:1,right:2},o=0;t.zui({uuid:function(t){var e=1e5*(Date.now()-1580890015292)+10*Math.floor(1e4*Math.random())+o++%10;return t?e:e.toString(36)},callEvent:function(t,e,n){if("function"==typeof t){n!==i&&(t=t.bind(n));var o=t(e);return e&&(e.result=o),!(o!==i&&!o)}return 1},strCode:function(t){var e=0;if("string"!=typeof t&&(t=String(t)),t&&t.length)for(var i=0;i=e.innerWidth)return 0;if(!t.zui._scrollbarWidth){var i=document.createElement("div");i.className="scrollbar-measure",document.body.appendChild(i),t.zui._scrollbarWidth=i.offsetWidth-i.clientWidth,document.body.removeChild(i)}return t.zui._scrollbarWidth},fixBodyScrollbar:function(){if(t.zui.checkBodyScrollbar()){var e=t("body"),i=parseInt(e.css("padding-right")||0,10);return t.zui._scrollbarWidth&&e.css({paddingRight:i+t.zui._scrollbarWidth,overflowY:"hidden"}),!0}},resetBodyScrollbar:function(){t("body").css({paddingRight:"",overflowY:""})}}),t.fn.callEvent=function(e,n,o){var a=t(this),r=e.indexOf(".zui."),s=r<0?e:e.substring(0,r),l=t.Event(s,n);if(o===i&&r>0&&(o=a.data(e.substring(r+1))),o&&o.options){var d=o.options[s];"function"==typeof d&&(l.result=t.zui.callEvent(d,l,o))}return a.trigger(l),l},t.fn.callComEvent=function(t,e,n){n===i||Array.isArray(n)||(n=[n]);var o,a=this;a.trigger(e,n);var r=t.options[e];return r&&(o=r.apply(t,n)),o}}(jQuery,window,void 0),function(t){"use strict";t.fn.fixOlPd=function(e){return e=e||10,this.each(function(){var i=t(this);i.css("paddingLeft",Math.ceil(Math.log10(i.children().length))*e+10)})},t(function(){t(".ol-pd-fix,.article ol").fixOlPd()})}(jQuery),+function(t){"use strict";var e=function(i,n){this.$element=t(i),this.options=t.extend({},e.DEFAULTS,n),this.isLoading=!1};e.DEFAULTS={loadingText:"loading..."},e.prototype.setState=function(t){var e="disabled",i=this.$element,n=i.is("input")?"val":"html",o=i.data();t+="Text",o.resetText||i.data("resetText",i[n]()),i[n](o[t]||this.options[t]),setTimeout(function(){"loadingText"==t?(this.isLoading=!0,i.addClass(e).attr(e,e)):this.isLoading&&(this.isLoading=!1,i.removeClass(e).removeAttr(e))}.bind(this),0)},e.prototype.toggle=function(){var t=!0,e=this.$element.closest('[data-toggle="buttons"]');if(e.length){var i=this.$element.find("input");"radio"==i.prop("type")&&(i.prop("checked")&&this.$element.hasClass("active")?t=!1:e.find(".active").removeClass("active")),t&&i.prop("checked",!this.$element.hasClass("active")).trigger("change")}t&&this.$element.toggleClass("active")};var i=t.fn.button;t.fn.button=function(i){return this.each(function(){var n=t(this),o=n.data("zui.button"),a="object"==typeof i&&i;o||n.data("zui.button",o=new e(this,a)),"toggle"==i?o.toggle():i&&o.setState(i)})},t.fn.button.Constructor=e,t.fn.button.noConflict=function(){return t.fn.button=i,this},t(document).on("click.zui.button.data-api","[data-toggle^=button]",function(e){var i=t(e.target);i.hasClass("btn")||(i=i.closest(".btn")),i.button("toggle"),e.preventDefault()})}(jQuery),+function(t){"use strict";var e='[data-dismiss="alert"]',i="zui.alert",n=function(i){t(i).on("click",e,this.close)};n.prototype.close=function(e){function n(){r.trigger("closed."+i).remove()}var o=t(this),a=o.attr("data-target");a||(a=o.attr("href"),a=a&&a.replace(/.*(?=#[^\s]*$)/,""));var r=t(a);e&&e.preventDefault(),r.length||(r=o.hasClass("alert")?o:o.parent()),r.trigger(e=t.Event("close."+i)),e.isDefaultPrevented()||(r.removeClass("in"),t.support.transition&&r.hasClass("fade")?r.one(t.support.transition.end,n).emulateTransitionEnd(150):n())};var o=t.fn.alert;t.fn.alert=function(e){return this.each(function(){var o=t(this),a=o.data(i);a||o.data(i,a=new n(this)),"string"==typeof e&&a[e].call(o)})},t.fn.alert.Constructor=n,t.fn.alert.noConflict=function(){return t.fn.alert=o,this},t(document).on("click."+i+".data-api",e,n.prototype.close)}(window.jQuery),function(t,e){"use strict";var i="zui.pager",n={page:1,recTotal:0,recPerPage:10},o={zh_cn:{pageOfText:"第 {0} 页",prev:"上一页",next:"下一页",first:"第一页",last:"最后一页","goto":"跳转",pageOf:"第 {page} 页",totalPage:"共 {totalPage} 页",totalCount:"共 {recTotal} 项",pageSize:"每页 {recPerPage} 项",itemsRange:"第 {start} ~ {end} 项",pageOfTotal:"第 {page}/{totalPage} 页"},zh_tw:{pageOfText:"第 {0} 頁",prev:"上一頁",next:"下一頁",first:"第一頁",last:"最後一頁","goto":"跳轉",pageOf:"第 {page} 頁",totalPage:"共 {totalPage} 頁",totalCount:"共 {recTotal} 項",pageSize:"每頁 {recPerPage} 項",itemsRange:"第 {start} ~ {end} 項",pageOfTotal:"第 {page}/{totalPage} 頁"},en:{pageOfText:"Page {0}",prev:"Prev",next:"Next",first:"First",last:"Last","goto":"Goto",pageOf:"Page {page}",totalPage:"{totalPage} pages",totalCount:"Total: {recTotal} items",pageSize:"{recPerPage} per page",itemsRange:"From {start} to {end}",pageOfTotal:"Page {page} of {totalPage}"}},a=function(e,n){var r=this;r.name=i,r.$=t(e),n=r.options=t.extend({},a.DEFAULTS,this.$.data(),n),r.langName=n.lang||t.zui.clientLang(),r.lang=t.zui.getLangData(i,r.langName,o),r.state={},r.set(n.page,n.recTotal,n.recPerPage,!0),r.$.on("click",".pager-goto-btn",function(){var e=t(this).closest(".pager-goto"),i=parseInt(e.find(".pager-goto-input").val());NaN!==i&&r.set(i)}).on("click",".pager-item",function(){var e=t(this).data("page");"number"==typeof e&&e>0&&r.set(e)}).on("click",".pager-size-menu [data-size]",function(){var e=t(this).data("size");"number"==typeof e&&e>0&&r.set(-1,-1,e)})};a.prototype.set=function(e,i,o,a){var r=this;"object"==typeof e&&null!==e&&(o=e.recPerPage,i=e.recTotal,e=e.page);var s=r.state;s||(s=t.extend({},n));var l=t.extend({},s);return"number"==typeof o&&o>0&&(s.recPerPage=o),"number"==typeof i&&i>=0&&(s.recTotal=i),"number"==typeof e&&e>=0&&(s.page=e),s.totalPage=s.recTotal&&s.recPerPage?Math.ceil(s.recTotal/s.recPerPage):1,s.page=Math.max(0,Math.min(s.page,s.totalPage)),s.pageRecCount=s.recTotal,s.page&&s.recTotal&&(s.page1&&(s.pageRecCount=s.recTotal-s.recPerPage*(s.page-1))),s.skip=s.page>1?(s.page-1)*s.recPerPage:0,s.start=s.skip+1,s.end=s.skip+s.pageRecCount,s.prev=s.page>1?s.page-1:0,s.next=s.page').attr("href",i?a.createLink(i,a.state):"###").html(n);return o||(r=t("
  • ").append(r).toggleClass("active",i===a.state.page).toggleClass("disabled",!i||i===a.state.page)),r},a.prototype.createNavItems=function(t){var i=this,n=i.$,o=i.state,a=o.totalPage,r=o.page,s=function(t,o){if(t===!1)return void n.append(i.createLinkItem(0,o||i.options.navEllipsisItem));o===e&&(o=t);for(var a=t;a<=o;++a)n.append(i.createLinkItem(a))};t===e&&(t=i.options.maxNavCount||10),s(1),a>1&&(a<=t?s(2,a):ra-t+2?(s(!1),s(a-t+2,a)):(s(!1),s(r-Math.ceil((t-4)/2),r+Math.floor((t-4)/2)),s(!1),s(a)))},a.prototype.createGoto=function(){var e=this,i=this.state,n=t('
    ");return n},a.prototype.createSizeMenu=function(){var e=this,i=this.state,n=t(''),o=e.options.pageSizeOptions;"string"==typeof o&&(o=o.split(","));for(var a=0;a'+r+"
  • ").toggleClass("active",r===i.recPerPage);n.append(s)}return t('
    ').addClass(e.options.menuDirection).append(n)},a.prototype.createElement=function(e,i,n){var o=this,a=o.createLinkItem.bind(o),r=o.lang;switch(e){case"prev":return a(n.prev,r.prev);case"prev_icon":return a(n.prev,'');case"next":return a(n.next,r.next);case"next_icon":return a(n.next,'');case"first":return a(1,r.first);case"first_icon":return a(1,'');case"last":return a(n.totalPage,r.last);case"last_icon":return a(n.totalPage,'');case"space":case"|":return t('
  • ');case"nav":case"pages":return void o.createNavItems();case"total_text":return t(('
    '+r.totalCount+"
    ").format(n));case"page_text":return t(('
    '+r.pageOf+"
    ").format(n));case"total_page_text":return t(('
    '+r.totalPage+"
    ").format(n));case"page_of_total_text":return t(('
    '+r.pageOfTotal+"
    ").format(n));case"page_size_text":return t(('
    '+r.pageSize+"
    ").format(n));case"items_range_text":return t(('
    '+r.itemsRange+"
    ").format(n));case"goto":return o.createGoto();case"size_menu":return o.createSizeMenu();default:return t("
  • ").html(e.format(n))}},a.prototype.createLink=function(i,n){i===e&&(i=this.state.page),n===e&&(n=this.state);var o=this.options.linkCreator;return"string"==typeof o?o.format(t.extend({},n,{page:i})):"function"==typeof o?o(i,n):"#page="+i},a.prototype.render=function(e){var i=this,n=i.state,o=i.options.elementCreator||i.createElement,a=t.isPlainObject(o);e=e||i.elements||i.options.elements,"string"==typeof e&&(e=e.split(",")),i.elements=e,i.$.empty();for(var r=0;r").append(d)),i.$.append(d))}var c=null;return i.$.children("li").each(function(){var e=t(this),i=!!e.children(".pager-item").length;c?c.toggleClass("pager-item-right",!i):i&&e.addClass("pager-item-left"),c=i?e:null}),c&&c.addClass("pager-item-right"),i.$.callComEvent(i,"onRender",[n]),i},a.DEFAULTS=t.extend({elements:["first_icon","prev_icon","pages","next_icon","last_icon","page_of_total_text","items_range_text","total_text"],prevIcon:"icon-double-angle-left",nextIcon:"icon-double-angle-right",firstIcon:"icon-step-backward",lastIcon:"icon-step-forward",navEllipsisItem:'',maxNavCount:10,menuDirection:"dropdown",pageSizeOptions:[10,20,30,50,100]},n),t.fn.pager=function(e){return this.each(function(){var n=t(this),o=n.data(i),r="object"==typeof e&&e;o||n.data(i,o=new a(this,r)),"string"==typeof e&&o[e]()})},a.NAME=i,a.LANG=o,t.fn.pager.Constructor=a,t(function(){t('[data-ride="pager"]').pager()})}(jQuery,void 0),+function(t){"use strict";var e="zui.tab",i=function(e){this.element=t(e)};i.prototype.show=function(){var i=this.element,n=i.closest("ul:not(.dropdown-menu)"),o=i.attr("data-target")||i.attr("data-tab");if(o||(o=i.attr("href"),o=o&&o.replace(/.*(?=#[^\s]*$)/,"")),!i.parent("li").hasClass("active")){var a=n.find(".active:last a")[0],r=t.Event("show."+e,{relatedTarget:a});if(i.trigger(r),!r.isDefaultPrevented()){var s=t(o);this.activate(i.parent("li"),n),this.activate(s,s.parent(),function(){i.trigger({type:"shown."+e,relatedTarget:a})})}}},i.prototype.activate=function(e,i,n){function o(){a.removeClass("active").find("> .dropdown-menu > .active").removeClass("active"),e.addClass("active"),r?(e[0].offsetWidth,e.addClass("in")):e.removeClass("fade"),e.parent(".dropdown-menu")&&e.closest("li.dropdown").addClass("active"),n&&n()}var a=i.find("> .active"),r=n&&t.support.transition&&a.hasClass("fade");r?a.one(t.support.transition.end,o).emulateTransitionEnd(150):o(),a.removeClass("in")};var n=t.fn.tab;t.fn.tab=function(n){return this.each(function(){var o=t(this),a=o.data(e);a||o.data(e,a=new i(this)),"string"==typeof n&&a[n]()})},t.fn.tab.Constructor=i,t.fn.tab.noConflict=function(){return t.fn.tab=n,this},t(document).on("click.zui.tab.data-api",'[data-toggle="tab"], [data-tab]',function(e){e.preventDefault(),t(this).tab("show")})}(window.jQuery),+function(t){"use strict";function e(){var t=document.createElement("bootstrap"),e={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(var i in e)if(void 0!==t.style[i])return{end:e[i]};return!1}t.fn.emulateTransitionEnd=function(e){var i=!1,n=this;t(this).one("bsTransitionEnd",function(){i=!0});var o=function(){i||t(n).trigger(t.support.transition.end)};return setTimeout(o,e),this},t(function(){t.support.transition=e(),t.support.transition&&(t.event.special.bsTransitionEnd={bindType:t.support.transition.end,delegateType:t.support.transition.end,handle:function(e){if(t(e.target).is(this))return e.handleObj.handler.apply(this,arguments)}})})}(jQuery),+function(t){"use strict";var e="zui.collapse",i=function(e,n){this.$element=t(e),this.options=t.extend({},i.DEFAULTS,n),this.transitioning=null,this.options.parent&&(this.$parent=t(this.options.parent)),this.options.toggle&&this.toggle()};i.DEFAULTS={toggle:!0},i.prototype.dimension=function(){var t=this.$element.hasClass("width");return t?"width":"height"},i.prototype.show=function(){if(!this.transitioning&&!this.$element.hasClass("in")){var i=t.Event("show."+e);if(this.$element.trigger(i),!i.isDefaultPrevented()){var n=this.$parent&&this.$parent.find(".in");if(n&&n.length){var o=n.data(e);if(o&&o.transitioning)return;n.collapse("hide"),o||n.data(e,null)}var a=this.dimension();this.$element.removeClass("collapse").addClass("collapsing")[a](0),this.transitioning=1;var r=function(){this.$element.removeClass("collapsing").addClass("in")[a]("auto"),this.transitioning=0,this.$element.trigger("shown."+e)};if(!t.support.transition)return r.call(this);var s=t.camelCase(["scroll",a].join("-"));this.$element.one(t.support.transition.end,r.bind(this)).emulateTransitionEnd(350)[a](this.$element[0][s])}}},i.prototype.hide=function(){if(!this.transitioning&&this.$element.hasClass("in")){var i=t.Event("hide."+e);if(this.$element.trigger(i),!i.isDefaultPrevented()){var n=this.dimension();this.$element[n](this.$element[n]())[0].offsetHeight,this.$element.addClass("collapsing").removeClass("collapse").removeClass("in"),this.transitioning=1;var o=function(){this.transitioning=0,this.$element.trigger("hidden."+e).removeClass("collapsing").addClass("collapse")};return t.support.transition?void this.$element[n](0).one(t.support.transition.end,o.bind(this)).emulateTransitionEnd(350):o.call(this)}}},i.prototype.toggle=function(){this[this.$element.hasClass("in")?"hide":"show"]()};var n=t.fn.collapse;t.fn.collapse=function(n){return this.each(function(){var o=t(this),a=o.data(e),r=t.extend({},i.DEFAULTS,o.data(),"object"==typeof n&&n);a||o.data(e,a=new i(this,r)),"string"==typeof n&&a[n]()})},t.fn.collapse.Constructor=i,t.fn.collapse.noConflict=function(){return t.fn.collapse=n,this},t(document).on("click."+e+".data-api","[data-toggle=collapse]",function(i){var n,o=t(this),a=o.attr("data-target")||i.preventDefault()||(n=o.attr("href"))&&n.replace(/.*(?=#[^\s]+$)/,""),r=t(a),s=r.data(e),l=s?"toggle":o.data(),d=o.attr("data-parent"),c=d&&t(d);s&&s.transitioning||(c&&c.find('[data-toggle=collapse][data-parent="'+d+'"]').not(o).addClass("collapsed"),o[r.hasClass("in")?"addClass":"removeClass"]("collapsed")),r.collapse(l)})}(window.jQuery),function(t,e){"use strict";var i=1200,n=992,o=768,a=e(t),r=function(){var t=a.width();e("html").toggleClass("screen-desktop",t>=n&&t=i).toggleClass("screen-tablet",t>=o&&t=n)},s="",l=navigator.userAgent;l.match(/(iPad|iPhone|iPod)/i)?s+=" os-ios":l.match(/android/i)?s+=" os-android":l.match(/Win/i)?s+=" os-windows":l.match(/Mac/i)?s+=" os-mac":l.match(/Linux/i)?s+=" os-linux":l.match(/X11/i)&&(s+=" os-unix"),"ontouchstart"in document.documentElement&&(s+=" is-touchable"),e("html").addClass(s),a.resize(r),r()}(window,jQuery),function(t){"use strict";var e={zh_cn:'您的浏览器版本过低,无法体验所有功能,建议升级或者更换浏览器。 了解更多...',zh_tw:'您的瀏覽器版本過低,無法體驗所有功能,建議升級或者更换瀏覽器。了解更多...',en:'Your browser is too old, it has been unable to experience the colorful internet. We strongly recommend that you upgrade a better one. Learn more...'},i=function(){for(var t=!1,e=11;e>5;e--)if(this.isIE(e)){t=e;break}this.ie=t,this.cssHelper()};i.prototype.cssHelper=function(){var e=this.ie,i=t("html");i.toggleClass("ie",e).removeClass("ie-6 ie-7 ie-8 ie-9 ie-10"),e&&i.addClass("ie-"+e).toggleClass("gt-ie-7 gte-ie-8 support-ie",e>=8).toggleClass("lte-ie-7 lt-ie-8 outdated-ie",e<8).toggleClass("gt-ie-8 gte-ie-9",e>=9).toggleClass("lte-ie-8 lt-ie-9",e<9).toggleClass("gt-ie-9 gte-ie-10",e>=10).toggleClass("lte-ie-9 lt-ie-10",e<10).toggleClass("gt-ie-10 gte-ie-11",e>=11).toggleClass("lte-ie-10 lt-ie-11",e<11)},i.prototype.tip=function(i){var n=t("#browseHappyTip");n.length||(n=t('
    '),n.prependTo("body")),i||(i=t.zui.getLangData("zui.browser",t.zui.clientLang(),e),"object"==typeof i&&(i=i.tip)),n.find(".content").html(i)},i.prototype.isIE=function(t){if(11===t)return this.isIE11();if(10===t)return this.isIE10();if(!t&&(this.isIE11()||this.isIE10()))return!0;var e=document.createElement("b");return e.innerHTML="",1===e.getElementsByTagName("i").length},i.prototype.isIE10=function(){return navigator.appVersion.indexOf("MSIE 10")!==-1},i.prototype.isIE11=function(){var t=navigator.userAgent;return t.indexOf("Trident")!==-1&&t.indexOf("rv:11")!==-1},t.zui({browser:new i}),t(function(){t("body").hasClass("disabled-browser-tip")||t.zui.browser.ie&&t.zui.browser.ie<8&&t.zui.browser.tip()})}(jQuery),function(t){"use strict";var e=864e5,i=function(t){return t instanceof Date||("number"==typeof t&&t<1e10&&(t*=1e3),t=new Date(t)),t},n=function(t){return i(t).getTime()},o=function(t,e){t=i(t),void 0===e&&(e="yyyy-MM-dd hh:mm:ss");var n={"M+":t.getMonth()+1,"d+":t.getDate(),"h+":t.getHours(),"m+":t.getMinutes(),"s+":t.getSeconds(),"q+":Math.floor((t.getMonth()+3)/3),"S+":t.getMilliseconds()};/(y+)/i.test(e)&&(e=e.replace(RegExp.$1,(t.getFullYear()+"").substr(4-RegExp.$1.length)));for(var o in n)new RegExp("("+o+")").test(e)&&(e=e.replace(RegExp.$1,1==RegExp.$1.length?n[o]:("00"+n[o]).substr((""+n[o]).length)));return e},a=function(t,e){return t.setTime(t.getTime()+e),t},r=function(t,i){return a(t,i*e)},s=function(t){return new Date(i(t).getTime())},l=function(t){return t%4===0&&t%100!==0||t%400===0},d=function(t,e){return[31,l(t)?29:28,31,30,31,30,31,31,30,31,30,31][e]},c=function(t){return d(t.getFullYear(),t.getMonth())},p=function(t){return t.setHours(0),t.setMinutes(0),t.setSeconds(0),t.setMilliseconds(0),t},u=function(t,e){var i=t.getDate();return t.setDate(1),t.setMonth(t.getMonth()+e),t.setDate(Math.min(i,c(t))),t},f=function(t,e){e=e||1;for(var i=new Date(t.getTime());i.getDay()!=e;)i=r(i,-1);return p(i)},h=function(t,e){return t.toDateString()===e.toDateString()},g=function(t,e){var i=f(t),n=r(s(i),7);return e>=i&&e1){var i;if(2==arguments.length&&"object"==typeof e)for(var n in e)void 0!==e[n]&&(i=new RegExp("({"+n+"})","g"),t=t.replace(i,e[n]));else for(var o=1;othis.length)&&(e=this.length),this.substring(e-t.length,e)===t}),String.prototype.startsWith||Object.defineProperty(String.prototype,"startsWith",{value:function(t,e){return e=!e||e<0?0:+e,this.substring(e,e+t.length)===t}}),String.prototype.includes||(String.prototype.includes=function(){return String.prototype.indexOf.apply(this,arguments)!==-1}))}(),/*! * jQuery resize event - v1.1 * http://benalman.com/projects/jquery-resize-plugin/ * Copyright (c) 2010 "Cowboy" Ben Alman * MIT & GPL http://benalman.com/about/license/ */ function(t,e,i){"$:nomunge";function n(){o=e[s](function(){a.each(function(){var e=t(this),i=e.width(),n=e.height(),o=t.data(this,d);i===o.w&&n===o.h||e.trigger(l,[o.w=i,o.h=n])}),n()},r[c])}var o,a=t([]),r=t.resize=t.extend(t.resize,{}),s="setTimeout",l="resize",d=l+"-special-event",c="delay",p="throttleWindow";r[c]=250,r[p]=!0,t.event.special[l]={setup:function(){if(!r[p]&&this[s])return!1;var e=t(this);a=a.add(e),t.data(this,d,{w:e.width(),h:e.height()}),1===a.length&&n()},teardown:function(){if(!r[p]&&this[s])return!1;var e=t(this);a=a.not(e),e.removeData(d),a.length||clearTimeout(o)},add:function(e){function n(e,n,a){var r=t(this),s=t.data(this,d)||{};s.w=n!==i?n:r.width(),s.h=a!==i?a:r.height(),o.apply(this,arguments)}if(!r[p]&&this[s])return!1;var o;return"function"==typeof e?(o=e,n):(o=e.handler,void(e.handler=n))}}}(jQuery,this),+function(t){"use strict";function e(n,o){var a,r=this.process.bind(this);this.$element=t(t(n).is("body")?window:n),this.$body=t("body"),this.$scrollElement=this.$element.on("scroll."+i+".data-api",r),this.options=t.extend({},e.DEFAULTS,o),this.selector||(this.selector=(this.options.target||(a=t(n).attr("href"))&&a.replace(/.*(?=#[^\s]+$)/,"")||"")+" .nav li > a"),this.offsets=t([]),this.targets=t([]),this.activeTarget=null,this.refresh(),this.process()}var i="zui.scrollspy";e.DEFAULTS={offset:10},e.prototype.refresh=function(){var e=this.$element[0]==window?"offset":"position";this.offsets=t([]),this.targets=t([]);var i=this;this.$body.find(this.selector).map(function(){var n=t(this),o=n.data("target")||n.attr("href"),a=/^#./.test(o)&&t(o);return a&&a.length&&a.is(":visible")&&[[a[e]().top+(!t.isWindow(i.$scrollElement.get(0))&&i.$scrollElement.scrollTop()),o]]||null}).sort(function(t,e){return t[0]-e[0]}).each(function(){i.offsets.push(this[0]),i.targets.push(this[1])})},e.prototype.process=function(){var t,e=this.$scrollElement.scrollTop()+this.options.offset,i=this.$scrollElement[0].scrollHeight||this.$body[0].scrollHeight,n=i-this.$scrollElement.height(),o=this.offsets,a=this.targets,r=this.activeTarget;if(e>=n)return r!=(t=a.last()[0])&&this.activate(t);if(r&&e<=o[0])return r!=(t=a[0])&&this.activate(t);for(t=o.length;t--;)r!=a[t]&&e>=o[t]&&(!o[t+1]||e<=o[t+1])&&this.activate(a[t])},e.prototype.activate=function(e){this.activeTarget=e,t(this.selector).parentsUntil(this.options.target,".active").removeClass("active");var n=this.selector+'[data-target="'+e+'"],'+this.selector+'[href="'+e+'"]',o=t(n).parents("li").addClass("active");o.parent(".dropdown-menu").length&&(o=o.closest("li.dropdown").addClass("active")),o.trigger("activate."+i)};var n=t.fn.scrollspy;t.fn.scrollspy=function(n){return this.each(function(){var o=t(this),a=o.data(i),r="object"==typeof n&&n;a||o.data(i,a=new e(this,r)),"string"==typeof n&&a[n]()})},t.fn.scrollspy.Constructor=e,t.fn.scrollspy.noConflict=function(){return t.fn.scrollspy=n,this},t(window).on("load",function(){t('[data-spy="scroll"]').each(function(){var e=t(this);e.scrollspy(e.data())})})}(jQuery),function(t,e){"use strict";var i,n,o="localStorage",a="page_"+t.location.pathname+t.location.search,r=function(){this.silence=!0;try{o in t&&t[o]&&t[o].setItem&&(this.enable=!0,i=t[o])}catch(r){}this.enable||(n={},i={getLength:function(){var t=0;return e.each(n,function(){t++}),t},key:function(t){var i,o=0;return e.each(n,function(e){return o===t?(i=e,!1):void o++}),i},removeItem:function(t){delete n[t]},getItem:function(t){return n[t]},setItem:function(t,e){n[t]=e},clear:function(){n={}}}),this.storage=i,this.page=this.get(a,{})};r.prototype.pageSave=function(){if(e.isEmptyObject(this.page))this.remove(a);else{var t,i=[];for(t in this.page){var n=this.page[t];null===n&&i.push(t)}for(t=i.length-1;t>=0;t--)delete this.page[i[t]];this.set(a,this.page)}},r.prototype.pageRemove=function(t){"undefined"!=typeof this.page[t]&&(this.page[t]=null,this.pageSave())},r.prototype.pageClear=function(){this.page={},this.pageSave()},r.prototype.pageGet=function(t,e){var i=this.page[t];return void 0===e||null!==i&&void 0!==i?i:e},r.prototype.pageSet=function(t,i){e.isPlainObject(t)?e.extend(!0,this.page,t):this.page[this.serialize(t)]=i,this.pageSave()},r.prototype.check=function(){if(!this.enable&&!this.silence)throw new Error("Browser not support localStorage or enable status been set true.");return this.enable},r.prototype.length=function(){return this.check()?i.getLength?i.getLength():i.length:0},r.prototype.removeItem=function(t){return i.removeItem(t),this},r.prototype.remove=function(t){return this.removeItem(t)},r.prototype.getItem=function(t){return i.getItem(t)},r.prototype.get=function(t,e){var i=this.deserialize(this.getItem(t));return"undefined"!=typeof i&&null!==i||"undefined"==typeof e?i:e},r.prototype.key=function(t){return i.key(t)},r.prototype.setItem=function(t,e){return i.setItem(t,e),this},r.prototype.set=function(t,e){return void 0===e?this.remove(t):(this.setItem(t,this.serialize(e)),this)},r.prototype.clear=function(){return i.clear(),this},r.prototype.forEach=function(t){for(var e=this.length(),n=e-1;n>=0;n--){var o=i.key(n);t(o,this.get(o))}return this},r.prototype.getAll=function(){var t={};return this.forEach(function(e,i){t[e]=i}),t},r.prototype.serialize=function(t){return"string"==typeof t?t:JSON.stringify(t)},r.prototype.deserialize=function(t){if("string"==typeof t)try{return JSON.parse(t)}catch(e){return t||void 0}},e.zui({store:new r})}(window,jQuery),function(t){"use strict";var e="zui.searchBox",i=function(e,n){var o=this;o.name=name,o.$=t(e),o.options=n=t.extend({},i.DEFAULTS,o.$.data(),n);var a=o.$.is(n.inputSelector)?o.$:o.$.find(n.inputSelector);if(a.length){var r=function(){o.changeTimer&&(clearTimeout(o.changeTimer),o.changeTimer=null)},s=function(){r();var t=o.getSearch();if(t!==o.lastValue){var e=""===t;a.toggleClass("empty",e),o.$.callComEvent(o,"onSearchChange",[t,e]),o.lastValue=t}};o.$input=a=a.first(),a.on(n.listenEvent,function(t){o.changeTimer=setTimeout(function(){s()},n.changeDelay)}).on("focus",function(t){a.addClass("focus"),o.$.callComEvent(o,"onFocus",[t])}).on("blur",function(t){a.removeClass("focus"),o.$.callComEvent(o,"onBlur",[t])}).on("keydown",function(t){var e=0,i=t.which;27===i&&n.escToClear?(this.setSearch("",!0),s(),e=1):13===i&&n.onPressEnter&&(s(),o.$.callComEvent(o,"onPressEnter",[t]));var a=o.$.callComEvent(o,"onKeyDown",[t]);a===!1&&(e=1),e&&t.preventDefault()}),o.$.on("click",".search-clear-btn",function(t){o.setSearch("",!0),s(),o.focus(),t.preventDefault()}),s()}else console.error("ZUI: search box init error, cannot find search box input element.")};i.DEFAULTS={inputSelector:'input[type="search"],input[type="text"]',listenEvent:"change input paste",changeDelay:500},i.prototype.getSearch=function(){return this.$input&&t.trim(this.$input.val())},i.prototype.setSearch=function(t,e){var i=this.$input;i&&(i.val(t),e||i.trigger("change"))},i.prototype.focus=function(){this.$input&&this.$input.focus()},t.fn.searchBox=function(n){return this.each(function(){var o=t(this),a=o.data(e),r="object"==typeof n&&n;a||o.data(e,a=new i(this,r)),"string"==typeof n&&a[n]()})},i.NAME=e,t.fn.searchBox.Constructor=i}(jQuery),function(t,e){"use strict";var i="zui.draggable",n={container:"body",move:!0},o=0,a=function(e,i){var a=this;a.$=t(e),a.id=o++,a.options=t.extend({},n,a.$.data(),i),a.init()};a.DEFAULTS=n,a.NAME=i,a.prototype.init=function(){var n,o,a,r,s,l=this,d=l.$,c="before",p="drag",u="finish",f="."+i+"."+l.id,h="mousedown"+f,g="mouseup"+f,m="mousemove"+f,v=l.options,y=v.selector,b=v.handle,w=d,C="function"==typeof v.move,x=function(t){var e=t.pageX,i=t.pageY;s=!0;var o={left:e-a.x,top:i-a.y};w.removeClass("drag-ready").addClass("dragging"),v.move&&(C?v.move(o,w):w.css(o)),v[p]&&v[p]({event:t,element:w,startOffset:a,pos:o,offset:{x:e-n.x,y:i-n.y},smallOffset:{x:e-r.x,y:i-r.y}}),r.x=e,r.y=i,v.stopPropagation&&t.stopPropagation()},$=function(i){if(t(e).off(f),!s)return void w.removeClass("drag-ready");var o={left:i.pageX-a.x,top:i.pageY-a.y};w.removeClass("drag-ready dragging"),v.move&&(C?v.move(o,w):w.css(o)),v[u]&&v[u]({event:i,element:w,startOffset:a,pos:o,offset:{x:i.pageX-n.x,y:i.pageY-n.y},smallOffset:{x:i.pageX-r.x,y:i.pageY-r.y}}),i.preventDefault(),v.stopPropagation&&i.stopPropagation()},T=function(i){var l=t.zui.getMouseButtonCode(v.mouseButton);if(!(l>-1&&i.button!==l)){var d=t(this);if(y&&(w=b?d.closest(y):d),v[c]){var p=v[c]({event:i,element:w});if(p===!1)return}var u=t(v.container),f=w.offset();o=u.offset(),n={x:i.pageX,y:i.pageY},a={x:i.pageX-f.left+o.left,y:i.pageY-f.top+o.top},r=t.extend({},n),s=!1,w.addClass("drag-ready"),i.preventDefault(),v.stopPropagation&&i.stopPropagation(),t(e).on(m,x).on(g,$)}};b?d.on(h,b,T):y?d.on(h,y,T):d.on(h,T)},a.prototype.destroy=function(){var n="."+i+"."+this.id;this.$.off(n),t(e).off(n),this.$.data(i,null)},t.fn.draggable=function(e){return this.each(function(){var n=t(this),o=n.data(i),r="object"==typeof e&&e;o||n.data(i,o=new a(this,r)),"string"==typeof e&&o[e]()})},t.fn.draggable.Constructor=a}(jQuery,document),function(t,e,i){"use strict";var n="zui.droppable",o={target:".droppable-target",deviation:5,sensorOffsetX:0,sensorOffsetY:0,dropToClass:"drop-to"},a=0,r=function(e,i){var n=this;n.id=a++,n.$=t(e),n.options=t.extend({},o,n.$.data(),i),n.init()};r.DEFAULTS=o,r.NAME=n,r.prototype.trigger=function(e,i){return t.zui.callEvent(this.options[e],i,this)},r.prototype.init=function(){var o,a,r,s,l,d,c,p,u,f,h,g,m,v=this,y=v.$,b=v.options,w=b.deviation,C="."+n+"."+v.id,x="mousedown"+C,$="mouseup"+C,T="mousemove"+C,D=b.selector,S=b.handle,k=b.flex,z=b.container,E=b.canMoveHere,P=b.dropToClass,I=y,M=!1,O=z?t(b.container).first():D?y:t("body"),L=function(e){if(M&&(h={left:e.pageX,top:e.pageY},!(i.abs(h.left-p.left)a&&h.top>r&&h.left-1&&i.button!==n)){var h=t(this);D&&(I=S?h.closest(D):h),I.hasClass("drag-shadow")||b.before&&b.before({event:i,element:I})===!1||(M=!0,o="function"==typeof b.target?b.target(I,y):O.find(b.target),a=null,r=null,s=!1,l=!0,d=null,c=I.offset(),u=O.offset(),u.top=u.top-O.scrollTop(),u.left=u.left-O.scrollLeft(),p={left:i.pageX,top:i.pageY},g=t.extend({},p),f={left:p.left-c.left,top:p.top-c.top},I.addClass("drag-from"),t(e).on(T,L).on($,j),m=setTimeout(function(){t(e).on(x,j)},10),i.preventDefault(),b.stopPropagation&&i.stopPropagation())}};S?y.on(x,S,A):D?y.on(x,D,A):y.on(x,A)},r.prototype.destroy=function(){var i="."+n+"."+this.id;this.$.off(i),t(e).off(i),this.$.data(n,null)},r.prototype.reset=function(){this.destroy(),this.init()},t.fn.droppable=function(e){return this.each(function(){var i=t(this),o=i.data(n),a="object"==typeof e&&e;o||i.data(n,o=new r(this,a)),"string"==typeof e&&o[e]()})},t.fn.droppable.Constructor=r}(jQuery,document,Math),+function(t,e){"use strict";function i(e,i,a){return this.each(function(){var r=t(this),s=r.data(n),l=t.extend({},o.DEFAULTS,r.data(),"object"==typeof e&&e);s||r.data(n,s=new o(this,l)),"string"==typeof e?s[e](i,a):l.show&&s.show(i,a)})}var n="zui.modal",o=function(i,o){var a=this;a.options=o,a.$body=t(document.body),a.$element=t(i),a.$backdrop=a.isShown=null,a.scrollbarWidth=0,o.moveable===e&&(a.options.moveable=a.$element.hasClass("modal-moveable")),o.remote&&a.$element.find(".modal-content").load(o.remote,function(){a.$element.trigger("loaded."+n)}),o.scrollInside&&t(window).on("resize."+n,function(){a.isShown&&a.adjustPosition(e,100)})};o.VERSION="3.2.0",o.TRANSITION_DURATION=300,o.BACKDROP_TRANSITION_DURATION=150,o.DEFAULTS={backdrop:!0,keyboard:!0,show:!0,position:"fit"};var a=function(e,i){var n=t(window);i.left=Math.max(0,Math.min(i.left,n.width()-e.outerWidth())),i.top=Math.max(0,Math.min(i.top,n.height()-e.outerHeight())),e.css(i)};o.prototype.toggle=function(t,e){return this.isShown?this.hide():this.show(t,e)},o.prototype.adjustPosition=function(i,o){var r=this;if(clearTimeout(r.reposTask),o)return void(r.reposTask=setTimeout(r.adjustPosition.bind(r,i,0),o));var s=r.options;if(i===e&&(i=s.position),i!==e&&null!==i){"function"==typeof i&&(i=i(r));var l=r.$element.find(".modal-dialog"),d=t(window).height(),c={maxHeight:"initial",overflow:"visible"},p=l.find(".modal-body").css(c);if(s.scrollInside&&p.length){var u=s.headerHeight,f=s.footerHeight,h=l.find(".modal-header"),g=l.find(".modal-footer");"number"!=typeof u&&(u=h.length?h.outerHeight():"function"==typeof u?u(h):0),"number"!=typeof f&&(f=g.length?g.outerHeight():"function"==typeof f?f(g):0),c.maxHeight=d-u-f,c.overflow=p[0].scrollHeight>c.maxHeight?"auto":"visible",p.css(c)}var m=Math.max(0,(d-l.outerHeight())/2);if("fit"===i?i={top:m>50?Math.floor(2*m/3):m}:"center"===i?i={top:m}:t.isPlainObject(i)||(i={top:i}),l.hasClass("modal-moveable")){var v=null,y=s.rememberPos;y&&(y===!0?v=r.$element.data("modal-pos"):t.zui.store&&(v=t.zui.store.pageGet(n+".rememberPos."+y))),i=t.extend(i,{left:Math.max(0,(t(window).width()-l.outerWidth())/2)},v),"inside"===s.moveable?a(l,i):l.css(i)}else l.css(i)}},o.prototype.setMoveable=function(){t.fn.draggable||console.error("Moveable modal requires draggable.js.");var e=this,i=e.options,o=e.$element.find(".modal-dialog").removeClass("modal-dragged");o.toggleClass("modal-moveable",!!i.moveable),e.$element.data("modal-moveable-setup")||o.draggable({container:e.$element,handle:".modal-header",before:function(){var t=o.css("margin-top");t&&"0px"!==t&&o.css("top",t).css("margin-top","").addClass("modal-dragged")},finish:function(o){var a=i.rememberPos;a&&(e.$element.data("modal-pos",o.pos),t.zui.store&&a!==!0&&t.zui.store.pageSet(n+".rememberPos."+a,o.pos))},move:"inside"!==i.moveable||function(t){a(o,t)}})},o.prototype.show=function(e,i){var a=this,r=t.Event("show."+n,{relatedTarget:e});a.$element.trigger(r),a.$element.toggleClass("modal-scroll-inside",!!a.options.scrollInside),a.isShown||r.isDefaultPrevented()||(a.isShown=!0,a.options.moveable&&a.setMoveable(),a.options.backdrop!==!1&&(a.setScrollbar(),a.$body.addClass("modal-open")),a.escape(),a.$element.on("click.dismiss."+n,'[data-dismiss="modal"]',function(t){a.hide(),t.stopPropagation()}),a.backdrop(function(){var r=t.support.transition&&a.$element.hasClass("fade");a.$element.parent().length||a.$element.appendTo(a.$body),a.$element.show().scrollTop(0),r&&a.$element[0].offsetWidth,a.$element.addClass("in").attr("aria-hidden",!1),a.adjustPosition(i),a.enforceFocus();var s=t.Event("shown."+n,{relatedTarget:e});r?a.$element.find(".modal-dialog").one("bsTransitionEnd",function(){a.$element.trigger("focus").trigger(s)}).emulateTransitionEnd(o.TRANSITION_DURATION):a.$element.trigger("focus").trigger(s)}))},o.prototype.hide=function(e){e&&e.preventDefault&&e.preventDefault();var i=this;e=t.Event("hide."+n),i.$element.trigger(e),i.isShown&&!e.isDefaultPrevented()&&(i.isShown=!1,i.options.backdrop!==!1&&(i.$body.removeClass("modal-open"),i.resetScrollbar()),i.escape(),t(document).off("focusin."+n),i.$element.removeClass("in").attr("aria-hidden",!0).off("click.dismiss."+n),t.support.transition&&i.$element.hasClass("fade")?i.$element.one("bsTransitionEnd",i.hideModal.bind(i)).emulateTransitionEnd(o.TRANSITION_DURATION):i.hideModal())},o.prototype.enforceFocus=function(){t(document).off("focusin."+n).on("focusin."+n,function(t){this.$element[0]===t.target||this.$element.has(t.target).length||this.$element.trigger("focus")}.bind(this))},o.prototype.escape=function(){this.isShown&&this.options.keyboard?t(document).on("keydown.dismiss."+n,function(i){if(27==i.which){var o=t.Event("escaping."+n),a=this.$element.triggerHandler(o,"esc");if(a!=e&&!a)return;this.hide()}}.bind(this)):this.isShown||t(document).off("keydown.dismiss."+n)},o.prototype.hideModal=function(){var t=this;this.$element.hide(),this.backdrop(function(){t.$element.trigger("hidden."+n)})},o.prototype.removeBackdrop=function(){this.$backdrop&&this.$backdrop.remove(),this.$backdrop=null},o.prototype.backdrop=function(e){var i=this,a=this.$element.hasClass("fade")?"fade":"";if(this.isShown&&this.options.backdrop){var r=t.support.transition&&a;if(this.$backdrop=t('
  • ');var n=t("").attr({href:e.url||"###","class":e.className,style:e.style}).data("item",e);return e.html?e.html===!0?n.html(e.label||e.text):n=t(e.html):n.text(e.label||e.text),e.onClick&&n.on("click",e.onClick),t("
  • ").toggleClass("disabled",e.disabled===!0).append(n)},p=function(e){var i=t("#"+r);return i.length&&i.hasClass("contextmenu-show")&&(!e||(i.data("options")||{}).id===e)},u=null,f=function(e,i){"function"==typeof e&&(i=e,e=null),u&&(clearTimeout(u),u=null);var n=t("#"+r);if(n.length){var o=n.removeClass("contextmenu-show").data("options");if(!e||o.id===e){var s=function(){n.find(".contextmenu-menu").removeClass("open"),o.onHidden&&o.onHidden(),i&&i()};o.onHide&&o.onHide();var l=o.animation;n.find(".contextmenu-menu").removeClass("in"),l?u=setTimeout(s,o.duration):s()}}return a},h=function(d,p,h){t.isPlainObject(d)&&(h=p,p=d,d=p.items),o=!0,p=t.extend({},n,p);var g=t("#"+r);g.length||(g=t('
    ').appendTo("body"));var m=g.find(".contextmenu-menu").off("click."+i).on("click."+i,"a,.contextmenu-item",function(e){var i=t(this),n=p.onClickItem&&p.onClickItem(i.data("item"),i,e);n!==!1&&f()}).empty();m.attr("class","contextmenu-menu"+(p.className?" "+p.className:"")),g.attr("class","contextmenu contextmenu-show");var v=p.menuCreator;if(v)m.append(v(d,p));else{m.append(p.menuTemplate);var y=m.children().first(),b=p.itemCreator||c,w=typeof d;"string"===w?d=d.split(","):"function"===w&&(d=d(p)),t.each(d,function(t,e){y.append(b(e,t,p))})}var C=p.animation,x=p.duration;C===!0&&(p.animation=C="fade"),u&&(clearTimeout(u),u=null);var $=function(){m.addClass("in"),p.onShown&&p.onShown(),h&&h()};p.onShow&&p.onShow(),g.data("options",{animation:C,onHide:p.onHide,onHidden:p.onHidden,id:p.id,duration:x});var T=p.x,D=p.y;T===e&&(T=(p.event||p).clientX),T===e&&(T=s),D===e&&(D=(p.event||p).clientY),D===e&&(D=l);var y=m.children().first(),S=y.outerWidth(),k=y.outerHeight();if(p.position){var z=p.position({x:T,y:D,width:S,height:k},p,m);z&&(T=z.x,D=z.y)}if(p.limitInsideWindow){var E=t(window);T=Math.max(0,Math.min(T,E.width()-S)),D=Math.max(0,Math.min(D,E.height()-k))}return g.css({left:T,top:D}).show(),m.addClass("open"),C?(m.addClass(C),u=setTimeout(function(){$(),o=!1},10)):($(),o=!1),a};t.extend(a,{NAME:i,DEFAULTS:n,show:h,hide:f,listenMouse:d,isShow:p}),t.zui({ContextMenu:a});var g=function(e,n){var o=this;o.name=i,o.$=t(e),o.id=t.zui.uuid(),n=o.options=t.extend({trigger:"contextmenu"},a.DEFAULTS,this.$.data(),n);var r=function(t){if("mousedown"!==t.type||2===t.button){if(n.toggleTrigger&&o.isShow())o.hide();else{var e={x:t.clientX,y:t.clientY,event:t};o.show(e)}return t.preventDefault(),t.returnValue=!1,!1}},s=n.trigger,l=s+"."+i;n.selector?o.$.on(l,n.selector,r):o.$.on(l,r),n.show&&o.show("object"==typeof n.show?n.show:null)};g.prototype.destory=function(){that.$.off("."+i)},g.prototype.hide=function(t){a.hide(this.id,t)},g.prototype.show=function(e,i){e=t.extend({id:this.id,$toggle:this.$},this.options,e),a.show(e,i)},g.prototype.isShow=function(){return p(this.id)},t.fn.contextmenu=function(e){return this.each(function(){var n=t(this),o=n.data(i),a="object"==typeof e&&e;o||n.data(i,o=new g(this,a)),"string"==typeof e&&o[e]()})},t.fn.contextmenu.Constructor=g,t.fn.contextDropdown=function(e){t(this).contextmenu(t.extend({trigger:"click",animation:"fade",toggleTrigger:!0,menuCreator:function(e,i){var n=i.$toggle,o=n.attr("data-target");o||(o=n.attr("href"),o=o&&/#/.test(o)&&o.replace(/.*(?=#[^\s]*$)/,""));var a=o?t(o):n.next(".dropdown-menu"),r=i.transferEvent;if(r!==!1){var s="data-contextmenu-index";a.find("a,.contextmenu-item").each(function(e){t(this).attr(s,e)});var l=a.clone();return l.on("string"==typeof r?r:"click","a,.contextmenu-item",function(e){var i=a.find("["+s+'="'+t(this).attr(s)+'"]'),n=i[0];if(n)return n[e.type]?n[e.type]():i.trigger(e.type),e.preventDefault(),e.stopPropagation(),!1}),l}return a.clone()},position:function(t,e,i){var n=e.placement,o=e.$toggle;if(!n){var a=i.find(".dropdown-menu"),r=a.hasClass("pull-right"),s=o.parent().hasClass("dropup");n=r?s?"top-right":"bottom-right":s?"top-left":"bottom-left",r&&a.removeClass("pull-right")}var l=o[0].getBoundingClientRect();switch(n){case"top-left":return{x:l.left,y:Math.floor(l.top-t.height)};case"top-right":return{x:Math.floor(l.right-t.width),y:Math.floor(l.top-t.height)};case"bottom-left":return{x:l.left,y:l.bottom};case"bottom-right":return{x:Math.floor(l.right-t.width),y:l.bottom}}return t}},e))},t(document).on("click",function(e){var n=t(e.target),a=n.closest('[data-toggle="context-dropdown"]');if(a.length){var r=a.data(i);r||a.contextDropdown({show:!0})}else o||n.closest(".contextmenu").length||f()})}(jQuery,void 0),+function(t){"use strict";var e=function(e,i){this.$element=t(e),this.$indicators=this.$element.find(".carousel-indicators"),this.options=i,this.paused=this.sliding=this.interval=this.$active=this.$items=null,"hover"==this.options.pause&&this.$element.on("mouseenter",this.pause.bind(this)).on("mouseleave",this.cycle.bind(this))};e.DEFAULTS={interval:5e3,pause:"hover",wrap:!0,touchable:!0},e.prototype.touchable=function(){function e(e){var e=e||window.event;e.originalEvent&&(e=e.originalEvent);var a=t(this);switch(e.type){case"touchstart":n=e.touches[0].pageX,o=e.touches[0].pageY;break;case"touchend":var r=e.changedTouches[0].pageX-n,s=e.changedTouches[0].pageY-o;if(Math.abs(r)>Math.abs(s))i(a,r),Math.abs(r)>10&&e.preventDefault();else{var l=t(window);t("body,html").animate({scrollTop:l.scrollTop()-s},400)}}}function i(t,e){e>10?a.prev():e<-10&&a.next()}if(this.options.touchable){this.$element.on("touchstart touchmove touchend",e);var n,o,a=this}},e.prototype.cycle=function(t){return t||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(this.next.bind(this),this.options.interval)),this},e.prototype.getActiveIndex=function(){return this.$active=this.$element.find(".item.active"),this.$items=this.$active.parent().children(),this.$items.index(this.$active)},e.prototype.to=function(e){var i=this,n=this.getActiveIndex();if(!(e>this.$items.length-1||e<0))return this.sliding?this.$element.one("slid",function(){i.to(e)}):n==e?this.pause().cycle():this.slide(e>n?"next":"prev",t(this.$items[e]))},e.prototype.pause=function(e){return e||(this.paused=!0),this.$element.find(".next, .prev").length&&t.support.transition.end&&(this.$element.trigger(t.support.transition.end),this.cycle(!0)),this.interval=clearInterval(this.interval),this},e.prototype.next=function(){if(!this.sliding)return this.slide("next")},e.prototype.prev=function(){if(!this.sliding)return this.slide("prev")},e.prototype.slide=function(e,i){var n=this.$element.find(".item.active"),o=i||n[e](),a=this.interval,r="next"==e?"left":"right",s="next"==e?"first":"last",l=this;if(!o.length){if(!this.options.wrap)return;o=this.$element.find(".item")[s]()}this.sliding=!0,a&&this.pause();var d=t.Event("slide.zui.carousel",{relatedTarget:o[0],direction:r});if(!o.hasClass("active")){if(this.$indicators.length&&(this.$indicators.find(".active").removeClass("active"),this.$element.one("slid",function(){var e=t(l.$indicators.children()[l.getActiveIndex()]);e&&e.addClass("active")})),t.support.transition&&this.$element.hasClass("slide")){if(this.$element.trigger(d),d.isDefaultPrevented())return;o.addClass(e),o[0].offsetWidth,n.addClass(r),o.addClass(r),n.one(t.support.transition.end,function(){o.removeClass([e,r].join(" ")).addClass("active"),n.removeClass(["active",r].join(" ")),l.sliding=!1,setTimeout(function(){l.$element.trigger("slid")},0)}).emulateTransitionEnd(600)}else{if(this.$element.trigger(d),d.isDefaultPrevented())return;n.removeClass("active"),o.addClass("active"),this.sliding=!1,this.$element.trigger("slid")}return a&&this.cycle(),this}};var i=t.fn.carousel;t.fn.carousel=function(i){return this.each(function(){var n=t(this),o=n.data("zui.carousel"),a=t.extend({},e.DEFAULTS,n.data(),"object"==typeof i&&i),r="string"==typeof i?i:a.slide;o||n.data("zui.carousel",o=new e(this,a)),"number"==typeof i?o.to(i):r?o[r]():a.interval&&o.pause().cycle(),a.touchable&&o.touchable()})},t.fn.carousel.Constructor=e,t.fn.carousel.noConflict=function(){return t.fn.carousel=i,this},t(document).on("click.zui.carousel.data-api","[data-slide], [data-slide-to]",function(e){var i,n=t(this),o=t(n.attr("data-target")||(i=n.attr("href"))&&i.replace(/.*(?=#[^\s]+$)/,"")),a=t.extend({},o.data(),n.data()),r=n.attr("data-slide-to");r&&(a.interval=!1),o.carousel(a),(r=n.attr("data-slide-to"))&&o.data("zui.carousel").to(r),e.preventDefault()}),t(window).on("load",function(){t('[data-ride="carousel"]').each(function(){var e=t(this);e.carousel(e.data())})})}(window.jQuery),/*! TangBin: image.ready.js http://www.planeart.cn/?p=1121 */ +var c=0;t.left<0&&(c=t.left*-2,t.left=0,n.offset(t),l=n[0].offsetWidth,d=n[0].offsetHeight),this.replaceArrow(c-o+l,l,"left")}else this.replaceArrow(d-a,d,"top");i&&n.offset(t)},e.prototype.replaceArrow=function(t,e,i){this.arrow().css(i,t?50*(1-t/e)+"%":"")},e.prototype.setContent=function(t){var e=this.tip(),i=t||this.getTitle();this.options.tipId&&e.attr("id",this.options.tipId),this.options.tipClass&&e.addClass(this.options.tipClass),e.find(".tooltip-inner")[this.options.html?"html":"text"](i),e.removeClass("fade in top bottom left right")},e.prototype.hide=function(){function e(){"in"!=i.hoverState&&n.detach()}var i=this,n=this.tip(),o=t.Event("hide.zui."+this.type);if(this.$element.trigger(o),!o.isDefaultPrevented())return n.removeClass("in"),t.support.transition&&this.$tip.hasClass("fade")?n.one(t.support.transition.end,e).emulateTransitionEnd(150):e(),this.$element.trigger("hidden.zui."+this.type),this},e.prototype.fixTitle=function(){var t=this.$element;(t.attr("title")||"string"!=typeof t.attr("data-original-title"))&&t.attr("data-original-title",t.attr("title")||"").attr("title","")},e.prototype.hasContent=function(){return this.getTitle()},e.prototype.getPosition=function(){var e=this.$element[0];return t.extend({},"function"==typeof e.getBoundingClientRect?e.getBoundingClientRect():{width:e.offsetWidth,height:e.offsetHeight},this.$element.offset())},e.prototype.getCalculatedOffset=function(t,e,i,n){return"bottom"==t?{top:e.top+e.height,left:e.left+e.width/2-i/2}:"top"==t?{top:e.top-n,left:e.left+e.width/2-i/2}:"left"==t?{top:e.top+e.height/2-n/2,left:e.left-i}:{top:e.top+e.height/2-n/2,left:e.left+e.width}},e.prototype.getTitle=function(){var t,e=this.$element,i=this.options;return t=e.attr("data-original-title")||("function"==typeof i.title?i.title.call(e[0]):i.title)},e.prototype.tip=function(){return this.$tip=this.$tip||t(this.options.template)},e.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".tooltip-arrow")},e.prototype.validate=function(){this.$element[0].parentNode||(this.hide(),this.$element=null,this.options=null)},e.prototype.enable=function(){this.enabled=!0},e.prototype.disable=function(){this.enabled=!1},e.prototype.toggleEnabled=function(){this.enabled=!this.enabled},e.prototype.toggle=function(e){var i=e?t(e.currentTarget)[this.type](this.getDelegateOptions()).data("zui."+this.type):this;i.tip().hasClass("in")?i.leave(i):i.enter(i)},e.prototype.destroy=function(){this.hide().$element.off("."+this.type).removeData("zui."+this.type)};var i=t.fn.tooltip;t.fn.tooltip=function(i,n){return this.each(function(){var o=t(this),a=o.data("zui.tooltip"),r="object"==typeof i&&i;a||o.data("zui.tooltip",a=new e(this,r)),"string"==typeof i&&a[i](n)})},t.fn.tooltip.Constructor=e,t.fn.tooltip.noConflict=function(){return t.fn.tooltip=i,this}}(window.jQuery),+function(t){"use strict";var e=function(t,e){this.init("popover",t,e)};if(!t.fn.tooltip)throw new Error("Popover requires tooltip.js");e.DEFAULTS=t.extend({},t.fn.tooltip.Constructor.DEFAULTS,{placement:"right",trigger:"click",content:"",template:'

    '}),e.prototype=t.extend({},t.fn.tooltip.Constructor.prototype),e.prototype.constructor=e,e.prototype.getDefaults=function(){return e.DEFAULTS},e.prototype.setContent=function(){var t=this.tip(),e=this.getTarget();if(e)return e.find(".arrow").length<1&&t.addClass("no-arrow"),void t.html(e.html());var i=this.getTitle(),n=this.getContent();t.find(".popover-title")[this.options.html?"html":"text"](i),t.find(".popover-content")[this.options.html?"html":"text"](n),t.removeClass("fade top bottom left right in"),this.options.tipId&&t.attr("id",this.options.tipId),this.options.tipClass&&t.addClass(this.options.tipClass),t.find(".popover-title").html()||t.find(".popover-title").hide()},e.prototype.hasContent=function(){return this.getTarget()||this.getTitle()||this.getContent()},e.prototype.getContent=function(){var t=this.$element,e=this.options;return t.attr("data-content")||("function"==typeof e.content?e.content.call(t[0]):e.content)},e.prototype.getTarget=function(){var e=this.$element,i=this.options,n=e.attr("data-target")||("function"==typeof i.target?i.target.call(e[0]):i.target);return!!n&&("$next"==n?e.next(".popover"):t(n))},e.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".arrow")},e.prototype.tip=function(){return this.$tip||(this.$tip=t(this.options.template)),this.$tip};var i=t.fn.popover;t.fn.popover=function(i){return this.each(function(){var n=t(this),o=n.data("zui.popover"),a="object"==typeof i&&i;o||n.data("zui.popover",o=new e(this,a)),"string"==typeof i&&o[i]()})},t.fn.popover.Constructor=e,t.fn.popover.noConflict=function(){return t.fn.popover=i,this}}(window.jQuery),+function(t){"use strict";function e(e){t(o).remove(),t(a).each(function(e){var o=i(t(this));o.hasClass("open")&&(o.trigger(e=t.Event("hide."+n)),e.isDefaultPrevented()||o.removeClass("open").trigger("hidden."+n))})}function i(e){var i=e.attr("data-target");i||(i=e.attr("href"),i=i&&/#/.test(i)&&i.replace(/.*(?=#[^\s]*$)/,""));var n;try{n=i&&t(i)}catch(o){}return n&&n.length?n:e.parent()}var n="zui.dropdown",o=".dropdown-backdrop",a="[data-toggle=dropdown]",r=function(e){t(e).on("click."+n,this.toggle)};r.prototype.toggle=function(o){var a=t(this);if(!a.is(".disabled, :disabled")){var r=i(a),s=r.hasClass("open");if(e(),!s){if("ontouchstart"in document.documentElement&&!r.closest(".navbar-nav").length&&t('
  • ');var n=t("
    ").attr(t.extend({href:e.url||"###","class":e.className,style:e.style},e.attrs)).data("item",e);return e.html?e.html===!0?n.html(e.label||e.text):n=t(e.html):n.text(e.label||e.text),e.icon&&n.prepend(''),e.onClick&&n.on("click",e.onClick),t("
  • ").toggleClass("disabled",e.disabled===!0).append(n)},p=function(e){var i=t("#"+r);return i.length&&i.hasClass("contextmenu-show")&&(!e||(i.data("options")||{}).id===e)},u=null,f=function(e,i){"function"==typeof e&&(i=e,e=null),u&&(clearTimeout(u),u=null);var n=t("#"+r);if(n.length){var o=n.removeClass("contextmenu-show").data("options");if(!e||o.id===e){var s=function(){n.find(".contextmenu-menu").removeClass("open"),o.onHidden&&o.onHidden(),i&&i()};o.onHide&&o.onHide();var l=o.animation;n.find(".contextmenu-menu").removeClass("in"),l?u=setTimeout(s,o.duration):s()}}return a},h=function(d,p,h){t.isPlainObject(d)&&(h=p,p=d,d=p.items),o=!0,p=t.extend({},n,p);var g=t("#"+r);g.length||(g=t('
    ').appendTo("body"));var m=g.find(".contextmenu-menu").off("click."+i).on("click."+i,"a,.contextmenu-item",function(e){var i=t(this),n=p.onClickItem&&p.onClickItem(i.data("item"),i,e,p);n!==!1&&f()}).empty();m.attr("class","contextmenu-menu"+(p.className?" "+p.className:"")),g.attr("class","contextmenu contextmenu-show");var v=p.menuCreator;if(v)m.append(v(d,p));else{m.append(p.menuTemplate);var y=m.children().first(),b=p.itemCreator||c,w=typeof d;if("string"===w?d=d.split(","):"function"===w&&(d=d(p)),!d)return!1;t.each(d,function(t,e){y.append(b(e,t,p))})}var C=p.animation,x=p.duration;C===!0&&(p.animation=C="fade"),u&&(clearTimeout(u),u=null);var $=function(){m.addClass("in"),p.onShown&&p.onShown(),h&&h()};p.onShow&&p.onShow(),g.data("options",{animation:C,onHide:p.onHide,onHidden:p.onHidden,id:p.id,duration:x});var T=p.x,D=p.y;T===e&&(T=(p.event||p).clientX),T===e&&(T=s),D===e&&(D=(p.event||p).clientY),D===e&&(D=l);var y=m.children().first(),S=y.outerWidth(),k=y.outerHeight();if(p.position){var z=p.position({x:T,y:D,width:S,height:k},p,m);z&&(T=z.x,D=z.y)}if(p.limitInsideWindow){var E=t(window);T=Math.max(0,Math.min(T,E.width()-S)),D=Math.max(0,Math.min(D,E.height()-k))}return g.css({left:T,top:D}).show(),m.addClass("open"),C?(m.addClass(C),u=setTimeout(function(){$(),o=!1},10)):($(),o=!1),a};t.extend(a,{NAME:i,DEFAULTS:n,show:h,hide:f,listenMouse:d,isShow:p}),t.zui({ContextMenu:a});var g=function(e,n){var o=this;o.name=i,o.$=t(e),o.id=t.zui.uuid(),n=o.options=t.extend({trigger:"contextmenu"},a.DEFAULTS,this.$.data(),n);var r=function(t){if("mousedown"!==t.type||2===t.button){if(n.toggleTrigger&&o.isShow())o.hide();else{var e={x:t.clientX,y:t.clientY,event:t};if(o.show(e)===!1)return}return t.preventDefault(),t.returnValue=!1,!1}},s=n.trigger,l=s+"."+i;n.selector?o.$.on(l,n.selector,r):o.$.on(l,r),n.show&&o.show("object"==typeof n.show?n.show:null)};g.prototype.destory=function(){that.$.off("."+i)},g.prototype.hide=function(t){return a.hide(this.id,t)},g.prototype.show=function(e,i){return e=t.extend({id:this.id,$toggle:this.$},this.options,e),a.show(e,i)},g.prototype.isShow=function(){return p(this.id)},t.fn.contextmenu=function(e){return this.each(function(){var n=t(this),o=n.data(i),a="object"==typeof e&&e;o||n.data(i,o=new g(this,a)),"string"==typeof e&&o[e]()})},t.fn.contextmenu.Constructor=g,t.fn.contextDropdown=function(e){t(this).contextmenu(t.extend({trigger:"click",animation:"fade",toggleTrigger:!0,menuCreator:function(e,i){var n=i.$toggle,o=n.attr("data-target");o||(o=n.attr("href"),o=o&&/#/.test(o)&&o.replace(/.*(?=#[^\s]*$)/,""));var a=o?t(o):n.next(".dropdown-menu"),r=i.transferEvent;if(r!==!1){var s="data-contextmenu-index";a.find("a,.contextmenu-item").each(function(e){t(this).attr(s,e)});var l=a.clone();return l.on("string"==typeof r?r:"click","a,.contextmenu-item",function(e){var i=a.find("["+s+'="'+t(this).attr(s)+'"]'),n=i[0];if(n)return n[e.type]?n[e.type]():i.trigger(e.type),e.preventDefault(),e.stopPropagation(),!1}),l}return a.clone()},position:function(t,e,i){var n=e.placement,o=e.$toggle;if(!n){var a=i.find(".dropdown-menu"),r=a.hasClass("pull-right"),s=o.parent().hasClass("dropup");n=r?s?"top-right":"bottom-right":s?"top-left":"bottom-left",r&&a.removeClass("pull-right")}var l=o[0].getBoundingClientRect();switch(n){case"top-left":return{x:l.left,y:Math.floor(l.top-t.height)};case"top-right":return{x:Math.floor(l.right-t.width),y:Math.floor(l.top-t.height)};case"bottom-left":return{x:l.left,y:l.bottom};case"bottom-right":return{x:Math.floor(l.right-t.width),y:l.bottom}}return t}},e))},t(document).on("click",function(e){var n=t(e.target),a=n.closest('[data-toggle="context-dropdown"]');if(a.length){var r=a.data(i);r||a.contextDropdown({show:!0})}else o||n.closest(".contextmenu").length||f()})}(jQuery,void 0),+function(t){"use strict";var e=function(e,i){this.$element=t(e),this.$indicators=this.$element.find(".carousel-indicators"),this.options=i,this.paused=this.sliding=this.interval=this.$active=this.$items=null,"hover"==this.options.pause&&this.$element.on("mouseenter",this.pause.bind(this)).on("mouseleave",this.cycle.bind(this))};e.DEFAULTS={interval:5e3,pause:"hover",wrap:!0,touchable:!0},e.prototype.touchable=function(){function e(e){var e=e||window.event;e.originalEvent&&(e=e.originalEvent);var a=t(this);switch(e.type){case"touchstart":n=e.touches[0].pageX,o=e.touches[0].pageY;break;case"touchend":var r=e.changedTouches[0].pageX-n,s=e.changedTouches[0].pageY-o;if(Math.abs(r)>Math.abs(s))i(a,r),Math.abs(r)>10&&e.preventDefault();else{var l=t(window);t("body,html").animate({scrollTop:l.scrollTop()-s},400)}}}function i(t,e){e>10?a.prev():e<-10&&a.next()}if(this.options.touchable){this.$element.on("touchstart touchmove touchend",e);var n,o,a=this}},e.prototype.cycle=function(t){return t||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(this.next.bind(this),this.options.interval)),this},e.prototype.getActiveIndex=function(){return this.$active=this.$element.find(".item.active"),this.$items=this.$active.parent().children(),this.$items.index(this.$active)},e.prototype.to=function(e){var i=this,n=this.getActiveIndex();if(!(e>this.$items.length-1||e<0))return this.sliding?this.$element.one("slid",function(){i.to(e)}):n==e?this.pause().cycle():this.slide(e>n?"next":"prev",t(this.$items[e]))},e.prototype.pause=function(e){return e||(this.paused=!0),this.$element.find(".next, .prev").length&&t.support.transition.end&&(this.$element.trigger(t.support.transition.end),this.cycle(!0)),this.interval=clearInterval(this.interval),this},e.prototype.next=function(){if(!this.sliding)return this.slide("next")},e.prototype.prev=function(){if(!this.sliding)return this.slide("prev")},e.prototype.slide=function(e,i){var n=this.$element.find(".item.active"),o=i||n[e](),a=this.interval,r="next"==e?"left":"right",s="next"==e?"first":"last",l=this;if(!o.length){if(!this.options.wrap)return;o=this.$element.find(".item")[s]()}this.sliding=!0,a&&this.pause();var d=t.Event("slide.zui.carousel",{relatedTarget:o[0],direction:r});if(!o.hasClass("active")){if(this.$indicators.length&&(this.$indicators.find(".active").removeClass("active"),this.$element.one("slid",function(){var e=t(l.$indicators.children()[l.getActiveIndex()]);e&&e.addClass("active")})),t.support.transition&&this.$element.hasClass("slide")){if(this.$element.trigger(d),d.isDefaultPrevented())return;o.addClass(e),o[0].offsetWidth,n.addClass(r),o.addClass(r),n.one(t.support.transition.end,function(){o.removeClass([e,r].join(" ")).addClass("active"),n.removeClass(["active",r].join(" ")),l.sliding=!1,setTimeout(function(){l.$element.trigger("slid")},0)}).emulateTransitionEnd(600)}else{if(this.$element.trigger(d),d.isDefaultPrevented())return;n.removeClass("active"),o.addClass("active"),this.sliding=!1,this.$element.trigger("slid")}return a&&this.cycle(),this}};var i=t.fn.carousel;t.fn.carousel=function(i){return this.each(function(){var n=t(this),o=n.data("zui.carousel"),a=t.extend({},e.DEFAULTS,n.data(),"object"==typeof i&&i),r="string"==typeof i?i:a.slide;o||n.data("zui.carousel",o=new e(this,a)),"number"==typeof i?o.to(i):r?o[r]():a.interval&&o.pause().cycle(),a.touchable&&o.touchable()})},t.fn.carousel.Constructor=e,t.fn.carousel.noConflict=function(){return t.fn.carousel=i,this},t(document).on("click.zui.carousel.data-api","[data-slide], [data-slide-to]",function(e){var i,n=t(this),o=t(n.attr("data-target")||(i=n.attr("href"))&&i.replace(/.*(?=#[^\s]+$)/,"")),a=t.extend({},o.data(),n.data()),r=n.attr("data-slide-to");r&&(a.interval=!1),o.carousel(a),(r=n.attr("data-slide-to"))&&o.data("zui.carousel").to(r),e.preventDefault()}),t(window).on("load",function(){t('[data-ride="carousel"]').each(function(){var e=t(this);e.carousel(e.data())})})}(window.jQuery),/*! TangBin: image.ready.js http://www.planeart.cn/?p=1121 */ function(t){"use strict";t.zui.imgReady=function(){var t=[],e=null,i=function(){for(var e=0;e1024)&&(o.call(u),s.end=!0)},s(),u.onload=function(){!s.end&&s(),a&&a.call(u),u=u.onload=u.onerror=null},void(s.end||(t.push(s),null===e&&(e=setInterval(i,40)))))}}()}(jQuery),function(t,e,i){"use strict";if(!t.fn.modalTrigger)throw new Error("modal & modalTrigger requires for lightbox");if(!t.zui.imgReady)throw new Error("imgReady requires for lightbox");var n=function(e,i){this.$=t(e),this.options=this.getOptions(i),this.init()};n.DEFAULTS={modalTeamplate:'